Skip to content

Commit 9bfe09d

Browse files
committed
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 <[email protected]> Run-TryBot: Dan Scales <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 49b7c9c commit 9bfe09d

File tree

3 files changed

+111
-1
lines changed

3 files changed

+111
-1
lines changed

src/cmd/compile/internal/types/identity.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ func identical(t1, t2 *Type, flags int, assumedEqual map[typePair]struct{}) bool
5959
case TINT32:
6060
return (t1 == Types[TINT32] || t1 == RuneType) && (t2 == Types[TINT32] || t2 == RuneType)
6161
case TINTER:
62-
// Make sure named any type matches any empty interface.
62+
// Make sure named any type matches any empty interface
63+
// (but not a shape type, if identStrict).
64+
if flags&identStrict != 0 {
65+
return t1 == AnyType && t2.IsEmptyInterface() && !t2.HasShape() || t2 == AnyType && t1.IsEmptyInterface() && !t1.HasShape()
66+
}
6367
return t1 == AnyType && t2.IsEmptyInterface() || t2 == AnyType && t1.IsEmptyInterface()
6468
default:
6569
return false

test/typeparam/issue50109.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// run -gcflags=-G=3
2+
3+
// Copyright 2021 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import (
10+
"fmt"
11+
)
12+
13+
type AnyCacher[T any] interface {
14+
// Get an item from the cache. Returns the item or nil, and a bool indicating
15+
// whether the key was found.
16+
Get(k string) (T, bool)
17+
// Add an item to the cache, replacing any existing item.
18+
Set(k string, x T)
19+
}
20+
21+
// Item ...
22+
type Item[T any] struct {
23+
Object T
24+
}
25+
26+
// AnyCache implements AnyCacher
27+
type AnyCache[T any] struct {
28+
*anyCache[T]
29+
}
30+
31+
type anyCache[T any] struct {
32+
items map[string]Item[T]
33+
janitor *janitor[T] // Needed for the failure in the issue
34+
}
35+
36+
// Set adds an item to the cache, replacing any existing item.
37+
func (c *anyCache[T]) Set(k string, x T) {
38+
c.items[k] = Item[T]{
39+
Object: x,
40+
}
41+
}
42+
43+
// Get gets an item from the cache. Returns the item or nil, and a bool indicating
44+
// whether the key was found.
45+
func (c *anyCache[T]) Get(k string) (T, bool) {
46+
// "Inlining" of get and Expired
47+
item, found := c.items[k]
48+
if !found {
49+
var ret T
50+
return ret, false
51+
}
52+
53+
return item.Object, true
54+
}
55+
56+
type janitor[T any] struct {
57+
stop chan bool
58+
}
59+
60+
func newAnyCache[T any](m map[string]Item[T]) *anyCache[T] {
61+
c := &anyCache[T]{
62+
items: m,
63+
}
64+
return c
65+
}
66+
67+
// NewAny[T any](...) returns a new AnyCache[T].
68+
func NewAny[T any]() *AnyCache[T] {
69+
items := make(map[string]Item[T])
70+
return &AnyCache[T]{newAnyCache(items)}
71+
}
72+
73+
// NewAnyCacher[T any](...) returns an AnyCacher[T] interface.
74+
func NewAnyCacher[T any]() AnyCacher[T] {
75+
return NewAny[T]()
76+
}
77+
78+
type MyStruct struct {
79+
Name string
80+
}
81+
82+
func main() {
83+
// Create a generic cache.
84+
// All items are cached as interface{} so they need to be cast back to their
85+
// original type when retrieved.
86+
// Failure in issue doesn't happen with 'any' replaced by 'interface{}'
87+
c := NewAnyCacher[any]()
88+
89+
myStruct := &MyStruct{"MySuperStruct"}
90+
91+
c.Set("MySuperStruct", myStruct)
92+
93+
myRawCachedStruct, found := c.Get("MySuperStruct")
94+
95+
if found {
96+
// Casting the retrieved object back to its original type
97+
myCachedStruct := myRawCachedStruct.(*MyStruct)
98+
fmt.Printf("%s", myCachedStruct.Name)
99+
} else {
100+
fmt.Printf("Error: MySuperStruct not found in cache")
101+
}
102+
103+
// Output:
104+
// MySuperStruct
105+
}

test/typeparam/issue50109.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
MySuperStruct

0 commit comments

Comments
 (0)