From: Robert Griesemer Date: Mon, 2 Oct 2023 22:47:08 +0000 (-0700) Subject: [release-branch.go1.21] go/types, types2: don't implicitly modify an argument functio... X-Git-Tag: go1.21.4~7 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=bae01521f3ab27979b454f2ecc77ff9403965957;p=gostls13.git [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 Reviewed-by: Robert Findley Reviewed-by: Robert Griesemer Auto-Submit: Robert Griesemer TryBot-Result: Gopher Robot Reviewed-on: https://go-review.googlesource.com/c/go/+/531998 Auto-Submit: Dmitri Shuralyov --- 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 +}