Skip to content

Commit bea5513

Browse files
griesemergopherbot
authored andcommitted
go/types, types2: introduce _Alias type node
This change introduces a new (unexported for now) _Alias type node which serves as an explicit representation for alias types in type alias declarations: type A = T The _Alias node stands for the type A in this declaration, with the _Alias' actual type pointing to (the type node for) T. Without the _Alias node, (the object for) A points directly to T. Explicit _Alias nodes permit better error messages (they mention A instead of T if the type in the source was named A) and can help with certain type cycle problems. They are also needed to hold type parameters for alias types, eventually. Because an _Alias node is simply an alternative representation for an aliased type, code that makes type-specific choices must always look at the actual (unaliased) type denoted by a type alias. The new function func _Unalias(t Type) Type performs the necessary indirection. Type switches that consider type nodes, must either switch on _Unalias(typ) or handle the _Alias case. To avoid breaking clients, _Alias nodes must be enabled explicitly, through the new Config flag _EnableAlias. To run tests with the _EnableAlias set, use the new -alias flag as in "go test -run short -alias". The testing harness understands the flag as well and it may be used to enable/disable _Alias nodes on a per-file basis, with a comment ("// -alias" or // -alias=false) on the first line in those files. The file-based flag overrides the command-line flag. The use of _Alias nodes is disabled by default and must be enabled by setting _EnableAlias. Passes type checker tests with and without -alias flag set. For #25838. For #44410. For #46477. Change-Id: I78e178a1aef4d7f325088c0c6cbae4cfb1e5fb5c Reviewed-on: https://go-review.googlesource.com/c/go/+/521956 Reviewed-by: Matthew Dempsky <[email protected]> Reviewed-by: Robert Findley <[email protected]> Auto-Submit: Robert Griesemer <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Robert Griesemer <[email protected]>
1 parent 1b03ec8 commit bea5513

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+719
-127
lines changed

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

+68-5
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,77 @@
44

55
package types2
66

7-
// This file will eventually define an Alias type.
8-
// For now it declares asNamed. Once Alias types
9-
// exist, asNamed will need to indirect through
10-
// them as needed.
7+
import "fmt"
8+
9+
// Names starting with a _ are intended to be exported eventually
10+
// (go.dev/issue/63223).
11+
12+
// An _Alias represents an alias type.
13+
type _Alias struct {
14+
obj *TypeName // corresponding declared alias object
15+
fromRHS Type // RHS of type alias declaration; may be an alias
16+
actual Type // actual (aliased) type; never an alias
17+
}
18+
19+
// _NewAlias creates a new Alias type with the given type name and rhs.
20+
// rhs must not be nil.
21+
func _NewAlias(obj *TypeName, rhs Type) *_Alias {
22+
return (*Checker)(nil).newAlias(obj, rhs)
23+
}
24+
25+
func (a *_Alias) Underlying() Type { return a.actual.Underlying() }
26+
func (a *_Alias) String() string { return TypeString(a, nil) }
27+
28+
// Type accessors
29+
30+
// _Unalias returns t if it is not an alias type;
31+
// otherwise it follows t's alias chain until it
32+
// reaches a non-alias type which is then returned.
33+
// Consequently, the result is never an alias type.
34+
func _Unalias(t Type) Type {
35+
if a0, _ := t.(*_Alias); a0 != nil {
36+
if a0.actual != nil {
37+
return a0.actual
38+
}
39+
for a := a0; ; {
40+
t = a.fromRHS
41+
a, _ = t.(*_Alias)
42+
if a == nil {
43+
break
44+
}
45+
}
46+
if t == nil {
47+
panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
48+
}
49+
a0.actual = t
50+
}
51+
return t
52+
}
1153

1254
// asNamed returns t as *Named if that is t's
1355
// actual type. It returns nil otherwise.
1456
func asNamed(t Type) *Named {
15-
n, _ := t.(*Named)
57+
n, _ := _Unalias(t).(*Named)
1658
return n
1759
}
60+
61+
// newAlias creates a new Alias type with the given type name and rhs.
62+
// rhs must not be nil.
63+
func (check *Checker) newAlias(obj *TypeName, rhs Type) *_Alias {
64+
assert(rhs != nil)
65+
a := &_Alias{obj, rhs, nil}
66+
if obj.typ == nil {
67+
obj.typ = a
68+
}
69+
70+
// Ensure that a.actual is set at the end of type checking.
71+
if check != nil {
72+
check.needsCleanup(a)
73+
}
74+
75+
return a
76+
}
77+
78+
func (a *_Alias) cleanup() {
79+
_Unalias(a)
80+
}

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

+5
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ type Config struct {
170170
// for unused imports.
171171
DisableUnusedImportCheck bool
172172

173+
// If EnableAlias is set, alias declarations produce an _Alias type.
174+
// Otherwise the alias information is only in the type name, which
175+
// points directly to the actual (aliased) type.
176+
_EnableAlias bool
177+
173178
// If a non-empty ErrorURL format string is provided, it is used
174179
// to format an error URL link that is appended to the first line
175180
// of an error message. ErrorURL must be a format string containing

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,14 @@ func (check *Checker) addDeclDep(to Object) {
152152
from.addDep(to)
153153
}
154154

155+
// Note: The following three alias-related functions are only used
156+
// when _Alias types are not enabled.
157+
155158
// brokenAlias records that alias doesn't have a determined type yet.
156159
// It also sets alias.typ to Typ[Invalid].
160+
// Not used if check.conf._EnableAlias is set.
157161
func (check *Checker) brokenAlias(alias *TypeName) {
162+
assert(!check.conf._EnableAlias)
158163
if check.brokenAliases == nil {
159164
check.brokenAliases = make(map[*TypeName]bool)
160165
}
@@ -164,13 +169,15 @@ func (check *Checker) brokenAlias(alias *TypeName) {
164169

165170
// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
166171
func (check *Checker) validAlias(alias *TypeName, typ Type) {
172+
assert(!check.conf._EnableAlias)
167173
delete(check.brokenAliases, alias)
168174
alias.typ = typ
169175
}
170176

171177
// isBrokenAlias reports whether alias doesn't have a determined type yet.
172178
func (check *Checker) isBrokenAlias(alias *TypeName) bool {
173-
return !isValid(alias.typ) && check.brokenAliases[alias]
179+
assert(!check.conf._EnableAlias)
180+
return check.brokenAliases[alias]
174181
}
175182

176183
func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {

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

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import (
5151
var (
5252
haltOnError = flag.Bool("halt", false, "halt on error")
5353
verifyErrors = flag.Bool("verify", false, "verify errors (rather than list them) in TestManual")
54+
enableAlias = flag.Bool("alias", false, "set Config._EnableAlias for tests")
5455
)
5556

5657
func parseFiles(t *testing.T, filenames []string, srcs [][]byte, mode syntax.Mode) ([]*syntax.File, []error) {
@@ -130,6 +131,7 @@ func testFiles(t *testing.T, filenames []string, srcs [][]byte, colDelta uint, m
130131
flags.StringVar(&conf.GoVersion, "lang", "", "")
131132
flags.StringVar(&goexperiment, "goexperiment", "", "")
132133
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
134+
flags.BoolVar(boolFieldAddr(&conf, "_EnableAlias"), "alias", *enableAlias, "")
133135
if err := parseFlags(srcs[0], flags); err != nil {
134136
t.Fatal(err)
135137
}

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

+31-11
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,14 @@ loop:
251251
// the syntactic information. We should consider storing
252252
// this information explicitly in the object.
253253
var alias bool
254-
if d := check.objMap[obj]; d != nil {
255-
alias = d.tdecl.Alias // package-level object
254+
if check.conf._EnableAlias {
255+
alias = obj.IsAlias()
256256
} else {
257-
alias = obj.IsAlias() // function local object
257+
if d := check.objMap[obj]; d != nil {
258+
alias = d.tdecl.Alias // package-level object
259+
} else {
260+
alias = obj.IsAlias() // function local object
261+
}
258262
}
259263
if !alias {
260264
ndef++
@@ -322,7 +326,11 @@ func (check *Checker) cycleError(cycle []Object) {
322326
// If obj is a type alias, mark it as valid (not broken) in order to avoid follow-on errors.
323327
tname, _ := obj.(*TypeName)
324328
if tname != nil && tname.IsAlias() {
325-
check.validAlias(tname, Typ[Invalid])
329+
// If we use Alias nodes, it is initialized with Typ[Invalid].
330+
// TODO(gri) Adjust this code if we initialize with nil.
331+
if !check.conf._EnableAlias {
332+
check.validAlias(tname, Typ[Invalid])
333+
}
326334
}
327335

328336
// report a more concise error for self references
@@ -495,20 +503,32 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *TypeN
495503
_ = check.isImportedConstraint(rhs) && check.verifyVersionf(tdecl.Type, go1_18, "using type constraint %s", rhs)
496504
}).describef(obj, "validType(%s)", obj.Name())
497505

498-
alias := tdecl.Alias
499-
if alias && tdecl.TParamList != nil {
506+
aliasDecl := tdecl.Alias
507+
if aliasDecl && tdecl.TParamList != nil {
500508
// The parser will ensure this but we may still get an invalid AST.
501509
// Complain and continue as regular type definition.
502510
check.error(tdecl, BadDecl, "generic type cannot be alias")
503-
alias = false
511+
aliasDecl = false
504512
}
505513

506514
// alias declaration
507-
if alias {
515+
if aliasDecl {
508516
check.verifyVersionf(tdecl, go1_9, "type aliases")
509-
check.brokenAlias(obj)
510-
rhs = check.typ(tdecl.Type)
511-
check.validAlias(obj, rhs)
517+
if check.conf._EnableAlias {
518+
// TODO(gri) Should be able to use nil instead of Typ[Invalid] to mark
519+
// the alias as incomplete. Currently this causes problems
520+
// with certain cycles. Investigate.
521+
alias := check.newAlias(obj, Typ[Invalid])
522+
setDefType(def, alias)
523+
rhs = check.definedType(tdecl.Type, obj)
524+
assert(rhs != nil)
525+
alias.fromRHS = rhs
526+
_Unalias(alias) // resolve alias.actual
527+
} else {
528+
check.brokenAlias(obj)
529+
rhs = check.typ(tdecl.Type)
530+
check.validAlias(obj, rhs)
531+
}
512532
return
513533
}
514534

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

+6
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,9 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
542542
case *Basic:
543543
// nothing to do
544544

545+
case *_Alias:
546+
return w.isParameterized(_Unalias(t))
547+
545548
case *Array:
546549
return w.isParameterized(t.elem)
547550

@@ -693,6 +696,9 @@ func (w *cycleFinder) typ(typ Type) {
693696
case *Basic:
694697
// nothing to do
695698

699+
case *_Alias:
700+
w.typ(_Unalias(t))
701+
696702
case *Array:
697703
w.typ(t.elem)
698704

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
152152
// use named receiver type if available (for better error messages)
153153
var recvTyp Type = ityp
154154
if def != nil {
155-
if named, _ := def.typ.(*Named); named != nil {
155+
if named := asNamed(def.typ); named != nil {
156156
recvTyp = named
157157
}
158158
}

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

+24
Original file line numberDiff line numberDiff line change
@@ -980,3 +980,27 @@ func f[I *T, T any]() {
980980
t.Fatalf("types of v and T are not pointer-identical: %p != %p", v.Type().(*TypeParam), T)
981981
}
982982
}
983+
984+
func TestIssue44410(t *testing.T) {
985+
const src = `
986+
package p
987+
988+
type A = []int
989+
type S struct{ A }
990+
`
991+
992+
var conf Config
993+
*boolFieldAddr(&conf, "_EnableAlias") = true
994+
pkg := mustTypecheck(src, &conf, nil)
995+
996+
S := pkg.Scope().Lookup("S")
997+
if S == nil {
998+
t.Fatal("object S not found")
999+
}
1000+
1001+
got := S.String()
1002+
const want = "type p.S struct{p.A /* = []int */}"
1003+
if got != want {
1004+
t.Fatalf("got %q; want %q", got, want)
1005+
}
1006+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ func (check *Checker) newAssertableTo(pos syntax.Pos, V, T Type, cause *string)
527527
// with an underlying pointer type!) and returns its base and true.
528528
// Otherwise it returns (typ, false).
529529
func deref(typ Type) (Type, bool) {
530-
if p, _ := typ.(*Pointer); p != nil {
530+
if p, _ := _Unalias(typ).(*Pointer); p != nil {
531531
// p.base should never be nil, but be conservative
532532
if p.base == nil {
533533
if debug {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func (w *monoGraph) assign(pkg *Package, pos syntax.Pos, tpar *TypeParam, targ T
208208
// type parameters.
209209
var do func(typ Type)
210210
do = func(typ Type) {
211-
switch typ := typ.(type) {
211+
switch typ := _Unalias(typ).(type) {
212212
default:
213213
panic("unexpected type")
214214

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,8 @@ func (t *Named) AddMethod(m *Func) {
453453
}
454454
}
455455

456-
func (t *Named) Underlying() Type { return t.resolve().underlying }
456+
// TODO(gri) Investigate if _Unalias can be moved to where underlying is set.
457+
func (t *Named) Underlying() Type { return _Unalias(t.resolve().underlying) }
457458
func (t *Named) String() string { return TypeString(t, nil) }
458459

459460
// ----------------------------------------------------------------------------

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

+2
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ func (obj *TypeName) IsAlias() bool {
285285
switch t := obj.typ.(type) {
286286
case nil:
287287
return false
288+
// case *_Alias:
289+
// handled by default case
288290
case *Basic:
289291
// unsafe.Pointer is not an alias.
290292
if obj.pkg == Unsafe {

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

+14-9
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
package types2
88

99
// isValid reports whether t is a valid type.
10-
func isValid(t Type) bool { return t != Typ[Invalid] }
10+
func isValid(t Type) bool { return _Unalias(t) != Typ[Invalid] }
1111

1212
// The isX predicates below report whether t is an X.
1313
// If t is a type parameter the result is false; i.e.,
@@ -50,7 +50,7 @@ func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) }
5050
// for all specific types of the type parameter's type set.
5151
// allBasic(t, info) is an optimized version of isBasic(coreType(t), info).
5252
func allBasic(t Type, info BasicInfo) bool {
53-
if tpar, _ := t.(*TypeParam); tpar != nil {
53+
if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil {
5454
return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
5555
}
5656
return isBasic(t, info)
@@ -60,7 +60,7 @@ func allBasic(t Type, info BasicInfo) bool {
6060
// predeclared types, defined types, and type parameters.
6161
// hasName may be called with types that are not fully set up.
6262
func hasName(t Type) bool {
63-
switch t.(type) {
63+
switch _Unalias(t).(type) {
6464
case *Basic, *Named, *TypeParam:
6565
return true
6666
}
@@ -71,7 +71,7 @@ func hasName(t Type) bool {
7171
// This includes all non-defined types, but also basic types.
7272
// isTypeLit may be called with types that are not fully set up.
7373
func isTypeLit(t Type) bool {
74-
switch t.(type) {
74+
switch _Unalias(t).(type) {
7575
case *Named, *TypeParam:
7676
return false
7777
}
@@ -82,8 +82,10 @@ func isTypeLit(t Type) bool {
8282
// constant or boolean. isTyped may be called with types that
8383
// are not fully set up.
8484
func isTyped(t Type) bool {
85-
// isTyped is called with types that are not fully
86-
// set up. Must not call under()!
85+
// Alias or Named types cannot denote untyped types,
86+
// thus we don't need to call _Unalias or under
87+
// (which would be unsafe to do for types that are
88+
// not fully set up).
8789
b, _ := t.(*Basic)
8890
return b == nil || b.info&IsUntyped == 0
8991
}
@@ -106,7 +108,7 @@ func isNonTypeParamInterface(t Type) bool {
106108

107109
// isTypeParam reports whether t is a type parameter.
108110
func isTypeParam(t Type) bool {
109-
_, ok := t.(*TypeParam)
111+
_, ok := _Unalias(t).(*TypeParam)
110112
return ok
111113
}
112114

@@ -115,7 +117,7 @@ func isTypeParam(t Type) bool {
115117
// use anywhere, but it may report a false negative if the type set has not been
116118
// computed yet.
117119
func hasEmptyTypeset(t Type) bool {
118-
if tpar, _ := t.(*TypeParam); tpar != nil && tpar.bound != nil {
120+
if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil && tpar.bound != nil {
119121
iface, _ := safeUnderlying(tpar.bound).(*Interface)
120122
return iface != nil && iface.tset != nil && iface.tset.IsEmpty()
121123
}
@@ -221,6 +223,9 @@ type comparer struct {
221223

222224
// For changes to this code the corresponding changes should be made to unifier.nify.
223225
func (c *comparer) identical(x, y Type, p *ifacePair) bool {
226+
x = _Unalias(x)
227+
y = _Unalias(y)
228+
224229
if x == y {
225230
return true
226231
}
@@ -495,7 +500,7 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool
495500
// it returns the incoming type for all other types. The default type
496501
// for untyped nil is untyped nil.
497502
func Default(t Type) Type {
498-
if t, ok := t.(*Basic); ok {
503+
if t, ok := _Unalias(t).(*Basic); ok {
499504
switch t.kind {
500505
case UntypedBool:
501506
return Typ[Bool]

0 commit comments

Comments
 (0)