aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Findley <rfindley@google.com>2022-03-24 13:29:03 -0400
committerCherry Mui <cherryyz@google.com>2022-04-04 20:28:11 +0000
commit6412231192755c1d9f8a671614ace8b45fcbe49c (patch)
treeb6451d3f461421a8ce85758c29c0ae7ab6f96b6e
parent0bf8319883298dbeea81444cb704d8c0e9935bae (diff)
downloadgo-6412231192755c1d9f8a671614ace8b45fcbe49c.tar.gz
go-6412231192755c1d9f8a671614ace8b45fcbe49c.zip
[release-branch.go1.18] go/types, types2: preserve parent scope when substituting receivers
For #51920 Fixes #52007 Change-Id: I29e44a52dabee5c09e1761f9ec8fb2e8795f8901 Reviewed-on: https://go-review.googlesource.com/c/go/+/395538 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> (cherry picked from commit 9b90838712856cad86504a526e566f963f86c04e) Reviewed-on: https://go-review.googlesource.com/c/go/+/395680
-rw-r--r--src/cmd/compile/internal/types2/api_test.go97
-rw-r--r--src/cmd/compile/internal/types2/named.go2
-rw-r--r--src/cmd/compile/internal/types2/subst.go15
-rw-r--r--src/go/types/api_test.go98
-rw-r--r--src/go/types/named.go2
-rw-r--r--src/go/types/subst.go15
6 files changed, 215 insertions, 14 deletions
diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go
index 5bb551798e..dae39ad92c 100644
--- a/src/cmd/compile/internal/types2/api_test.go
+++ b/src/cmd/compile/internal/types2/api_test.go
@@ -2288,6 +2288,103 @@ func TestInstanceIdentity(t *testing.T) {
}
}
+// TestInstantiatedObjects verifies properties of instantiated objects.
+func TestInstantiatedObjects(t *testing.T) {
+ const src = `
+package p
+
+type T[P any] struct {
+ field P
+}
+
+func (recv *T[Q]) concreteMethod() {}
+
+type FT[P any] func(ftp P) (ftrp P)
+
+func F[P any](fp P) (frp P){ return }
+
+type I[P any] interface {
+ interfaceMethod(P)
+}
+
+var (
+ t T[int]
+ ft FT[int]
+ f = F[int]
+ i I[int]
+)
+`
+ info := &Info{
+ Defs: make(map[*syntax.Name]Object),
+ }
+ f, err := parseSrc("p.go", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ conf := Config{}
+ pkg, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, info)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ lookup := func(name string) Type { return pkg.Scope().Lookup(name).Type() }
+ tests := []struct {
+ ident string
+ obj Object
+ }{
+ {"field", lookup("t").Underlying().(*Struct).Field(0)},
+ {"concreteMethod", lookup("t").(*Named).Method(0)},
+ {"recv", lookup("t").(*Named).Method(0).Type().(*Signature).Recv()},
+ {"ftp", lookup("ft").Underlying().(*Signature).Params().At(0)},
+ {"ftrp", lookup("ft").Underlying().(*Signature).Results().At(0)},
+ {"fp", lookup("f").(*Signature).Params().At(0)},
+ {"frp", lookup("f").(*Signature).Results().At(0)},
+ {"interfaceMethod", lookup("i").Underlying().(*Interface).Method(0)},
+ }
+
+ // Collect all identifiers by name.
+ idents := make(map[string][]*syntax.Name)
+ syntax.Inspect(f, func(n syntax.Node) bool {
+ if id, ok := n.(*syntax.Name); ok {
+ idents[id.Value] = append(idents[id.Value], id)
+ }
+ return true
+ })
+
+ for _, test := range tests {
+ test := test
+ t.Run(test.ident, func(t *testing.T) {
+ if got := len(idents[test.ident]); got != 1 {
+ t.Fatalf("found %d identifiers named %s, want 1", got, test.ident)
+ }
+ ident := idents[test.ident][0]
+ def := info.Defs[ident]
+ if def == test.obj {
+ t.Fatalf("info.Defs[%s] contains the test object", test.ident)
+ }
+ if def.Pkg() != test.obj.Pkg() {
+ t.Errorf("Pkg() = %v, want %v", def.Pkg(), test.obj.Pkg())
+ }
+ if def.Name() != test.obj.Name() {
+ t.Errorf("Name() = %v, want %v", def.Name(), test.obj.Name())
+ }
+ if def.Pos() != test.obj.Pos() {
+ t.Errorf("Pos() = %v, want %v", def.Pos(), test.obj.Pos())
+ }
+ if def.Parent() != test.obj.Parent() {
+ t.Fatalf("Parent() = %v, want %v", def.Parent(), test.obj.Parent())
+ }
+ if def.Exported() != test.obj.Exported() {
+ t.Fatalf("Exported() = %v, want %v", def.Exported(), test.obj.Exported())
+ }
+ if def.Id() != test.obj.Id() {
+ t.Fatalf("Id() = %v, want %v", def.Id(), test.obj.Id())
+ }
+ // String and Type are expected to differ.
+ })
+ }
+}
+
func TestImplements(t *testing.T) {
const src = `
package p
diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go
index daf8fdc986..8a936ebcec 100644
--- a/src/cmd/compile/internal/types2/named.go
+++ b/src/cmd/compile/internal/types2/named.go
@@ -190,7 +190,7 @@ func (t *Named) instantiateMethod(i int) *Func {
rtyp = t
}
- sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
+ sig.recv = substVar(origSig.recv, rtyp)
return NewFunc(origm.pos, origm.pkg, origm.name, sig)
}
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
index 037f04797b..c9e8f9676d 100644
--- a/src/cmd/compile/internal/types2/subst.go
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -290,14 +290,18 @@ func (subst *subster) typOrNil(typ Type) Type {
func (subst *subster) var_(v *Var) *Var {
if v != nil {
if typ := subst.typ(v.typ); typ != v.typ {
- copy := *v
- copy.typ = typ
- return &copy
+ return substVar(v, typ)
}
}
return v
}
+func substVar(v *Var, typ Type) *Var {
+ copy := *v
+ copy.typ = typ
+ return &copy
+}
+
func (subst *subster) tuple(t *Tuple) *Tuple {
if t != nil {
if vars, copied := subst.varList(t.vars); copied {
@@ -410,9 +414,8 @@ func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) {
copied = true
}
newsig := *sig
- sig = &newsig
- sig.recv = NewVar(sig.recv.pos, sig.recv.pkg, "", new)
- out[i] = NewFunc(method.pos, method.pkg, method.name, sig)
+ newsig.recv = substVar(sig.recv, new)
+ out[i] = NewFunc(method.pos, method.pkg, method.name, &newsig)
}
}
return
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index 4014201769..6b05d66b10 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -2288,6 +2288,104 @@ func TestInstanceIdentity(t *testing.T) {
}
}
+// TestInstantiatedObjects verifies properties of instantiated objects.
+func TestInstantiatedObjects(t *testing.T) {
+ const src = `
+package p
+
+type T[P any] struct {
+ field P
+}
+
+func (recv *T[Q]) concreteMethod() {}
+
+type FT[P any] func(ftp P) (ftrp P)
+
+func F[P any](fp P) (frp P){ return }
+
+type I[P any] interface {
+ interfaceMethod(P)
+}
+
+var (
+ t T[int]
+ ft FT[int]
+ f = F[int]
+ i I[int]
+)
+`
+ info := &Info{
+ Defs: make(map[*ast.Ident]Object),
+ }
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "p.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ conf := Config{}
+ pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ lookup := func(name string) Type { return pkg.Scope().Lookup(name).Type() }
+ tests := []struct {
+ name string
+ obj Object
+ }{
+ {"field", lookup("t").Underlying().(*Struct).Field(0)},
+ {"concreteMethod", lookup("t").(*Named).Method(0)},
+ {"recv", lookup("t").(*Named).Method(0).Type().(*Signature).Recv()},
+ {"ftp", lookup("ft").Underlying().(*Signature).Params().At(0)},
+ {"ftrp", lookup("ft").Underlying().(*Signature).Results().At(0)},
+ {"fp", lookup("f").(*Signature).Params().At(0)},
+ {"frp", lookup("f").(*Signature).Results().At(0)},
+ {"interfaceMethod", lookup("i").Underlying().(*Interface).Method(0)},
+ }
+
+ // Collect all identifiers by name.
+ idents := make(map[string][]*ast.Ident)
+ ast.Inspect(f, func(n ast.Node) bool {
+ if id, ok := n.(*ast.Ident); ok {
+ idents[id.Name] = append(idents[id.Name], id)
+ }
+ return true
+ })
+
+ for _, test := range tests {
+ test := test
+ t.Run(test.name, func(t *testing.T) {
+ if got := len(idents[test.name]); got != 1 {
+ t.Fatalf("found %d identifiers named %s, want 1", got, test.name)
+ }
+ ident := idents[test.name][0]
+ def := info.Defs[ident]
+ if def == test.obj {
+ t.Fatalf("info.Defs[%s] contains the test object", test.name)
+ }
+ if def.Pkg() != test.obj.Pkg() {
+ t.Errorf("Pkg() = %v, want %v", def.Pkg(), test.obj.Pkg())
+ }
+ if def.Name() != test.obj.Name() {
+ t.Errorf("Name() = %v, want %v", def.Name(), test.obj.Name())
+ }
+ if def.Pos() != test.obj.Pos() {
+ t.Errorf("Pos() = %v, want %v", def.Pos(), test.obj.Pos())
+ }
+ if def.Parent() != test.obj.Parent() {
+ t.Fatalf("Parent() = %v, want %v", def.Parent(), test.obj.Parent())
+ }
+ if def.Exported() != test.obj.Exported() {
+ t.Fatalf("Exported() = %v, want %v", def.Exported(), test.obj.Exported())
+ }
+ if def.Id() != test.obj.Id() {
+ t.Fatalf("Id() = %v, want %v", def.Id(), test.obj.Id())
+ }
+ // String and Type are expected to differ.
+ })
+ }
+}
+
func TestImplements(t *testing.T) {
const src = `
package p
diff --git a/src/go/types/named.go b/src/go/types/named.go
index 876f7e8551..e60b229bb3 100644
--- a/src/go/types/named.go
+++ b/src/go/types/named.go
@@ -192,7 +192,7 @@ func (t *Named) instantiateMethod(i int) *Func {
rtyp = t
}
- sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
+ sig.recv = substVar(origSig.recv, rtyp)
return NewFunc(origm.pos, origm.pkg, origm.name, sig)
}
diff --git a/src/go/types/subst.go b/src/go/types/subst.go
index 4b4a0f4ad6..ef5593b71f 100644
--- a/src/go/types/subst.go
+++ b/src/go/types/subst.go
@@ -290,14 +290,18 @@ func (subst *subster) typOrNil(typ Type) Type {
func (subst *subster) var_(v *Var) *Var {
if v != nil {
if typ := subst.typ(v.typ); typ != v.typ {
- copy := *v
- copy.typ = typ
- return &copy
+ return substVar(v, typ)
}
}
return v
}
+func substVar(v *Var, typ Type) *Var {
+ copy := *v
+ copy.typ = typ
+ return &copy
+}
+
func (subst *subster) tuple(t *Tuple) *Tuple {
if t != nil {
if vars, copied := subst.varList(t.vars); copied {
@@ -410,9 +414,8 @@ func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) {
copied = true
}
newsig := *sig
- sig = &newsig
- sig.recv = NewVar(sig.recv.pos, sig.recv.pkg, "", new)
- out[i] = NewFunc(method.pos, method.pkg, method.name, sig)
+ newsig.recv = substVar(sig.recv, new)
+ out[i] = NewFunc(method.pos, method.pkg, method.name, &newsig)
}
}
return