Skip to content

Commit fe85c24

Browse files
committed
go/types, types2: report an error when using a broken alias
The type checker doesn't have a general mechanism to "use" the type of a type alias whose type depends on a recursive type declaration which is not yet completely type-checked. In some cases, the type of a type alias is needed before it is determined; the type is incorrect (invalid) in that case but no error is reported. The type-checker is happy with this (incorrect type), but the compiler may crash under some circumstances. A correct fix will likely require some form of forwarding type which is a fairly pervasive change and may also affect the type checker API. This CL introduces a simple side table, a map of broken type aliases, which is consulted before the type associated with a type alias is used. If the type alias is broken, an error is reported. This is a stop-gap solution that prevents the compiler from crashing. The reported error refers to the corresponding issue which suggests a work-around that may be applicable in some cases. Also fix a minor error related to type cycles: If we have a cycle that doesn't start with a type, don't use a compiler error message that explicitly mentions "type". Fixes #50259. Fixes #50276. Fixes #50779. For #50729. Change-Id: Ie8e38f49ef724e742e8e78625e6d4f3d4014a52c Reviewed-on: https://go-review.googlesource.com/c/go/+/379916 Trust: Robert Griesemer <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent fef14fd commit fe85c24

File tree

15 files changed

+251
-10
lines changed

15 files changed

+251
-10
lines changed

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

+23
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ type Checker struct {
130130
imports []*PkgName // list of imported packages
131131
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
132132
recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type
133+
brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types
133134
unionTypeSets map[*Union]*_TypeSet // computed type sets for union types
134135
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
135136

@@ -160,6 +161,27 @@ func (check *Checker) addDeclDep(to Object) {
160161
from.addDep(to)
161162
}
162163

164+
// brokenAlias records that alias doesn't have a determined type yet.
165+
// It also sets alias.typ to Typ[Invalid].
166+
func (check *Checker) brokenAlias(alias *TypeName) {
167+
if check.brokenAliases == nil {
168+
check.brokenAliases = make(map[*TypeName]bool)
169+
}
170+
check.brokenAliases[alias] = true
171+
alias.typ = Typ[Invalid]
172+
}
173+
174+
// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
175+
func (check *Checker) validAlias(alias *TypeName, typ Type) {
176+
delete(check.brokenAliases, alias)
177+
alias.typ = typ
178+
}
179+
180+
// isBrokenAlias reports whether alias doesn't have a determined type yet.
181+
func (check *Checker) isBrokenAlias(alias *TypeName) bool {
182+
return alias.typ == Typ[Invalid] && check.brokenAliases[alias]
183+
}
184+
163185
func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
164186
m := check.untyped
165187
if m == nil {
@@ -333,6 +355,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
333355
check.pkgPathMap = nil
334356
check.seenPkgMap = nil
335357
check.recvTParamMap = nil
358+
check.brokenAliases = nil
336359
check.unionTypeSets = nil
337360
check.defTypes = nil
338361
check.ctxt = nil

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

+8-3
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,13 @@ func (check *Checker) cycleError(cycle []Object) {
314314
// cycle? That would be more consistent with other error messages.
315315
i := firstInSrc(cycle)
316316
obj := cycle[i]
317+
// If obj is a type alias, mark it as valid (not broken) in order to avoid follow-on errors.
318+
tname, _ := obj.(*TypeName)
319+
if tname != nil && tname.IsAlias() {
320+
check.validAlias(tname, Typ[Invalid])
321+
}
317322
var err error_
318-
if check.conf.CompilerErrorMessages {
323+
if tname != nil && check.conf.CompilerErrorMessages {
319324
err.errorf(obj, "invalid recursive type %s", obj.Name())
320325
} else {
321326
err.errorf(obj, "illegal cycle in declaration of %s", obj.Name())
@@ -502,9 +507,9 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
502507
check.versionErrorf(tdecl, "go1.9", "type aliases")
503508
}
504509

505-
obj.typ = Typ[Invalid]
510+
check.brokenAlias(obj)
506511
rhs = check.varType(tdecl.Type)
507-
obj.typ = rhs
512+
check.validAlias(obj, rhs)
508513
return
509514
}
510515

src/cmd/compile/internal/types2/testdata/check/cycles5.src

+2-2
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ type (
135135
type (
136136
a struct{ *b }
137137
b = c
138-
c struct{ *b }
138+
c struct{ *b /* ERROR invalid use of type alias */ }
139139
)
140140

141141
// issue #24939
@@ -145,7 +145,7 @@ type (
145145
}
146146

147147
M interface {
148-
F() P
148+
F() P // ERROR invalid use of type alias
149149
}
150150

151151
P = interface {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
var x T[B]
8+
9+
type T[_ any] struct{}
10+
type A T[B /* ERROR invalid use of type alias */ ]
11+
type B = T[A]
12+
13+
// test case from issue
14+
15+
var v Box[Step]
16+
type Box[T any] struct{}
17+
type Step = Box[StepBox]
18+
type StepBox Box[Step /* ERROR invalid use of type alias */ ]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
// simplified test case
8+
9+
type transform[T any] struct{}
10+
type pair[S any] struct {}
11+
12+
var _ transform[step]
13+
14+
type box transform[step /* ERROR invalid use of type alias */ ]
15+
type step = pair[box]
16+
17+
// test case from issue
18+
19+
type Transform[T any] struct{ hold T }
20+
type Pair[S, T any] struct {
21+
First S
22+
Second T
23+
}
24+
25+
var first Transform[Step]
26+
27+
// This line doesn't use the Step alias, and it compiles fine if you uncomment it.
28+
var second Transform[Pair[Box, interface{}]]
29+
30+
type Box *Transform[Step /* ERROR invalid use of type alias */ ]
31+
32+
// This line is the same as the `first` line, but it comes after the Box declaration and
33+
// does not break the compile.
34+
var third Transform[Step]
35+
36+
type Step = Pair[Box, interface{}]
37+
38+
// This line also does not break the compile
39+
var fourth Transform[Step]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
type AC interface {
8+
C
9+
}
10+
11+
type ST []int
12+
13+
type R[S any, P any] struct{}
14+
15+
type SR = R[SS, ST]
16+
17+
type SS interface {
18+
NSR(any) *SR // ERROR invalid use of type alias SR in recursive type
19+
}
20+
21+
type C interface {
22+
NSR(any) *SR
23+
}

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

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo
9999
x.mode = constant_
100100

101101
case *TypeName:
102+
if check.isBrokenAlias(obj) {
103+
check.errorf(e, "invalid use of type alias %s in recursive type (see issue #50729)", obj.name)
104+
return
105+
}
102106
x.mode = typexpr
103107

104108
case *Var:

src/go/types/check.go

+23
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ type Checker struct {
137137
imports []*PkgName // list of imported packages
138138
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
139139
recvTParamMap map[*ast.Ident]*TypeParam // maps blank receiver type parameters to their type
140+
brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types
140141
unionTypeSets map[*Union]*_TypeSet // computed type sets for union types
141142
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
142143

@@ -167,6 +168,27 @@ func (check *Checker) addDeclDep(to Object) {
167168
from.addDep(to)
168169
}
169170

171+
// brokenAlias records that alias doesn't have a determined type yet.
172+
// It also sets alias.typ to Typ[Invalid].
173+
func (check *Checker) brokenAlias(alias *TypeName) {
174+
if check.brokenAliases == nil {
175+
check.brokenAliases = make(map[*TypeName]bool)
176+
}
177+
check.brokenAliases[alias] = true
178+
alias.typ = Typ[Invalid]
179+
}
180+
181+
// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
182+
func (check *Checker) validAlias(alias *TypeName, typ Type) {
183+
delete(check.brokenAliases, alias)
184+
alias.typ = typ
185+
}
186+
187+
// isBrokenAlias reports whether alias doesn't have a determined type yet.
188+
func (check *Checker) isBrokenAlias(alias *TypeName) bool {
189+
return alias.typ == Typ[Invalid] && check.brokenAliases[alias]
190+
}
191+
170192
func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
171193
m := check.untyped
172194
if m == nil {
@@ -326,6 +348,7 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
326348
check.pkgPathMap = nil
327349
check.seenPkgMap = nil
328350
check.recvTParamMap = nil
351+
check.brokenAliases = nil
329352
check.unionTypeSets = nil
330353
check.defTypes = nil
331354
check.ctxt = nil

src/go/types/decl.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,16 @@ func (check *Checker) cycleError(cycle []Object) {
313313
// cycle? That would be more consistent with other error messages.
314314
i := firstInSrc(cycle)
315315
obj := cycle[i]
316-
check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
316+
// If obj is a type alias, mark it as valid (not broken) in order to avoid follow-on errors.
317+
tname, _ := obj.(*TypeName)
318+
if tname != nil && tname.IsAlias() {
319+
check.validAlias(tname, Typ[Invalid])
320+
}
321+
if tname != nil && compilerErrorMessages {
322+
check.errorf(obj, _InvalidDeclCycle, "invalid recursive type %s", obj.Name())
323+
} else {
324+
check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
325+
}
317326
for range cycle {
318327
check.errorf(obj, _InvalidDeclCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
319328
i++
@@ -555,9 +564,9 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
555564
check.errorf(atPos(tdecl.Assign), _BadDecl, "type aliases requires go1.9 or later")
556565
}
557566

558-
obj.typ = Typ[Invalid]
567+
check.brokenAlias(obj)
559568
rhs = check.varType(tdecl.Type)
560-
obj.typ = rhs
569+
check.validAlias(obj, rhs)
561570
return
562571
}
563572

src/go/types/testdata/check/cycles5.src

+2-2
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ type (
135135
type (
136136
a struct{ *b }
137137
b = c
138-
c struct{ *b }
138+
c struct{ *b /* ERROR invalid use of type alias */ }
139139
)
140140

141141
// issue #24939
@@ -145,7 +145,7 @@ type (
145145
}
146146

147147
M interface {
148-
F() P
148+
F() P // ERROR invalid use of type alias
149149
}
150150

151151
P = interface {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
var x T[B]
8+
9+
type T[_ any] struct{}
10+
type A T[B /* ERROR invalid use of type alias */ ]
11+
type B = T[A]
12+
13+
// test case from issue
14+
15+
var v Box[Step]
16+
type Box[T any] struct{}
17+
type Step = Box[StepBox]
18+
type StepBox Box[Step /* ERROR invalid use of type alias */ ]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
// simplified test case
8+
9+
type transform[T any] struct{}
10+
type pair[S any] struct {}
11+
12+
var _ transform[step]
13+
14+
type box transform[step /* ERROR invalid use of type alias */ ]
15+
type step = pair[box]
16+
17+
// test case from issue
18+
19+
type Transform[T any] struct{ hold T }
20+
type Pair[S, T any] struct {
21+
First S
22+
Second T
23+
}
24+
25+
var first Transform[Step]
26+
27+
// This line doesn't use the Step alias, and it compiles fine if you uncomment it.
28+
var second Transform[Pair[Box, interface{}]]
29+
30+
type Box *Transform[Step /* ERROR invalid use of type alias */ ]
31+
32+
// This line is the same as the `first` line, but it comes after the Box declaration and
33+
// does not break the compile.
34+
var third Transform[Step]
35+
36+
type Step = Pair[Box, interface{}]
37+
38+
// This line also does not break the compile
39+
var fourth Transform[Step]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
type AC interface {
8+
C
9+
}
10+
11+
type ST []int
12+
13+
type R[S any, P any] struct{}
14+
15+
type SR = R[SS, ST]
16+
17+
type SS interface {
18+
NSR(any) *SR // ERROR invalid use of type alias SR in recursive type
19+
}
20+
21+
type C interface {
22+
NSR(any) *SR
23+
}

src/go/types/typexpr.go

+4
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
9696
x.mode = constant_
9797

9898
case *TypeName:
99+
if check.isBrokenAlias(obj) {
100+
check.errorf(e, _InvalidDeclCycle, "invalid use of type alias %s in recursive type (see issue #50729)", obj.name)
101+
return
102+
}
99103
x.mode = typexpr
100104

101105
case *Var:

test/typeparam/issue50259.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// errorcheck -G=3
2+
3+
// Copyright 2022 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+
package p
8+
9+
var x T[B]
10+
11+
type T[_ any] struct{}
12+
type A T[B] // ERROR "invalid use of type alias B in recursive type"
13+
type B = T[A]

0 commit comments

Comments
 (0)