]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: fix identity case relating to 'any' and shape types
authorDan Scales <danscales@google.com>
Sun, 12 Dec 2021 19:08:59 +0000 (11:08 -0800)
committerDan Scales <danscales@google.com>
Mon, 13 Dec 2021 06:35:06 +0000 (06:35 +0000)
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>
src/cmd/compile/internal/types/identity.go
test/typeparam/issue50109.go [new file with mode: 0644]
test/typeparam/issue50109.out [new file with mode: 0644]

index f99e50a1c3851a21c241e8fe1ed929f1ffe80bb1..a164b84da929b3259396cc0b7410c405cc7c9406 100644 (file)
@@ -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
diff --git a/test/typeparam/issue50109.go b/test/typeparam/issue50109.go
new file mode 100644 (file)
index 0000000..a6913df
--- /dev/null
@@ -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
+}
diff --git a/test/typeparam/issue50109.out b/test/typeparam/issue50109.out
new file mode 100644 (file)
index 0000000..7d6ecc0
--- /dev/null
@@ -0,0 +1 @@
+MySuperStruct
\ No newline at end of file