aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2023-10-02 15:47:08 -0700
committerGopher Robot <gobot@golang.org>2023-10-12 23:16:08 +0000
commitbae01521f3ab27979b454f2ecc77ff9403965957 (patch)
treeb8d76207e91e8084683a973382b8d38adca5f57a
parent236c07c0496786586f72e2a96ed15003f71ff975 (diff)
downloadgo-bae01521f3ab27979b454f2ecc77ff9403965957.tar.gz
go-bae01521f3ab27979b454f2ecc77ff9403965957.zip
[release-branch.go1.21] go/types, types2: don't implicitly modify an argument function's type
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>
-rw-r--r--src/cmd/compile/internal/types2/call.go7
-rw-r--r--src/cmd/compile/internal/types2/issues_test.go44
-rw-r--r--src/cmd/compile/internal/types2/predicates.go6
-rw-r--r--src/go/types/call.go7
-rw-r--r--src/go/types/issues_test.go44
-rw-r--r--src/go/types/predicates.go6
6 files changed, 114 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go
index f7a8a8dfcd..4d7d9b6634 100644
--- a/src/cmd/compile/internal/types2/call.go
+++ b/src/cmd/compile/internal/types2/call.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go
index 9f67ad0902..3ac345729b 100644
--- a/src/cmd/compile/internal/types2/issues_test.go
+++ b/src/cmd/compile/internal/types2/issues_test.go
@@ -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)
+ }
+}
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
index 13a3bf8af5..f4203789f0 100644
--- a/src/cmd/compile/internal/types2/predicates.go
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -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
+}
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 8a3cec7309..fd0de54e25 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -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
diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go
index 64e1c20d7e..4a559cbab3 100644
--- a/src/go/types/issues_test.go
+++ b/src/go/types/issues_test.go
@@ -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)
+ }
+}
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index b821b584c1..a78191871f 100644
--- a/src/go/types/predicates.go
+++ b/src/go/types/predicates.go
@@ -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
+}