]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[release-branch.go1.21] go/types, types2: don't implicitly modify an argument functio...
authorRobert Griesemer <gri@golang.org>
Mon, 2 Oct 2023 22:47:08 +0000 (15:47 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 12 Oct 2023 23:16:08 +0000 (23:16 +0000)
See the comment in the (very small) fix for a detailed description.
Use the opportunity to introduce a generic clone function which may
be useful elsewhere.

Fixes #63339.

Change-Id: Ic63c6b8bc443011b1a201908254f7d062e1aec71
Reviewed-on: https://go-review.googlesource.com/c/go/+/532157
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-on: https://go-review.googlesource.com/c/go/+/531998
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>

src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/issues_test.go
src/cmd/compile/internal/types2/predicates.go
src/go/types/call.go
src/go/types/issues_test.go
src/go/types/predicates.go

index f7a8a8dfcd3c040a912b231940983e839ad901c0..4d7d9b6634019846475164e7bac41507ad595c8c 100644 (file)
@@ -569,6 +569,13 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
                for i, arg := range args {
                        // generic arguments cannot have a defined (*Named) type - no need for underlying type below
                        if asig, _ := arg.typ.(*Signature); asig != nil && asig.TypeParams().Len() > 0 {
+                               // The argument type is a generic function signature. This type is
+                               // pointer-identical with (it's copied from) the type of the generic
+                               // function argument and thus the function object.
+                               // Before we change the type (type parameter renaming, below), make
+                               // a clone of it as otherwise we implicitly modify the object's type
+                               // (go.dev/issues/63260).
+                               asig = clone(asig)
                                // Rename type parameters for cases like f(g, g); this gives each
                                // generic function argument a unique type identity (go.dev/issues/59956).
                                // TODO(gri) Consider only doing this if a function argument appears
index 9f67ad09025116deef80945bd08d12c88b66f352..3ac345729b9a51dc8dc3dc5e8c0945456a99f266 100644 (file)
@@ -920,3 +920,47 @@ func _() {
        var conf Config
        conf.Check(f.PkgName.Value, []*syntax.File{f}, nil) // must not panic
 }
+
+func TestIssue63260(t *testing.T) {
+       const src = `
+package p
+
+func _() {
+        use(f[*string])
+}
+
+func use(func()) {}
+
+func f[I *T, T any]() {
+        var v T
+        _ = v
+}`
+
+       info := Info{
+               Defs: make(map[*syntax.Name]Object),
+       }
+       pkg := mustTypecheck(src, nil, &info)
+
+       // get type parameter T in signature of f
+       T := pkg.Scope().Lookup("f").Type().(*Signature).TypeParams().At(1)
+       if T.Obj().Name() != "T" {
+               t.Fatalf("got type parameter %s, want T", T)
+       }
+
+       // get type of variable v in body of f
+       var v Object
+       for name, obj := range info.Defs {
+               if name.Value == "v" {
+                       v = obj
+                       break
+               }
+       }
+       if v == nil {
+               t.Fatal("variable v not found")
+       }
+
+       // type of v and T must be pointer-identical
+       if v.Type() != T {
+               t.Fatalf("types of v and T are not pointer-identical: %p != %p", v.Type().(*TypeParam), T)
+       }
+}
index 13a3bf8af5b5b43fd42f83abbc43a2ea3134c711..f4203789f05ff2518bca49360a6ce70daaf68495 100644 (file)
@@ -530,3 +530,9 @@ func maxType(x, y Type) Type {
        }
        return nil
 }
+
+// clone makes a "flat copy" of *p and returns a pointer to the copy.
+func clone[P *T, T any](p P) P {
+       c := *p
+       return &c
+}
index 8a3cec7309eae53573185f5926c0fc7356fe64e9..fd0de54e25d3552c11145a7e577e157b341c245f 100644 (file)
@@ -571,6 +571,13 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
                for i, arg := range args {
                        // generic arguments cannot have a defined (*Named) type - no need for underlying type below
                        if asig, _ := arg.typ.(*Signature); asig != nil && asig.TypeParams().Len() > 0 {
+                               // The argument type is a generic function signature. This type is
+                               // pointer-identical with (it's copied from) the type of the generic
+                               // function argument and thus the function object.
+                               // Before we change the type (type parameter renaming, below), make
+                               // a clone of it as otherwise we implicitly modify the object's type
+                               // (go.dev/issues/63260).
+                               asig = clone(asig)
                                // Rename type parameters for cases like f(g, g); this gives each
                                // generic function argument a unique type identity (go.dev/issues/59956).
                                // TODO(gri) Consider only doing this if a function argument appears
index 64e1c20d7eb1c31198dd13f8c857e6f05272923e..4a559cbab31581000cb083ce5f9abf636e18408c 100644 (file)
@@ -930,3 +930,47 @@ func _() {
        var conf Config
        conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // must not panic
 }
+
+func TestIssue63260(t *testing.T) {
+       const src = `
+package p
+
+func _() {
+        use(f[*string])
+}
+
+func use(func()) {}
+
+func f[I *T, T any]() {
+        var v T
+        _ = v
+}`
+
+       info := Info{
+               Defs: make(map[*ast.Ident]Object),
+       }
+       pkg := mustTypecheck(src, nil, &info)
+
+       // get type parameter T in signature of f
+       T := pkg.Scope().Lookup("f").Type().(*Signature).TypeParams().At(1)
+       if T.Obj().Name() != "T" {
+               t.Fatalf("got type parameter %s, want T", T)
+       }
+
+       // get type of variable v in body of f
+       var v Object
+       for name, obj := range info.Defs {
+               if name.Name == "v" {
+                       v = obj
+                       break
+               }
+       }
+       if v == nil {
+               t.Fatal("variable v not found")
+       }
+
+       // type of v and T must be pointer-identical
+       if v.Type() != T {
+               t.Fatalf("types of v and T are not pointer-identical: %p != %p", v.Type().(*TypeParam), T)
+       }
+}
index b821b584c151156634dad0a2633139e9f2cf35ed..a78191871f68465c4510591e0bce8c1d2352af55 100644 (file)
@@ -532,3 +532,9 @@ func maxType(x, y Type) Type {
        }
        return nil
 }
+
+// clone makes a "flat copy" of *p and returns a pointer to the copy.
+func clone[P *T, T any](p P) P {
+       c := *p
+       return &c
+}