Skip to content

Commit 1ba2074

Browse files
committed
[dev.typeparams] cmd/compile/internal/types2: support local defined types
This CL changes types2's instance hashing logic to include position information for function-scope defined types as disambiguation. This isn't ideal, but it worked for getting nested.go passing. Updates #46592. Change-Id: Id83ba0001f44af69b81260306cc8b05e44fc4f09 Reviewed-on: https://go-review.googlesource.com/c/go/+/327170 Run-TryBot: Matthew Dempsky <[email protected]> Trust: Matthew Dempsky <[email protected]> Trust: Robert Griesemer <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent dd95a4e commit 1ba2074

File tree

4 files changed

+168
-9
lines changed

4 files changed

+168
-9
lines changed

src/cmd/compile/internal/types2/subst.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,14 +425,19 @@ func (subst *subster) typ(typ Type) Type {
425425
return typ
426426
}
427427

428+
var instanceHashing = 0
429+
428430
// TODO(gri) Eventually, this should be more sophisticated.
429431
// It won't work correctly for locally declared types.
430432
func instantiatedHash(typ *Named, targs []Type) string {
433+
assert(instanceHashing == 0)
434+
instanceHashing++
431435
var buf bytes.Buffer
432436
writeTypeName(&buf, typ.obj, nil)
433437
buf.WriteByte('[')
434438
writeTypeList(&buf, targs, nil, nil)
435439
buf.WriteByte(']')
440+
instanceHashing--
436441

437442
// With respect to the represented type, whether a
438443
// type is fully expanded or stored as instance

src/cmd/compile/internal/types2/typestring.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -350,17 +350,33 @@ func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited
350350
}
351351

352352
func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
353-
s := "<Named w/o object>"
354-
if obj != nil {
355-
if obj.pkg != nil {
356-
writePackage(buf, obj.pkg, qf)
353+
if obj == nil {
354+
buf.WriteString("<Named w/o object>")
355+
return
356+
}
357+
if obj.pkg != nil {
358+
writePackage(buf, obj.pkg, qf)
359+
}
360+
buf.WriteString(obj.name)
361+
362+
if instanceHashing != 0 {
363+
// For local defined types, use the (original!) TypeName's position
364+
// to disambiguate. This is overkill, and could probably instead
365+
// just be the pointer value (if we assume a non-moving GC) or
366+
// a unique ID (like cmd/compile uses). But this works for now,
367+
// and is convenient for debugging.
368+
369+
// TODO(mdempsky): I still don't fully understand why typ.orig.orig
370+
// can differ from typ.orig, or whether looping more than twice is
371+
// ever necessary.
372+
typ := obj.typ.(*Named)
373+
for typ.orig != typ {
374+
typ = typ.orig
375+
}
376+
if orig := typ.obj; orig.pkg != nil && orig.parent != orig.pkg.scope {
377+
fmt.Fprintf(buf, "@%q", orig.pos)
357378
}
358-
// TODO(gri): function-local named types should be displayed
359-
// differently from named types at package level to avoid
360-
// ambiguity.
361-
s = obj.name
362379
}
363-
buf.WriteString(s)
364380
}
365381

366382
func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {

test/typeparam/nested.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// run -gcflags=all="-d=unified -G"
2+
3+
// Copyright 2021 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// This test case stress tests a number of subtle cases involving
8+
// nested type-parameterized declarations. At a high-level, it
9+
// declares a generic function that contains a generic type
10+
// declaration:
11+
//
12+
// func F[A intish]() {
13+
// type T[B intish] struct{}
14+
//
15+
// // store reflect.Type tuple (A, B, F[A].T[B]) in tests
16+
// }
17+
//
18+
// It then instantiates this function with a variety of type arguments
19+
// for A and B. Particularly tricky things like shadowed types.
20+
//
21+
// From this data it tests two things:
22+
//
23+
// 1. Given tuples (A, B, F[A].T[B]) and (A', B', F[A'].T[B']),
24+
// F[A].T[B] should be identical to F[A'].T[B'] iff (A, B) is
25+
// identical to (A', B').
26+
//
27+
// 2. A few of the instantiations are constructed to be identical, and
28+
// it tests that exactly these pairs are duplicated (by golden
29+
// output comparison to nested.out).
30+
//
31+
// In both cases, we're effectively using the compiler's existing
32+
// runtime.Type handling (which is well tested) of type identity of A
33+
// and B as a way to help bootstrap testing and validate its new
34+
// runtime.Type handling of F[A].T[B].
35+
//
36+
// This isn't perfect, but it smoked out a handful of issues in
37+
// gotypes2 and unified IR.
38+
39+
package main
40+
41+
import (
42+
"fmt"
43+
"reflect"
44+
)
45+
46+
type test struct {
47+
TArgs [2]reflect.Type
48+
Instance reflect.Type
49+
}
50+
51+
var tests []test
52+
53+
type intish interface{ ~int }
54+
55+
type Int int
56+
type GlobalInt = Int // allow access to global Int, even when shadowed
57+
58+
func F[A intish]() {
59+
add := func(B, T interface{}) {
60+
tests = append(tests, test{
61+
TArgs: [2]reflect.Type{
62+
reflect.TypeOf(A(0)),
63+
reflect.TypeOf(B),
64+
},
65+
Instance: reflect.TypeOf(T),
66+
})
67+
}
68+
69+
type Int int
70+
71+
type T[B intish] struct{}
72+
73+
add(int(0), T[int]{})
74+
add(Int(0), T[Int]{})
75+
add(GlobalInt(0), T[GlobalInt]{})
76+
add(A(0), T[A]{}) // NOTE: intentionally dups with int and GlobalInt
77+
78+
type U[_ any] int
79+
type V U[int]
80+
type W V
81+
82+
add(U[int](0), T[U[int]]{})
83+
add(U[Int](0), T[U[Int]]{})
84+
add(U[GlobalInt](0), T[U[GlobalInt]]{})
85+
add(U[A](0), T[U[A]]{}) // NOTE: intentionally dups with U[int] and U[GlobalInt]
86+
add(V(0), T[V]{})
87+
add(W(0), T[W]{})
88+
}
89+
90+
func main() {
91+
type Int int
92+
93+
F[int]()
94+
F[Int]()
95+
F[GlobalInt]()
96+
97+
type U[_ any] int
98+
type V U[int]
99+
type W V
100+
101+
F[U[int]]()
102+
F[U[Int]]()
103+
F[U[GlobalInt]]()
104+
F[V]()
105+
F[W]()
106+
107+
type X[A any] U[X[A]]
108+
109+
F[X[int]]()
110+
F[X[Int]]()
111+
F[X[GlobalInt]]()
112+
113+
for j, tj := range tests {
114+
for i, ti := range tests[:j+1] {
115+
if (ti.TArgs == tj.TArgs) != (ti.Instance == tj.Instance) {
116+
fmt.Printf("FAIL: %d,%d: %s, but %s\n", i, j, eq(ti.TArgs, tj.TArgs), eq(ti.Instance, tj.Instance))
117+
}
118+
119+
// The test is constructed so we should see a few identical types.
120+
// See "NOTE" comments above.
121+
if i != j && ti.Instance == tj.Instance {
122+
fmt.Printf("%d,%d: %v\n", i, j, ti.Instance)
123+
}
124+
}
125+
}
126+
}
127+
128+
func eq(a, b interface{}) string {
129+
op := "=="
130+
if a != b {
131+
op = "!="
132+
}
133+
return fmt.Sprintf("%v %s %v", a, op, b)
134+
}

test/typeparam/nested.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
0,3: main.T·2[int;int]
2+
4,7: main.T·2[int;"".U·3[int;int]]
3+
22,23: main.T·2["".Int;"".Int]
4+
26,27: main.T·2["".Int;"".U·3["".Int;"".Int]]

0 commit comments

Comments
 (0)