]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.18] go/types, types2: record all type instances, even duplicates
authorRobert Findley <rfindley@google.com>
Fri, 4 Mar 2022 23:52:49 +0000 (18:52 -0500)
committerDmitri Shuralyov <dmitshur@golang.org>
Tue, 8 Mar 2022 00:42:28 +0000 (00:42 +0000)
Due to instance de-duplication, we were failing to record some type
instances in types.Info.Instances. Fix this by moving the instance
recording out of the resolver.

Fixes #51494

Change-Id: Iddd8989307d95886eedb321efa4ab98cd2b3573a
Reviewed-on: https://go-review.googlesource.com/c/go/+/390041
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 43b09c096a33b8a3cd3477546c445e2c41efcfdf)
Reviewed-on: https://go-review.googlesource.com/c/go/+/390574
Trust: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/typexpr.go
src/go/types/api_test.go
src/go/types/typexpr.go

index 5c38c59c80336b6df14a214c1a428bba92225332..5bb551798ee230499178fa27699194de1cc54eac 100644 (file)
@@ -12,6 +12,7 @@ import (
        "internal/testenv"
        "reflect"
        "regexp"
+       "sort"
        "strings"
        "testing"
 
@@ -403,69 +404,61 @@ func TestTypesInfo(t *testing.T) {
 }
 
 func TestInstanceInfo(t *testing.T) {
-       var tests = []struct {
-               src   string
+       const lib = `package lib
+
+func F[P any](P) {}
+
+type T[P any] []P
+`
+
+       type testInst struct {
                name  string
                targs []string
                typ   string
+       }
+
+       var tests = []struct {
+               src       string
+               instances []testInst // recorded instances in source order
        }{
                {`package p0; func f[T any](T) {}; func _() { f(42) }`,
-                       `f`,
-                       []string{`int`},
-                       `func(int)`,
+                       []testInst{{`f`, []string{`int`}, `func(int)`}},
                },
                {`package p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`,
-                       `f`,
-                       []string{`rune`},
-                       `func(rune) rune`,
+                       []testInst{{`f`, []string{`rune`}, `func(rune) rune`}},
                },
                {`package p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`,
-                       `f`,
-                       []string{`complex128`},
-                       `func(...complex128) complex128`,
+                       []testInst{{`f`, []string{`complex128`}, `func(...complex128) complex128`}},
                },
                {`package p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`,
-                       `f`,
-                       []string{`float64`, `string`, `byte`},
-                       `func(float64, *string, []byte)`,
+                       []testInst{{`f`, []string{`float64`, `string`, `byte`}, `func(float64, *string, []byte)`}},
                },
                {`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`,
-                       `f`,
-                       []string{`float64`, `byte`},
-                       `func(float64, *byte, ...[]byte)`,
+                       []testInst{{`f`, []string{`float64`, `byte`}, `func(float64, *byte, ...[]byte)`}},
                },
-
                // we don't know how to translate these but we can type-check them
                {`package q0; type T struct{}; func (T) m[P any](P) {}; func _(x T) { x.m(42) }`,
-                       `m`,
-                       []string{`int`},
-                       `func(int)`,
+                       []testInst{{`m`, []string{`int`}, `func(int)`}},
                },
                {`package q1; type T struct{}; func (T) m[P any](P) P { panic(0) }; func _(x T) { x.m(42) }`,
-                       `m`,
-                       []string{`int`},
-                       `func(int) int`,
+                       []testInst{{`m`, []string{`int`}, `func(int) int`}},
                },
                {`package q2; type T struct{}; func (T) m[P any](...P) P { panic(0) }; func _(x T) { x.m(42) }`,
-                       `m`,
-                       []string{`int`},
-                       `func(...int) int`,
+                       []testInst{{`m`, []string{`int`}, `func(...int) int`}},
                },
                {`package q3; type T struct{}; func (T) m[A, B, C any](A, *B, []C) {}; func _(x T) { x.m(1.2, new(string), []byte{}) }`,
-                       `m`,
-                       []string{`float64`, `string`, `byte`},
-                       `func(float64, *string, []byte)`,
+                       []testInst{{`m`, []string{`float64`, `string`, `byte`}, `func(float64, *string, []byte)`}},
                },
                {`package q4; type T struct{}; func (T) m[A, B any](A, *B, ...[]B) {}; func _(x T) { x.m(1.2, new(byte)) }`,
-                       `m`,
-                       []string{`float64`, `byte`},
-                       `func(float64, *byte, ...[]byte)`,
+                       []testInst{{`m`, []string{`float64`, `byte`}, `func(float64, *byte, ...[]byte)`}},
                },
 
-               {`package r0; type T[P any] struct{}; func (_ T[P]) m[Q any](Q) {}; func _[P any](x T[P]) { x.m(42) }`,
-                       `m`,
-                       []string{`int`},
-                       `func(int)`,
+               {`package r0; type T[P1 any] struct{}; func (_ T[P2]) m[Q any](Q) {}; func _[P3 any](x T[P3]) { x.m(42) }`,
+                       []testInst{
+                               {`T`, []string{`P2`}, `struct{}`},
+                               {`T`, []string{`P3`}, `struct{}`},
+                               {`m`, []string{`int`}, `func(int)`},
+                       },
                },
                // TODO(gri) record method type parameters in syntax.FuncType so we can check this
                // {`package r1; type T interface{ m[P any](P) }; func _(x T) { x.m(4.2) }`,
@@ -475,98 +468,112 @@ func TestInstanceInfo(t *testing.T) {
                // },
 
                {`package s1; func f[T any, P interface{*T}](x T) {}; func _(x string) { f(x) }`,
-                       `f`,
-                       []string{`string`, `*string`},
-                       `func(x string)`,
+                       []testInst{{`f`, []string{`string`, `*string`}, `func(x string)`}},
                },
                {`package s2; func f[T any, P interface{*T}](x []T) {}; func _(x []int) { f(x) }`,
-                       `f`,
-                       []string{`int`, `*int`},
-                       `func(x []int)`,
+                       []testInst{{`f`, []string{`int`, `*int`}, `func(x []int)`}},
                },
                {`package s3; type C[T any] interface{chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
-                       `f`,
-                       []string{`int`, `chan<- int`},
-                       `func(x []int)`,
+                       []testInst{
+                               {`C`, []string{`T`}, `interface{chan<- T}`},
+                               {`f`, []string{`int`, `chan<- int`}, `func(x []int)`},
+                       },
                },
                {`package s4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
-                       `f`,
-                       []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
-                       `func(x []int)`,
+                       []testInst{
+                               {`C`, []string{`T`}, `interface{chan<- T}`},
+                               {`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
+                               {`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func(x []int)`},
+                       },
                },
 
                {`package t1; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = f[string] }`,
-                       `f`,
-                       []string{`string`, `*string`},
-                       `func() string`,
+                       []testInst{{`f`, []string{`string`, `*string`}, `func() string`}},
                },
                {`package t2; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
-                       `f`,
-                       []string{`string`, `*string`},
-                       `func() string`,
+                       []testInst{{`f`, []string{`string`, `*string`}, `func() string`}},
                },
                {`package t3; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
-                       `f`,
-                       []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
-                       `func() []int`,
+                       []testInst{
+                               {`C`, []string{`T`}, `interface{chan<- T}`},
+                               {`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
+                               {`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func() []int`},
+                       },
                },
                {`package t4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = (f[int]) }`,
-                       `f`,
-                       []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
-                       `func() []int`,
+                       []testInst{
+                               {`C`, []string{`T`}, `interface{chan<- T}`},
+                               {`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
+                               {`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func() []int`},
+                       },
                },
-
                {`package i0; import "lib"; func _() { lib.F(42) }`,
-                       `F`,
-                       []string{`int`},
-                       `func(int)`,
+                       []testInst{{`F`, []string{`int`}, `func(int)`}},
+               },
+
+               {`package duplfunc0; func f[T any](T) {}; func _() { f(42); f("foo"); f[int](3) }`,
+                       []testInst{
+                               {`f`, []string{`int`}, `func(int)`},
+                               {`f`, []string{`string`}, `func(string)`},
+                               {`f`, []string{`int`}, `func(int)`},
+                       },
+               },
+               {`package duplfunc1; import "lib"; func _() { lib.F(42); lib.F("foo"); lib.F(3) }`,
+                       []testInst{
+                               {`F`, []string{`int`}, `func(int)`},
+                               {`F`, []string{`string`}, `func(string)`},
+                               {`F`, []string{`int`}, `func(int)`},
+                       },
                },
 
                {`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
-                       `T`,
-                       []string{`int`},
-                       `struct{x int}`,
+                       []testInst{{`T`, []string{`int`}, `struct{x int}`}},
                },
                {`package type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`,
-                       `T`,
-                       []string{`int`},
-                       `struct{x int}`,
+                       []testInst{{`T`, []string{`int`}, `struct{x int}`}},
                },
                {`package type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`,
-                       `T`,
-                       []string{`int`},
-                       `struct{x int}`,
+                       []testInst{{`T`, []string{`int`}, `struct{x int}`}},
                },
                {`package type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`,
-                       `T`,
-                       []string{`[]int`, `int`},
-                       `struct{x []int; y int}`,
+                       []testInst{{`T`, []string{`[]int`, `int`}, `struct{x []int; y int}`}},
                },
                {`package type4; import "lib"; var _ lib.T[int]`,
-                       `T`,
-                       []string{`int`},
-                       `[]int`,
+                       []testInst{{`T`, []string{`int`}, `[]int`}},
+               },
+
+               {`package dupltype0; type T[P interface{~int}] struct{ x P }; var x T[int]; var y T[int]`,
+                       []testInst{
+                               {`T`, []string{`int`}, `struct{x int}`},
+                               {`T`, []string{`int`}, `struct{x int}`},
+                       },
+               },
+               {`package dupltype1; type T[P ~int] struct{ x P }; func (r *T[Q]) add(z T[Q]) { r.x += z.x }`,
+                       []testInst{
+                               {`T`, []string{`Q`}, `struct{x Q}`},
+                               {`T`, []string{`Q`}, `struct{x Q}`},
+                       },
+               },
+               {`package dupltype1; import "lib"; var x lib.T[int]; var y lib.T[int]; var z lib.T[string]`,
+                       []testInst{
+                               {`T`, []string{`int`}, `[]int`},
+                               {`T`, []string{`int`}, `[]int`},
+                               {`T`, []string{`string`}, `[]string`},
+                       },
                },
        }
 
        for _, test := range tests {
-               const lib = `package lib
-
-func F[P any](P) {}
-
-type T[P any] []P
-`
-
                imports := make(testImporter)
                conf := Config{Importer: imports}
-               instances := make(map[*syntax.Name]Instance)
-               uses := make(map[*syntax.Name]Object)
+               instMap := make(map[*syntax.Name]Instance)
+               useMap := make(map[*syntax.Name]Object)
                makePkg := func(src string) *Package {
                        f, err := parseSrc("p.go", src)
                        if err != nil {
                                t.Fatal(err)
                        }
-                       pkg, err := conf.Check("", []*syntax.File{f}, &Info{Instances: instances, Uses: uses})
+                       pkg, err := conf.Check("", []*syntax.File{f}, &Info{Instances: instMap, Uses: useMap})
                        if err != nil {
                                t.Fatal(err)
                        }
@@ -576,58 +583,70 @@ type T[P any] []P
                makePkg(lib)
                pkg := makePkg(test.src)
 
-               // look for instance information
-               var targs []Type
-               var typ Type
-               for ident, inst := range instances {
-                       if syntax.String(ident) == test.name {
-                               for i := 0; i < inst.TypeArgs.Len(); i++ {
-                                       targs = append(targs, inst.TypeArgs.At(i))
+               t.Run(pkg.Name(), func(t *testing.T) {
+                       // Sort instances in source order for stability.
+                       instances := sortedInstances(instMap)
+                       if got, want := len(instances), len(test.instances); got != want {
+                               t.Fatalf("got %d instances, want %d", got, want)
+                       }
+
+                       // Pairwise compare with the expected instances.
+                       for ii, inst := range instances {
+                               var targs []Type
+                               for i := 0; i < inst.Inst.TypeArgs.Len(); i++ {
+                                       targs = append(targs, inst.Inst.TypeArgs.At(i))
+                               }
+                               typ := inst.Inst.Type
+
+                               testInst := test.instances[ii]
+                               if got := inst.Name.Value; got != testInst.name {
+                                       t.Fatalf("got name %s, want %s", got, testInst.name)
+                               }
+
+                               if len(targs) != len(testInst.targs) {
+                                       t.Fatalf("got %d type arguments; want %d", len(targs), len(testInst.targs))
+                               }
+                               for i, targ := range targs {
+                                       if got := targ.String(); got != testInst.targs[i] {
+                                               t.Errorf("type argument %d: got %s; want %s", i, got, testInst.targs[i])
+                                       }
+                               }
+                               if got := typ.Underlying().String(); got != testInst.typ {
+                                       t.Errorf("package %s: got %s; want %s", pkg.Name(), got, testInst.typ)
                                }
-                               typ = inst.Type
 
-                               // Check that we can find the corresponding parameterized type.
-                               ptype := uses[ident].Type()
+                               // Verify the invariant that re-instantiating the corresponding generic
+                               // type with TypeArgs results in an identical instance.
+                               ptype := useMap[inst.Name].Type()
                                lister, _ := ptype.(interface{ TypeParams() *TypeParamList })
                                if lister == nil || lister.TypeParams().Len() == 0 {
-                                       t.Errorf("package %s: info.Types[%v] = %v, want parameterized type", pkg.Name(), ident, ptype)
-                                       continue
+                                       t.Fatalf("info.Types[%v] = %v, want parameterized type", inst.Name, ptype)
                                }
-
-                               // Verify the invariant that re-instantiating the generic type with
-                               // TypeArgs results in an equivalent type.
                                inst2, err := Instantiate(nil, ptype, targs, true)
                                if err != nil {
                                        t.Errorf("Instantiate(%v, %v) failed: %v", ptype, targs, err)
                                }
-                               if !Identical(inst.Type, inst2) {
-                                       t.Errorf("%v and %v are not identical", inst.Type, inst2)
+                               if !Identical(inst.Inst.Type, inst2) {
+                                       t.Errorf("%v and %v are not identical", inst.Inst.Type, inst2)
                                }
-                               break
                        }
-               }
-               if targs == nil {
-                       t.Errorf("package %s: no instance information found for %s", pkg.Name(), test.name)
-                       continue
-               }
+               })
+       }
+}
 
-               // check that type arguments are correct
-               if len(targs) != len(test.targs) {
-                       t.Errorf("package %s: got %d type arguments; want %d", pkg.Name(), len(targs), len(test.targs))
-                       continue
-               }
-               for i, targ := range targs {
-                       if got := targ.String(); got != test.targs[i] {
-                               t.Errorf("package %s, %d. type argument: got %s; want %s", pkg.Name(), i, got, test.targs[i])
-                               continue
-                       }
-               }
+type recordedInstance struct {
+       Name *syntax.Name
+       Inst Instance
+}
 
-               // check that the types match
-               if got := typ.Underlying().String(); got != test.typ {
-                       t.Errorf("package %s: got %s; want %s", pkg.Name(), got, test.typ)
-               }
+func sortedInstances(m map[*syntax.Name]Instance) (instances []recordedInstance) {
+       for id, inst := range m {
+               instances = append(instances, recordedInstance{id, inst})
        }
+       sort.Slice(instances, func(i, j int) bool {
+               return instances[i].Name.Pos().Cmp(instances[j].Name.Pos()) < 0
+       })
+       return instances
 }
 
 func TestDefsInfo(t *testing.T) {
index b9bc992a82f4634824b86cad7a495ccc5d2e6bcb..89c1f7b3a08756a71a66542e42bc11790f9ef16e 100644 (file)
@@ -455,17 +455,15 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
        inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) {
                tparams := orig.TypeParams().list()
 
-               inferred := targs
                if len(targs) < len(tparams) {
                        // If inference fails, len(inferred) will be 0, and inst.underlying will
                        // be set to Typ[Invalid] in expandNamed.
-                       inferred = check.infer(x.Pos(), tparams, targs, nil, nil)
+                       inferred := check.infer(x.Pos(), tparams, targs, nil, nil)
                        if len(inferred) > len(targs) {
                                inst.targs = newTypeList(inferred)
                        }
                }
 
-               check.recordInstance(x, inferred, inst)
                return expandNamed(ctxt, n, x.Pos())
        }
 
@@ -475,6 +473,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
                // and so it must be resolved during type-checking so that we can report
                // errors.
                inst.resolve(ctxt)
+               check.recordInstance(x, inst.TypeArgs().list(), inst)
                // Since check is non-nil, we can still mutate inst. Unpinning the resolver
                // frees some memory.
                inst.resolver = nil
index 4c732dd58e7b26713b3f710f297f183d1962cf33..4014201769ea546537d1d502ddbd09554f311957 100644 (file)
@@ -16,6 +16,7 @@ import (
        "internal/testenv"
        "reflect"
        "regexp"
+       "sort"
        "strings"
        "testing"
 
@@ -434,131 +435,146 @@ func TestTypesInfo(t *testing.T) {
 }
 
 func TestInstanceInfo(t *testing.T) {
-       var tests = []struct {
-               src   string
+       const lib = `package lib
+
+func F[P any](P) {}
+
+type T[P any] []P
+`
+
+       type testInst struct {
                name  string
                targs []string
                typ   string
+       }
+
+       var tests = []struct {
+               src       string
+               instances []testInst // recorded instances in source order
        }{
                {`package p0; func f[T any](T) {}; func _() { f(42) }`,
-                       `f`,
-                       []string{`int`},
-                       `func(int)`,
+                       []testInst{{`f`, []string{`int`}, `func(int)`}},
                },
                {`package p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`,
-                       `f`,
-                       []string{`rune`},
-                       `func(rune) rune`,
+                       []testInst{{`f`, []string{`rune`}, `func(rune) rune`}},
                },
                {`package p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`,
-                       `f`,
-                       []string{`complex128`},
-                       `func(...complex128) complex128`,
+                       []testInst{{`f`, []string{`complex128`}, `func(...complex128) complex128`}},
                },
                {`package p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`,
-                       `f`,
-                       []string{`float64`, `string`, `byte`},
-                       `func(float64, *string, []byte)`,
+                       []testInst{{`f`, []string{`float64`, `string`, `byte`}, `func(float64, *string, []byte)`}},
                },
                {`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`,
-                       `f`,
-                       []string{`float64`, `byte`},
-                       `func(float64, *byte, ...[]byte)`,
+                       []testInst{{`f`, []string{`float64`, `byte`}, `func(float64, *byte, ...[]byte)`}},
                },
 
                {`package s1; func f[T any, P interface{*T}](x T) {}; func _(x string) { f(x) }`,
-                       `f`,
-                       []string{`string`, `*string`},
-                       `func(x string)`,
+                       []testInst{{`f`, []string{`string`, `*string`}, `func(x string)`}},
                },
                {`package s2; func f[T any, P interface{*T}](x []T) {}; func _(x []int) { f(x) }`,
-                       `f`,
-                       []string{`int`, `*int`},
-                       `func(x []int)`,
+                       []testInst{{`f`, []string{`int`, `*int`}, `func(x []int)`}},
                },
                {`package s3; type C[T any] interface{chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
-                       `f`,
-                       []string{`int`, `chan<- int`},
-                       `func(x []int)`,
+                       []testInst{
+                               {`C`, []string{`T`}, `interface{chan<- T}`},
+                               {`f`, []string{`int`, `chan<- int`}, `func(x []int)`},
+                       },
                },
                {`package s4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
-                       `f`,
-                       []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
-                       `func(x []int)`,
+                       []testInst{
+                               {`C`, []string{`T`}, `interface{chan<- T}`},
+                               {`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
+                               {`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func(x []int)`},
+                       },
                },
 
                {`package t1; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = f[string] }`,
-                       `f`,
-                       []string{`string`, `*string`},
-                       `func() string`,
+                       []testInst{{`f`, []string{`string`, `*string`}, `func() string`}},
                },
                {`package t2; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
-                       `f`,
-                       []string{`string`, `*string`},
-                       `func() string`,
+                       []testInst{{`f`, []string{`string`, `*string`}, `func() string`}},
                },
                {`package t3; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
-                       `f`,
-                       []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
-                       `func() []int`,
+                       []testInst{
+                               {`C`, []string{`T`}, `interface{chan<- T}`},
+                               {`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
+                               {`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func() []int`},
+                       },
                },
                {`package t4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = (f[int]) }`,
-                       `f`,
-                       []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
-                       `func() []int`,
+                       []testInst{
+                               {`C`, []string{`T`}, `interface{chan<- T}`},
+                               {`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
+                               {`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func() []int`},
+                       },
                },
-
                {`package i0; import "lib"; func _() { lib.F(42) }`,
-                       `F`,
-                       []string{`int`},
-                       `func(int)`,
+                       []testInst{{`F`, []string{`int`}, `func(int)`}},
+               },
+
+               {`package duplfunc0; func f[T any](T) {}; func _() { f(42); f("foo"); f[int](3) }`,
+                       []testInst{
+                               {`f`, []string{`int`}, `func(int)`},
+                               {`f`, []string{`string`}, `func(string)`},
+                               {`f`, []string{`int`}, `func(int)`},
+                       },
+               },
+               {`package duplfunc1; import "lib"; func _() { lib.F(42); lib.F("foo"); lib.F(3) }`,
+                       []testInst{
+                               {`F`, []string{`int`}, `func(int)`},
+                               {`F`, []string{`string`}, `func(string)`},
+                               {`F`, []string{`int`}, `func(int)`},
+                       },
                },
 
                {`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
-                       `T`,
-                       []string{`int`},
-                       `struct{x int}`,
+                       []testInst{{`T`, []string{`int`}, `struct{x int}`}},
                },
                {`package type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`,
-                       `T`,
-                       []string{`int`},
-                       `struct{x int}`,
+                       []testInst{{`T`, []string{`int`}, `struct{x int}`}},
                },
                {`package type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`,
-                       `T`,
-                       []string{`int`},
-                       `struct{x int}`,
+                       []testInst{{`T`, []string{`int`}, `struct{x int}`}},
                },
                {`package type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`,
-                       `T`,
-                       []string{`[]int`, `int`},
-                       `struct{x []int; y int}`,
+                       []testInst{{`T`, []string{`[]int`, `int`}, `struct{x []int; y int}`}},
                },
                {`package type4; import "lib"; var _ lib.T[int]`,
-                       `T`,
-                       []string{`int`},
-                       `[]int`,
+                       []testInst{{`T`, []string{`int`}, `[]int`}},
+               },
+
+               {`package dupltype0; type T[P interface{~int}] struct{ x P }; var x T[int]; var y T[int]`,
+                       []testInst{
+                               {`T`, []string{`int`}, `struct{x int}`},
+                               {`T`, []string{`int`}, `struct{x int}`},
+                       },
+               },
+               {`package dupltype1; type T[P ~int] struct{ x P }; func (r *T[Q]) add(z T[Q]) { r.x += z.x }`,
+                       []testInst{
+                               {`T`, []string{`Q`}, `struct{x Q}`},
+                               {`T`, []string{`Q`}, `struct{x Q}`},
+                       },
+               },
+               {`package dupltype1; import "lib"; var x lib.T[int]; var y lib.T[int]; var z lib.T[string]`,
+                       []testInst{
+                               {`T`, []string{`int`}, `[]int`},
+                               {`T`, []string{`int`}, `[]int`},
+                               {`T`, []string{`string`}, `[]string`},
+                       },
                },
        }
 
        for _, test := range tests {
-               const lib = `package lib
-
-func F[P any](P) {}
-
-type T[P any] []P
-`
-
                imports := make(testImporter)
                conf := Config{Importer: imports}
-               instances := make(map[*ast.Ident]Instance)
-               uses := make(map[*ast.Ident]Object)
+               instMap := make(map[*ast.Ident]Instance)
+               useMap := make(map[*ast.Ident]Object)
                makePkg := func(src string) *Package {
                        f, err := parser.ParseFile(fset, "p.go", src, 0)
                        if err != nil {
                                t.Fatal(err)
                        }
-                       pkg, err := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instances, Uses: uses})
+                       pkg, err := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instMap, Uses: useMap})
                        if err != nil {
                                t.Fatal(err)
                        }
@@ -568,58 +584,69 @@ type T[P any] []P
                makePkg(lib)
                pkg := makePkg(test.src)
 
-               // look for instance information
-               var targs []Type
-               var typ Type
-               for ident, inst := range instances {
-                       if ExprString(ident) == test.name {
-                               for i := 0; i < inst.TypeArgs.Len(); i++ {
-                                       targs = append(targs, inst.TypeArgs.At(i))
+               t.Run(pkg.Name(), func(t *testing.T) {
+                       // Sort instances in source order for stability.
+                       instances := sortedInstances(instMap)
+                       if got, want := len(instances), len(test.instances); got != want {
+                               t.Fatalf("got %d instances, want %d", got, want)
+                       }
+
+                       // Pairwise compare with the expected instances.
+                       for ii, inst := range instances {
+                               var targs []Type
+                               for i := 0; i < inst.Inst.TypeArgs.Len(); i++ {
+                                       targs = append(targs, inst.Inst.TypeArgs.At(i))
+                               }
+                               typ := inst.Inst.Type
+
+                               testInst := test.instances[ii]
+                               if got := inst.Ident.Name; got != testInst.name {
+                                       t.Fatalf("got name %s, want %s", got, testInst.name)
+                               }
+                               if len(targs) != len(testInst.targs) {
+                                       t.Fatalf("got %d type arguments; want %d", len(targs), len(testInst.targs))
+                               }
+                               for i, targ := range targs {
+                                       if got := targ.String(); got != testInst.targs[i] {
+                                               t.Errorf("type argument %d: got %s; want %s", i, got, testInst.targs[i])
+                                       }
+                               }
+                               if got := typ.Underlying().String(); got != testInst.typ {
+                                       t.Errorf("package %s: got %s; want %s", pkg.Name(), got, testInst.typ)
                                }
-                               typ = inst.Type
 
-                               // Check that we can find the corresponding parameterized type.
-                               ptype := uses[ident].Type()
+                               // Verify the invariant that re-instantiating the corresponding generic
+                               // type with TypeArgs results in an identical instance.
+                               ptype := useMap[inst.Ident].Type()
                                lister, _ := ptype.(interface{ TypeParams() *TypeParamList })
                                if lister == nil || lister.TypeParams().Len() == 0 {
-                                       t.Errorf("package %s: info.Types[%v] = %v, want parameterized type", pkg.Name(), ident, ptype)
-                                       continue
+                                       t.Fatalf("info.Types[%v] = %v, want parameterized type", inst.Ident, ptype)
                                }
-
-                               // Verify the invariant that re-instantiating the generic type with
-                               // TypeArgs results in an equivalent type.
                                inst2, err := Instantiate(nil, ptype, targs, true)
                                if err != nil {
                                        t.Errorf("Instantiate(%v, %v) failed: %v", ptype, targs, err)
                                }
-                               if !Identical(inst.Type, inst2) {
-                                       t.Errorf("%v and %v are not identical", inst.Type, inst2)
+                               if !Identical(inst.Inst.Type, inst2) {
+                                       t.Errorf("%v and %v are not identical", inst.Inst.Type, inst2)
                                }
-                               break
                        }
-               }
-               if targs == nil {
-                       t.Errorf("package %s: no instance information found for %s", pkg.Name(), test.name)
-                       continue
-               }
+               })
+       }
+}
 
-               // check that type arguments are correct
-               if len(targs) != len(test.targs) {
-                       t.Errorf("package %s: got %d type arguments; want %d", pkg.Name(), len(targs), len(test.targs))
-                       continue
-               }
-               for i, targ := range targs {
-                       if got := targ.String(); got != test.targs[i] {
-                               t.Errorf("package %s, %d. type argument: got %s; want %s", pkg.Name(), i, got, test.targs[i])
-                               continue
-                       }
-               }
+type recordedInstance struct {
+       Ident *ast.Ident
+       Inst  Instance
+}
 
-               // check that the types match
-               if got := typ.Underlying().String(); got != test.typ {
-                       t.Errorf("package %s: got %s; want %s", pkg.Name(), got, test.typ)
-               }
+func sortedInstances(m map[*ast.Ident]Instance) (instances []recordedInstance) {
+       for id, inst := range m {
+               instances = append(instances, recordedInstance{id, inst})
        }
+       sort.Slice(instances, func(i, j int) bool {
+               return instances[i].Ident.Pos() < instances[j].Ident.Pos()
+       })
+       return instances
 }
 
 func TestDefsInfo(t *testing.T) {
index 838febc087b94ee1c68537960bd2fbc5fa7ab2e4..373ade04eb5edad07780febd693693e0afe299f4 100644 (file)
@@ -440,17 +440,15 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
        inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) {
                tparams := orig.TypeParams().list()
 
-               inferred := targs
                if len(targs) < len(tparams) {
                        // If inference fails, len(inferred) will be 0, and inst.underlying will
                        // be set to Typ[Invalid] in expandNamed.
-                       inferred = check.infer(ix.Orig, tparams, targs, nil, nil)
+                       inferred := check.infer(ix.Orig, tparams, targs, nil, nil)
                        if len(inferred) > len(targs) {
                                inst.targs = newTypeList(inferred)
                        }
                }
 
-               check.recordInstance(ix.Orig, inferred, inst)
                return expandNamed(ctxt, n, pos)
        }
 
@@ -463,6 +461,7 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
                // Since check is non-nil, we can still mutate inst. Unpinning the resolver
                // frees some memory.
                inst.resolver = nil
+               check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst)
 
                if check.validateTArgLen(pos, inst.tparams.Len(), inst.targs.Len()) {
                        if i, err := check.verify(pos, inst.tparams.list(), inst.targs.list()); err != nil {