mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
cmd/compile: fix identity case relating to 'any' and shape types
In identical(), we don't want any to match a shape empty-interface type for the identStrict option, since IdenticalStrict() is specifically not supposed to match a shape type with a non-shape type. There is similar code in (*Type).cmp() (TINTER case), but I don't believe that we want to disqualify shape types from matching any in this case, since cmp() is used for back-end code, where we don't care about shape types vs non-shape types. The issue mainly comes about when 'any' is used as a type argument (rather than 'interface{}'), but only with some complicated circumstances, as shown by the test case. (Couldn't reproduce with simpler test cases.) Fixes #50109 Change-Id: I3f2f88be158f9ad09273237e1d346bc56aac099f Reviewed-on: https://go-review.googlesource.com/c/go/+/371154 Trust: Dan Scales <danscales@google.com> Run-TryBot: Dan Scales <danscales@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
49b7c9caec
commit
9bfe09d78b
@ -59,7 +59,11 @@ func identical(t1, t2 *Type, flags int, assumedEqual map[typePair]struct{}) bool
|
||||
case TINT32:
|
||||
return (t1 == Types[TINT32] || t1 == RuneType) && (t2 == Types[TINT32] || t2 == RuneType)
|
||||
case TINTER:
|
||||
// Make sure named any type matches any empty interface.
|
||||
// Make sure named any type matches any empty interface
|
||||
// (but not a shape type, if identStrict).
|
||||
if flags&identStrict != 0 {
|
||||
return t1 == AnyType && t2.IsEmptyInterface() && !t2.HasShape() || t2 == AnyType && t1.IsEmptyInterface() && !t1.HasShape()
|
||||
}
|
||||
return t1 == AnyType && t2.IsEmptyInterface() || t2 == AnyType && t1.IsEmptyInterface()
|
||||
default:
|
||||
return false
|
||||
|
105
test/typeparam/issue50109.go
Normal file
105
test/typeparam/issue50109.go
Normal file
@ -0,0 +1,105 @@
|
||||
// run -gcflags=-G=3
|
||||
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type AnyCacher[T any] interface {
|
||||
// Get an item from the cache. Returns the item or nil, and a bool indicating
|
||||
// whether the key was found.
|
||||
Get(k string) (T, bool)
|
||||
// Add an item to the cache, replacing any existing item.
|
||||
Set(k string, x T)
|
||||
}
|
||||
|
||||
// Item ...
|
||||
type Item[T any] struct {
|
||||
Object T
|
||||
}
|
||||
|
||||
// AnyCache implements AnyCacher
|
||||
type AnyCache[T any] struct {
|
||||
*anyCache[T]
|
||||
}
|
||||
|
||||
type anyCache[T any] struct {
|
||||
items map[string]Item[T]
|
||||
janitor *janitor[T] // Needed for the failure in the issue
|
||||
}
|
||||
|
||||
// Set adds an item to the cache, replacing any existing item.
|
||||
func (c *anyCache[T]) Set(k string, x T) {
|
||||
c.items[k] = Item[T]{
|
||||
Object: x,
|
||||
}
|
||||
}
|
||||
|
||||
// Get gets an item from the cache. Returns the item or nil, and a bool indicating
|
||||
// whether the key was found.
|
||||
func (c *anyCache[T]) Get(k string) (T, bool) {
|
||||
// "Inlining" of get and Expired
|
||||
item, found := c.items[k]
|
||||
if !found {
|
||||
var ret T
|
||||
return ret, false
|
||||
}
|
||||
|
||||
return item.Object, true
|
||||
}
|
||||
|
||||
type janitor[T any] struct {
|
||||
stop chan bool
|
||||
}
|
||||
|
||||
func newAnyCache[T any](m map[string]Item[T]) *anyCache[T] {
|
||||
c := &anyCache[T]{
|
||||
items: m,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// NewAny[T any](...) returns a new AnyCache[T].
|
||||
func NewAny[T any]() *AnyCache[T] {
|
||||
items := make(map[string]Item[T])
|
||||
return &AnyCache[T]{newAnyCache(items)}
|
||||
}
|
||||
|
||||
// NewAnyCacher[T any](...) returns an AnyCacher[T] interface.
|
||||
func NewAnyCacher[T any]() AnyCacher[T] {
|
||||
return NewAny[T]()
|
||||
}
|
||||
|
||||
type MyStruct struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create a generic cache.
|
||||
// All items are cached as interface{} so they need to be cast back to their
|
||||
// original type when retrieved.
|
||||
// Failure in issue doesn't happen with 'any' replaced by 'interface{}'
|
||||
c := NewAnyCacher[any]()
|
||||
|
||||
myStruct := &MyStruct{"MySuperStruct"}
|
||||
|
||||
c.Set("MySuperStruct", myStruct)
|
||||
|
||||
myRawCachedStruct, found := c.Get("MySuperStruct")
|
||||
|
||||
if found {
|
||||
// Casting the retrieved object back to its original type
|
||||
myCachedStruct := myRawCachedStruct.(*MyStruct)
|
||||
fmt.Printf("%s", myCachedStruct.Name)
|
||||
} else {
|
||||
fmt.Printf("Error: MySuperStruct not found in cache")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// MySuperStruct
|
||||
}
|
1
test/typeparam/issue50109.out
Normal file
1
test/typeparam/issue50109.out
Normal file
@ -0,0 +1 @@
|
||||
MySuperStruct
|
Loading…
x
Reference in New Issue
Block a user