From 43b09c096a33b8a3cd3477546c445e2c41efcfdf Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Fri, 4 Mar 2022 18:52:49 -0500 Subject: [PATCH] go/types, types2: record all type instances, even duplicates 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 Run-TryBot: Robert Findley Reviewed-by: Robert Griesemer TryBot-Result: Gopher Robot --- src/cmd/compile/internal/types2/api_test.go | 275 +++++++++++--------- src/cmd/compile/internal/types2/typexpr.go | 5 +- src/go/types/api_test.go | 243 +++++++++-------- src/go/types/typexpr.go | 5 +- 4 files changed, 286 insertions(+), 242 deletions(-) diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 5c38c59c80336..5bb551798ee23 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -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) { diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index b9bc992a82f46..89c1f7b3a0875 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -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 diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index 4c732dd58e7b2..4014201769ea5 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -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) { diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 838febc087b94..373ade04eb5ed 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -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 {