mirror of https://github.com/golang/go.git
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 #63260. 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>
This commit is contained in:
parent
dc523c8ddf
commit
51cb717d27
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -936,3 +936,47 @@ func _() { f() }
|
|||
conf.Error = func(error) {}
|
||||
typecheck(src, &conf, 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -533,3 +533,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -946,3 +946,47 @@ func _() { f() }
|
|||
conf.Error = func(error) {}
|
||||
typecheck(src, &conf, 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -535,3 +535,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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue