imports []*PkgName // list of imported packages
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type
+ brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types
unionTypeSets map[*Union]*_TypeSet // computed type sets for union types
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
from.addDep(to)
}
+// brokenAlias records that alias doesn't have a determined type yet.
+// It also sets alias.typ to Typ[Invalid].
+func (check *Checker) brokenAlias(alias *TypeName) {
+ if check.brokenAliases == nil {
+ check.brokenAliases = make(map[*TypeName]bool)
+ }
+ check.brokenAliases[alias] = true
+ alias.typ = Typ[Invalid]
+}
+
+// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
+func (check *Checker) validAlias(alias *TypeName, typ Type) {
+ delete(check.brokenAliases, alias)
+ alias.typ = typ
+}
+
+// isBrokenAlias reports whether alias doesn't have a determined type yet.
+func (check *Checker) isBrokenAlias(alias *TypeName) bool {
+ return alias.typ == Typ[Invalid] && check.brokenAliases[alias]
+}
+
func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
m := check.untyped
if m == nil {
check.pkgPathMap = nil
check.seenPkgMap = nil
check.recvTParamMap = nil
+ check.brokenAliases = nil
check.unionTypeSets = nil
check.defTypes = nil
check.ctxt = nil
// cycle? That would be more consistent with other error messages.
i := firstInSrc(cycle)
obj := cycle[i]
+ // If obj is a type alias, mark it as valid (not broken) in order to avoid follow-on errors.
+ tname, _ := obj.(*TypeName)
+ if tname != nil && tname.IsAlias() {
+ check.validAlias(tname, Typ[Invalid])
+ }
var err error_
- if check.conf.CompilerErrorMessages {
+ if tname != nil && check.conf.CompilerErrorMessages {
err.errorf(obj, "invalid recursive type %s", obj.Name())
} else {
err.errorf(obj, "illegal cycle in declaration of %s", obj.Name())
check.versionErrorf(tdecl, "go1.9", "type aliases")
}
- obj.typ = Typ[Invalid]
+ check.brokenAlias(obj)
rhs = check.varType(tdecl.Type)
- obj.typ = rhs
+ check.validAlias(obj, rhs)
return
}
type (
a struct{ *b }
b = c
- c struct{ *b }
+ c struct{ *b /* ERROR invalid use of type alias */ }
)
// issue #24939
}
M interface {
- F() P
+ F() P // ERROR invalid use of type alias
}
P = interface {
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var x T[B]
+
+type T[_ any] struct{}
+type A T[B /* ERROR invalid use of type alias */ ]
+type B = T[A]
+
+// test case from issue
+
+var v Box[Step]
+type Box[T any] struct{}
+type Step = Box[StepBox]
+type StepBox Box[Step /* ERROR invalid use of type alias */ ]
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+// simplified test case
+
+type transform[T any] struct{}
+type pair[S any] struct {}
+
+var _ transform[step]
+
+type box transform[step /* ERROR invalid use of type alias */ ]
+type step = pair[box]
+
+// test case from issue
+
+type Transform[T any] struct{ hold T }
+type Pair[S, T any] struct {
+ First S
+ Second T
+}
+
+var first Transform[Step]
+
+// This line doesn't use the Step alias, and it compiles fine if you uncomment it.
+var second Transform[Pair[Box, interface{}]]
+
+type Box *Transform[Step /* ERROR invalid use of type alias */ ]
+
+// This line is the same as the `first` line, but it comes after the Box declaration and
+// does not break the compile.
+var third Transform[Step]
+
+type Step = Pair[Box, interface{}]
+
+// This line also does not break the compile
+var fourth Transform[Step]
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type AC interface {
+ C
+}
+
+type ST []int
+
+type R[S any, P any] struct{}
+
+type SR = R[SS, ST]
+
+type SS interface {
+ NSR(any) *SR // ERROR invalid use of type alias SR in recursive type
+}
+
+type C interface {
+ NSR(any) *SR
+}
x.mode = constant_
case *TypeName:
+ if check.isBrokenAlias(obj) {
+ check.errorf(e, "invalid use of type alias %s in recursive type (see issue #50729)", obj.name)
+ return
+ }
x.mode = typexpr
case *Var:
imports []*PkgName // list of imported packages
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
recvTParamMap map[*ast.Ident]*TypeParam // maps blank receiver type parameters to their type
+ brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types
unionTypeSets map[*Union]*_TypeSet // computed type sets for union types
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
from.addDep(to)
}
+// brokenAlias records that alias doesn't have a determined type yet.
+// It also sets alias.typ to Typ[Invalid].
+func (check *Checker) brokenAlias(alias *TypeName) {
+ if check.brokenAliases == nil {
+ check.brokenAliases = make(map[*TypeName]bool)
+ }
+ check.brokenAliases[alias] = true
+ alias.typ = Typ[Invalid]
+}
+
+// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
+func (check *Checker) validAlias(alias *TypeName, typ Type) {
+ delete(check.brokenAliases, alias)
+ alias.typ = typ
+}
+
+// isBrokenAlias reports whether alias doesn't have a determined type yet.
+func (check *Checker) isBrokenAlias(alias *TypeName) bool {
+ return alias.typ == Typ[Invalid] && check.brokenAliases[alias]
+}
+
func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
m := check.untyped
if m == nil {
check.pkgPathMap = nil
check.seenPkgMap = nil
check.recvTParamMap = nil
+ check.brokenAliases = nil
check.unionTypeSets = nil
check.defTypes = nil
check.ctxt = nil
// cycle? That would be more consistent with other error messages.
i := firstInSrc(cycle)
obj := cycle[i]
- check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
+ // If obj is a type alias, mark it as valid (not broken) in order to avoid follow-on errors.
+ tname, _ := obj.(*TypeName)
+ if tname != nil && tname.IsAlias() {
+ check.validAlias(tname, Typ[Invalid])
+ }
+ if tname != nil && compilerErrorMessages {
+ check.errorf(obj, _InvalidDeclCycle, "invalid recursive type %s", obj.Name())
+ } else {
+ check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
+ }
for range cycle {
check.errorf(obj, _InvalidDeclCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
i++
check.errorf(atPos(tdecl.Assign), _BadDecl, "type aliases requires go1.9 or later")
}
- obj.typ = Typ[Invalid]
+ check.brokenAlias(obj)
rhs = check.varType(tdecl.Type)
- obj.typ = rhs
+ check.validAlias(obj, rhs)
return
}
type (
a struct{ *b }
b = c
- c struct{ *b }
+ c struct{ *b /* ERROR invalid use of type alias */ }
)
// issue #24939
}
M interface {
- F() P
+ F() P // ERROR invalid use of type alias
}
P = interface {
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var x T[B]
+
+type T[_ any] struct{}
+type A T[B /* ERROR invalid use of type alias */ ]
+type B = T[A]
+
+// test case from issue
+
+var v Box[Step]
+type Box[T any] struct{}
+type Step = Box[StepBox]
+type StepBox Box[Step /* ERROR invalid use of type alias */ ]
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+// simplified test case
+
+type transform[T any] struct{}
+type pair[S any] struct {}
+
+var _ transform[step]
+
+type box transform[step /* ERROR invalid use of type alias */ ]
+type step = pair[box]
+
+// test case from issue
+
+type Transform[T any] struct{ hold T }
+type Pair[S, T any] struct {
+ First S
+ Second T
+}
+
+var first Transform[Step]
+
+// This line doesn't use the Step alias, and it compiles fine if you uncomment it.
+var second Transform[Pair[Box, interface{}]]
+
+type Box *Transform[Step /* ERROR invalid use of type alias */ ]
+
+// This line is the same as the `first` line, but it comes after the Box declaration and
+// does not break the compile.
+var third Transform[Step]
+
+type Step = Pair[Box, interface{}]
+
+// This line also does not break the compile
+var fourth Transform[Step]
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type AC interface {
+ C
+}
+
+type ST []int
+
+type R[S any, P any] struct{}
+
+type SR = R[SS, ST]
+
+type SS interface {
+ NSR(any) *SR // ERROR invalid use of type alias SR in recursive type
+}
+
+type C interface {
+ NSR(any) *SR
+}
x.mode = constant_
case *TypeName:
+ if check.isBrokenAlias(obj) {
+ check.errorf(e, _InvalidDeclCycle, "invalid use of type alias %s in recursive type (see issue #50729)", obj.name)
+ return
+ }
x.mode = typexpr
case *Var:
--- /dev/null
+// errorcheck -G=3
+
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var x T[B]
+
+type T[_ any] struct{}
+type A T[B] // ERROR "invalid use of type alias B in recursive type"
+type B = T[A]