package types2
-// This file will eventually define an Alias type.
-// For now it declares asNamed. Once Alias types
-// exist, asNamed will need to indirect through
-// them as needed.
+import "fmt"
+
+// Names starting with a _ are intended to be exported eventually
+// (go.dev/issue/63223).
+
+// An _Alias represents an alias type.
+type _Alias struct {
+ obj *TypeName // corresponding declared alias object
+ fromRHS Type // RHS of type alias declaration; may be an alias
+ actual Type // actual (aliased) type; never an alias
+}
+
+// _NewAlias creates a new Alias type with the given type name and rhs.
+// rhs must not be nil.
+func _NewAlias(obj *TypeName, rhs Type) *_Alias {
+ return (*Checker)(nil).newAlias(obj, rhs)
+}
+
+func (a *_Alias) Underlying() Type { return a.actual.Underlying() }
+func (a *_Alias) String() string { return TypeString(a, nil) }
+
+// Type accessors
+
+// _Unalias returns t if it is not an alias type;
+// otherwise it follows t's alias chain until it
+// reaches a non-alias type which is then returned.
+// Consequently, the result is never an alias type.
+func _Unalias(t Type) Type {
+ if a0, _ := t.(*_Alias); a0 != nil {
+ if a0.actual != nil {
+ return a0.actual
+ }
+ for a := a0; ; {
+ t = a.fromRHS
+ a, _ = t.(*_Alias)
+ if a == nil {
+ break
+ }
+ }
+ if t == nil {
+ panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
+ }
+ a0.actual = t
+ }
+ return t
+}
// asNamed returns t as *Named if that is t's
// actual type. It returns nil otherwise.
func asNamed(t Type) *Named {
- n, _ := t.(*Named)
+ n, _ := _Unalias(t).(*Named)
return n
}
+
+// newAlias creates a new Alias type with the given type name and rhs.
+// rhs must not be nil.
+func (check *Checker) newAlias(obj *TypeName, rhs Type) *_Alias {
+ assert(rhs != nil)
+ a := &_Alias{obj, rhs, nil}
+ if obj.typ == nil {
+ obj.typ = a
+ }
+
+ // Ensure that a.actual is set at the end of type checking.
+ if check != nil {
+ check.needsCleanup(a)
+ }
+
+ return a
+}
+
+func (a *_Alias) cleanup() {
+ _Unalias(a)
+}
// for unused imports.
DisableUnusedImportCheck bool
+ // If EnableAlias is set, alias declarations produce an _Alias type.
+ // Otherwise the alias information is only in the type name, which
+ // points directly to the actual (aliased) type.
+ _EnableAlias bool
+
// If a non-empty ErrorURL format string is provided, it is used
// to format an error URL link that is appended to the first line
// of an error message. ErrorURL must be a format string containing
from.addDep(to)
}
+// Note: The following three alias-related functions are only used
+// when _Alias types are not enabled.
+
// brokenAlias records that alias doesn't have a determined type yet.
// It also sets alias.typ to Typ[Invalid].
+// Not used if check.conf._EnableAlias is set.
func (check *Checker) brokenAlias(alias *TypeName) {
+ assert(!check.conf._EnableAlias)
if check.brokenAliases == nil {
check.brokenAliases = make(map[*TypeName]bool)
}
// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
func (check *Checker) validAlias(alias *TypeName, typ Type) {
+ assert(!check.conf._EnableAlias)
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 !isValid(alias.typ) && check.brokenAliases[alias]
+ assert(!check.conf._EnableAlias)
+ return check.brokenAliases[alias]
}
func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
var (
haltOnError = flag.Bool("halt", false, "halt on error")
verifyErrors = flag.Bool("verify", false, "verify errors (rather than list them) in TestManual")
+ enableAlias = flag.Bool("alias", false, "set Config._EnableAlias for tests")
)
func parseFiles(t *testing.T, filenames []string, srcs [][]byte, mode syntax.Mode) ([]*syntax.File, []error) {
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.StringVar(&goexperiment, "goexperiment", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
+ flags.BoolVar(boolFieldAddr(&conf, "_EnableAlias"), "alias", *enableAlias, "")
if err := parseFlags(srcs[0], flags); err != nil {
t.Fatal(err)
}
// the syntactic information. We should consider storing
// this information explicitly in the object.
var alias bool
- if d := check.objMap[obj]; d != nil {
- alias = d.tdecl.Alias // package-level object
+ if check.conf._EnableAlias {
+ alias = obj.IsAlias()
} else {
- alias = obj.IsAlias() // function local object
+ if d := check.objMap[obj]; d != nil {
+ alias = d.tdecl.Alias // package-level object
+ } else {
+ alias = obj.IsAlias() // function local object
+ }
}
if !alias {
ndef++
// 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 we use Alias nodes, it is initialized with Typ[Invalid].
+ // TODO(gri) Adjust this code if we initialize with nil.
+ if !check.conf._EnableAlias {
+ check.validAlias(tname, Typ[Invalid])
+ }
}
// report a more concise error for self references
_ = check.isImportedConstraint(rhs) && check.verifyVersionf(tdecl.Type, go1_18, "using type constraint %s", rhs)
}).describef(obj, "validType(%s)", obj.Name())
- alias := tdecl.Alias
- if alias && tdecl.TParamList != nil {
+ aliasDecl := tdecl.Alias
+ if aliasDecl && tdecl.TParamList != nil {
// The parser will ensure this but we may still get an invalid AST.
// Complain and continue as regular type definition.
check.error(tdecl, BadDecl, "generic type cannot be alias")
- alias = false
+ aliasDecl = false
}
// alias declaration
- if alias {
+ if aliasDecl {
check.verifyVersionf(tdecl, go1_9, "type aliases")
- check.brokenAlias(obj)
- rhs = check.typ(tdecl.Type)
- check.validAlias(obj, rhs)
+ if check.conf._EnableAlias {
+ // TODO(gri) Should be able to use nil instead of Typ[Invalid] to mark
+ // the alias as incomplete. Currently this causes problems
+ // with certain cycles. Investigate.
+ alias := check.newAlias(obj, Typ[Invalid])
+ setDefType(def, alias)
+ rhs = check.definedType(tdecl.Type, obj)
+ assert(rhs != nil)
+ alias.fromRHS = rhs
+ _Unalias(alias) // resolve alias.actual
+ } else {
+ check.brokenAlias(obj)
+ rhs = check.typ(tdecl.Type)
+ check.validAlias(obj, rhs)
+ }
return
}
case *Basic:
// nothing to do
+ case *_Alias:
+ return w.isParameterized(_Unalias(t))
+
case *Array:
return w.isParameterized(t.elem)
case *Basic:
// nothing to do
+ case *_Alias:
+ w.typ(_Unalias(t))
+
case *Array:
w.typ(t.elem)
// use named receiver type if available (for better error messages)
var recvTyp Type = ityp
if def != nil {
- if named, _ := def.typ.(*Named); named != nil {
+ if named := asNamed(def.typ); named != nil {
recvTyp = named
}
}
t.Fatalf("types of v and T are not pointer-identical: %p != %p", v.Type().(*TypeParam), T)
}
}
+
+func TestIssue44410(t *testing.T) {
+ const src = `
+package p
+
+type A = []int
+type S struct{ A }
+`
+
+ var conf Config
+ *boolFieldAddr(&conf, "_EnableAlias") = true
+ pkg := mustTypecheck(src, &conf, nil)
+
+ S := pkg.Scope().Lookup("S")
+ if S == nil {
+ t.Fatal("object S not found")
+ }
+
+ got := S.String()
+ const want = "type p.S struct{p.A /* = []int */}"
+ if got != want {
+ t.Fatalf("got %q; want %q", got, want)
+ }
+}
// with an underlying pointer type!) and returns its base and true.
// Otherwise it returns (typ, false).
func deref(typ Type) (Type, bool) {
- if p, _ := typ.(*Pointer); p != nil {
+ if p, _ := _Unalias(typ).(*Pointer); p != nil {
// p.base should never be nil, but be conservative
if p.base == nil {
if debug {
// type parameters.
var do func(typ Type)
do = func(typ Type) {
- switch typ := typ.(type) {
+ switch typ := _Unalias(typ).(type) {
default:
panic("unexpected type")
}
}
-func (t *Named) Underlying() Type { return t.resolve().underlying }
+// TODO(gri) Investigate if _Unalias can be moved to where underlying is set.
+func (t *Named) Underlying() Type { return _Unalias(t.resolve().underlying) }
func (t *Named) String() string { return TypeString(t, nil) }
// ----------------------------------------------------------------------------
switch t := obj.typ.(type) {
case nil:
return false
+ // case *_Alias:
+ // handled by default case
case *Basic:
// unsafe.Pointer is not an alias.
if obj.pkg == Unsafe {
package types2
// isValid reports whether t is a valid type.
-func isValid(t Type) bool { return t != Typ[Invalid] }
+func isValid(t Type) bool { return _Unalias(t) != Typ[Invalid] }
// The isX predicates below report whether t is an X.
// If t is a type parameter the result is false; i.e.,
// for all specific types of the type parameter's type set.
// allBasic(t, info) is an optimized version of isBasic(coreType(t), info).
func allBasic(t Type, info BasicInfo) bool {
- if tpar, _ := t.(*TypeParam); tpar != nil {
+ if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil {
return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
}
return isBasic(t, info)
// predeclared types, defined types, and type parameters.
// hasName may be called with types that are not fully set up.
func hasName(t Type) bool {
- switch t.(type) {
+ switch _Unalias(t).(type) {
case *Basic, *Named, *TypeParam:
return true
}
// This includes all non-defined types, but also basic types.
// isTypeLit may be called with types that are not fully set up.
func isTypeLit(t Type) bool {
- switch t.(type) {
+ switch _Unalias(t).(type) {
case *Named, *TypeParam:
return false
}
// constant or boolean. isTyped may be called with types that
// are not fully set up.
func isTyped(t Type) bool {
- // isTyped is called with types that are not fully
- // set up. Must not call under()!
+ // Alias or Named types cannot denote untyped types,
+ // thus we don't need to call _Unalias or under
+ // (which would be unsafe to do for types that are
+ // not fully set up).
b, _ := t.(*Basic)
return b == nil || b.info&IsUntyped == 0
}
// isTypeParam reports whether t is a type parameter.
func isTypeParam(t Type) bool {
- _, ok := t.(*TypeParam)
+ _, ok := _Unalias(t).(*TypeParam)
return ok
}
// use anywhere, but it may report a false negative if the type set has not been
// computed yet.
func hasEmptyTypeset(t Type) bool {
- if tpar, _ := t.(*TypeParam); tpar != nil && tpar.bound != nil {
+ if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil && tpar.bound != nil {
iface, _ := safeUnderlying(tpar.bound).(*Interface)
return iface != nil && iface.tset != nil && iface.tset.IsEmpty()
}
// For changes to this code the corresponding changes should be made to unifier.nify.
func (c *comparer) identical(x, y Type, p *ifacePair) bool {
+ x = _Unalias(x)
+ y = _Unalias(y)
+
if x == y {
return true
}
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
func Default(t Type) Type {
- if t, ok := t.(*Basic); ok {
+ if t, ok := _Unalias(t).(*Basic); ok {
switch t.kind {
case UntypedBool:
return Typ[Bool]
}
}
- // We process non-alias type declarations first, followed by alias declarations,
- // and then everything else. This appears to avoid most situations where the type
- // of an alias is needed before it is available.
- // There may still be cases where this is not good enough (see also go.dev/issue/25838).
- // In those cases Checker.ident will report an error ("invalid use of type alias").
- var aliasList []*TypeName
- var othersList []Object // everything that's not a type
- // phase 1: non-alias type declarations
- for _, obj := range objList {
- if tname, _ := obj.(*TypeName); tname != nil {
- if check.objMap[tname].tdecl.Alias {
- aliasList = append(aliasList, tname)
+ if check.conf._EnableAlias {
+ // With _Alias nodes we can process declarations in any order.
+ for _, obj := range objList {
+ check.objDecl(obj, nil)
+ }
+ } else {
+ // Without _Alias nodes, we process non-alias type declarations first, followed by
+ // alias declarations, and then everything else. This appears to avoid most situations
+ // where the type of an alias is needed before it is available.
+ // There may still be cases where this is not good enough (see also go.dev/issue/25838).
+ // In those cases Checker.ident will report an error ("invalid use of type alias").
+ var aliasList []*TypeName
+ var othersList []Object // everything that's not a type
+ // phase 1: non-alias type declarations
+ for _, obj := range objList {
+ if tname, _ := obj.(*TypeName); tname != nil {
+ if check.objMap[tname].tdecl.Alias {
+ aliasList = append(aliasList, tname)
+ } else {
+ check.objDecl(obj, nil)
+ }
} else {
- check.objDecl(obj, nil)
+ othersList = append(othersList, obj)
}
- } else {
- othersList = append(othersList, obj)
}
- }
- // phase 2: alias type declarations
- for _, obj := range aliasList {
- check.objDecl(obj, nil)
- }
- // phase 3: all other declarations
- for _, obj := range othersList {
- check.objDecl(obj, nil)
+ // phase 2: alias type declarations
+ for _, obj := range aliasList {
+ check.objDecl(obj, nil)
+ }
+ // phase 3: all other declarations
+ for _, obj := range othersList {
+ check.objDecl(obj, nil)
+ }
}
// At this point we may have a non-empty check.methods map; this means that not all
check.later(func() {
// spec: "The receiver type must be of the form T or *T where T is a type name."
rtyp, _ := deref(recv.typ)
- if !isValid(rtyp) {
+ atyp := _Unalias(rtyp)
+ if !isValid(atyp) {
return // error was reported before
}
// spec: "The type denoted by T is called the receiver base type; it must not
// be a pointer or interface type and it must be declared in the same package
// as the method."
- switch T := rtyp.(type) {
+ switch T := atyp.(type) {
case *Named:
// The receiver type may be an instantiated type referred to
// by an alias (which cannot have receiver parameters for now).
// error messages. This doesn't need to be super-elegant; we just
// need a clear indication that this is not a predeclared name.
if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil {
- w.string(sprintf(nil, false, " /* with %s declared at %s */", t.obj.name, t.obj.Pos()))
+ w.string(fmt.Sprintf(" /* with %s declared at %s */", t.obj.name, t.obj.Pos()))
}
}
+ case *_Alias:
+ w.typeName(t.obj)
+ if w.ctxt != nil {
+ // TODO(gri) do we need to print the alias type name, too?
+ w.typ(_Unalias(t.obj.typ))
+ } else {
+ w.string(fmt.Sprintf(" /* = %s */", _Unalias(t.obj.typ)))
+ }
+
default:
// For externally defined implementations of Type.
// Note: In this case cycles won't be caught.
x.mode = constant_
case *TypeName:
- if check.isBrokenAlias(obj) {
+ if !check.conf._EnableAlias && check.isBrokenAlias(obj) {
check.errorf(e, InvalidDeclCycle, "invalid use of type alias %s in recursive type (see go.dev/issue/50729)", obj.name)
return
}
func setDefType(def *TypeName, typ Type) {
if def != nil {
switch t := def.typ.(type) {
- // case *_Alias:
+ case *_Alias:
+ // t.fromRHS should always be set, either to an invalid type
+ // in the beginning, or to typ in certain cyclic declarations.
+ if t.fromRHS != Typ[Invalid] && t.fromRHS != typ {
+ panic(sprintf(nil, true, "t.fromRHS = %s, typ = %s\n", t.fromRHS, typ))
+ }
+ t.fromRHS = typ
case *Basic:
assert(t == Typ[Invalid])
case *Named:
u.depth--
}()
+ x = _Unalias(x)
+ y = _Unalias(y)
+
// nothing to do if x == y
if x == y {
return true
// (say S->F->S) we have an invalid recursive type. The path list is the full
// path of named types in a cycle, it is only needed for error reporting.
func (check *Checker) validType0(typ Type, nest, path []*Named) bool {
- switch t := typ.(type) {
+ switch t := _Unalias(typ).(type) {
case nil:
// We should never see a nil type but be conservative and panic
// only in debug mode.
package types
-// This file will eventually define an Alias type.
-// For now it declares asNamed. Once Alias types
-// exist, asNamed will need to indirect through
-// them as needed.
+import "fmt"
+
+// Names starting with a _ are intended to be exported eventually
+// (go.dev/issue/63223).
+
+// An _Alias represents an alias type.
+type _Alias struct {
+ obj *TypeName // corresponding declared alias object
+ fromRHS Type // RHS of type alias declaration; may be an alias
+ actual Type // actual (aliased) type; never an alias
+}
+
+// _NewAlias creates a new Alias type with the given type name and rhs.
+// rhs must not be nil.
+func _NewAlias(obj *TypeName, rhs Type) *_Alias {
+ return (*Checker)(nil).newAlias(obj, rhs)
+}
+
+func (a *_Alias) Underlying() Type { return a.actual.Underlying() }
+func (a *_Alias) String() string { return TypeString(a, nil) }
+
+// Type accessors
+
+// _Unalias returns t if it is not an alias type;
+// otherwise it follows t's alias chain until it
+// reaches a non-alias type which is then returned.
+// Consequently, the result is never an alias type.
+func _Unalias(t Type) Type {
+ if a0, _ := t.(*_Alias); a0 != nil {
+ if a0.actual != nil {
+ return a0.actual
+ }
+ for a := a0; ; {
+ t = a.fromRHS
+ a, _ = t.(*_Alias)
+ if a == nil {
+ break
+ }
+ }
+ if t == nil {
+ panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
+ }
+ a0.actual = t
+ }
+ return t
+}
// asNamed returns t as *Named if that is t's
// actual type. It returns nil otherwise.
func asNamed(t Type) *Named {
- n, _ := t.(*Named)
+ n, _ := _Unalias(t).(*Named)
return n
}
+
+// newAlias creates a new Alias type with the given type name and rhs.
+// rhs must not be nil.
+func (check *Checker) newAlias(obj *TypeName, rhs Type) *_Alias {
+ assert(rhs != nil)
+ a := &_Alias{obj, rhs, nil}
+ if obj.typ == nil {
+ obj.typ = a
+ }
+
+ // Ensure that a.actual is set at the end of type checking.
+ if check != nil {
+ check.needsCleanup(a)
+ }
+
+ return a
+}
+
+func (a *_Alias) cleanup() {
+ _Unalias(a)
+}
// for unused imports.
DisableUnusedImportCheck bool
+ // If EnableAlias is set, alias declarations produce an _Alias type.
+ // Otherwise the alias information is only in the type name, which
+ // points directly to the actual (aliased) type.
+ _EnableAlias bool
+
// If a non-empty _ErrorURL format string is provided, it is used
// to format an error URL link that is appended to the first line
// of an error message. ErrorURL must be a format string containing
from.addDep(to)
}
+// Note: The following three alias-related functions are only used
+// when _Alias types are not enabled.
+
// brokenAlias records that alias doesn't have a determined type yet.
// It also sets alias.typ to Typ[Invalid].
+// Not used if check.conf._EnableAlias is set.
func (check *Checker) brokenAlias(alias *TypeName) {
+ assert(!check.conf._EnableAlias)
if check.brokenAliases == nil {
check.brokenAliases = make(map[*TypeName]bool)
}
// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
func (check *Checker) validAlias(alias *TypeName, typ Type) {
+ assert(!check.conf._EnableAlias)
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 !isValid(alias.typ) && check.brokenAliases[alias]
+ assert(!check.conf._EnableAlias)
+ return check.brokenAliases[alias]
}
func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
var (
haltOnError = flag.Bool("halt", false, "halt on error")
verifyErrors = flag.Bool("verify", false, "verify errors (rather than list them) in TestManual")
+ enableAlias = flag.Bool("alias", false, "set Config._EnableAlias for tests")
)
var fset = token.NewFileSet()
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.StringVar(&goexperiment, "goexperiment", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
+ flags.BoolVar(boolFieldAddr(&conf, "_EnableAlias"), "alias", *enableAlias, "")
if err := parseFlags(srcs[0], flags); err != nil {
t.Fatal(err)
}
// the syntactic information. We should consider storing
// this information explicitly in the object.
var alias bool
- if d := check.objMap[obj]; d != nil {
- alias = d.tdecl.Assign.IsValid() // package-level object
+ if check.conf._EnableAlias {
+ alias = obj.IsAlias()
} else {
- alias = obj.IsAlias() // function local object
+ if d := check.objMap[obj]; d != nil {
+ alias = d.tdecl.Assign.IsValid() // package-level object
+ } else {
+ alias = obj.IsAlias() // function local object
+ }
}
if !alias {
ndef++
// 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 we use Alias nodes, it is initialized with Typ[Invalid].
+ // TODO(gri) Adjust this code if we initialize with nil.
+ if !check.conf._EnableAlias {
+ check.validAlias(tname, Typ[Invalid])
+ }
}
// report a more concise error for self references
_ = check.isImportedConstraint(rhs) && check.verifyVersionf(tdecl.Type, go1_18, "using type constraint %s", rhs)
}).describef(obj, "validType(%s)", obj.Name())
- alias := tdecl.Assign.IsValid()
- if alias && tdecl.TypeParams.NumFields() != 0 {
+ aliasDecl := tdecl.Assign.IsValid()
+ if aliasDecl && tdecl.TypeParams.NumFields() != 0 {
// The parser will ensure this but we may still get an invalid AST.
// Complain and continue as regular type definition.
check.error(atPos(tdecl.Assign), BadDecl, "generic type cannot be alias")
- alias = false
+ aliasDecl = false
}
// alias declaration
- if alias {
+ if aliasDecl {
check.verifyVersionf(atPos(tdecl.Assign), go1_9, "type aliases")
- check.brokenAlias(obj)
- rhs = check.typ(tdecl.Type)
- check.validAlias(obj, rhs)
+ if check.conf._EnableAlias {
+ // TODO(gri) Should be able to use nil instead of Typ[Invalid] to mark
+ // the alias as incomplete. Currently this causes problems
+ // with certain cycles. Investigate.
+ alias := check.newAlias(obj, Typ[Invalid])
+ setDefType(def, alias)
+ rhs = check.definedType(tdecl.Type, obj)
+ assert(rhs != nil)
+ alias.fromRHS = rhs
+ _Unalias(alias) // resolve alias.actual
+ } else {
+ check.brokenAlias(obj)
+ rhs = check.typ(tdecl.Type)
+ check.validAlias(obj, rhs)
+ }
return
}
case *Basic:
// nothing to do
+ case *_Alias:
+ return w.isParameterized(_Unalias(t))
+
case *Array:
return w.isParameterized(t.elem)
case *Basic:
// nothing to do
+ case *_Alias:
+ w.typ(_Unalias(t))
+
case *Array:
w.typ(t.elem)
// use named receiver type if available (for better error messages)
var recvTyp Type = ityp
if def != nil {
- if named, _ := def.typ.(*Named); named != nil {
+ if named := asNamed(def.typ); named != nil {
recvTyp = named
}
}
t.Fatalf("types of v and T are not pointer-identical: %p != %p", v.Type().(*TypeParam), T)
}
}
+
+func TestIssue44410(t *testing.T) {
+ const src = `
+package p
+
+type A = []int
+type S struct{ A }
+`
+
+ var conf Config
+ *boolFieldAddr(&conf, "_EnableAlias") = true
+ pkg := mustTypecheck(src, &conf, nil)
+
+ S := pkg.Scope().Lookup("S")
+ if S == nil {
+ t.Fatal("object S not found")
+ }
+
+ got := S.String()
+ const want = "type p.S struct{p.A /* = []int */}"
+ if got != want {
+ t.Fatalf("got %q; want %q", got, want)
+ }
+}
// with an underlying pointer type!) and returns its base and true.
// Otherwise it returns (typ, false).
func deref(typ Type) (Type, bool) {
- if p, _ := typ.(*Pointer); p != nil {
+ if p, _ := _Unalias(typ).(*Pointer); p != nil {
// p.base should never be nil, but be conservative
if p.base == nil {
if debug {
// type parameters.
var do func(typ Type)
do = func(typ Type) {
- switch typ := typ.(type) {
+ switch typ := _Unalias(typ).(type) {
default:
panic("unexpected type")
}
}
-func (t *Named) Underlying() Type { return t.resolve().underlying }
+// TODO(gri) Investigate if _Unalias can be moved to where underlying is set.
+func (t *Named) Underlying() Type { return _Unalias(t.resolve().underlying) }
func (t *Named) String() string { return TypeString(t, nil) }
// ----------------------------------------------------------------------------
switch t := obj.typ.(type) {
case nil:
return false
+ // case *_Alias:
+ // handled by default case
case *Basic:
// unsafe.Pointer is not an alias.
if obj.pkg == Unsafe {
package types
// isValid reports whether t is a valid type.
-func isValid(t Type) bool { return t != Typ[Invalid] }
+func isValid(t Type) bool { return _Unalias(t) != Typ[Invalid] }
// The isX predicates below report whether t is an X.
// If t is a type parameter the result is false; i.e.,
// for all specific types of the type parameter's type set.
// allBasic(t, info) is an optimized version of isBasic(coreType(t), info).
func allBasic(t Type, info BasicInfo) bool {
- if tpar, _ := t.(*TypeParam); tpar != nil {
+ if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil {
return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
}
return isBasic(t, info)
// predeclared types, defined types, and type parameters.
// hasName may be called with types that are not fully set up.
func hasName(t Type) bool {
- switch t.(type) {
+ switch _Unalias(t).(type) {
case *Basic, *Named, *TypeParam:
return true
}
// This includes all non-defined types, but also basic types.
// isTypeLit may be called with types that are not fully set up.
func isTypeLit(t Type) bool {
- switch t.(type) {
+ switch _Unalias(t).(type) {
case *Named, *TypeParam:
return false
}
// constant or boolean. isTyped may be called with types that
// are not fully set up.
func isTyped(t Type) bool {
- // isTyped is called with types that are not fully
- // set up. Must not call under()!
+ // Alias or Named types cannot denote untyped types,
+ // thus we don't need to call _Unalias or under
+ // (which would be unsafe to do for types that are
+ // not fully set up).
b, _ := t.(*Basic)
return b == nil || b.info&IsUntyped == 0
}
// isTypeParam reports whether t is a type parameter.
func isTypeParam(t Type) bool {
- _, ok := t.(*TypeParam)
+ _, ok := _Unalias(t).(*TypeParam)
return ok
}
// use anywhere, but it may report a false negative if the type set has not been
// computed yet.
func hasEmptyTypeset(t Type) bool {
- if tpar, _ := t.(*TypeParam); tpar != nil && tpar.bound != nil {
+ if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil && tpar.bound != nil {
iface, _ := safeUnderlying(tpar.bound).(*Interface)
return iface != nil && iface.tset != nil && iface.tset.IsEmpty()
}
// For changes to this code the corresponding changes should be made to unifier.nify.
func (c *comparer) identical(x, y Type, p *ifacePair) bool {
+ x = _Unalias(x)
+ y = _Unalias(y)
+
if x == y {
return true
}
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
func Default(t Type) Type {
- if t, ok := t.(*Basic); ok {
+ if t, ok := _Unalias(t).(*Basic); ok {
switch t.kind {
case UntypedBool:
return Typ[Bool]
}
}
- // We process non-alias type declarations first, followed by alias declarations,
- // and then everything else. This appears to avoid most situations where the type
- // of an alias is needed before it is available.
- // There may still be cases where this is not good enough (see also go.dev/issue/25838).
- // In those cases Checker.ident will report an error ("invalid use of type alias").
- var aliasList []*TypeName
- var othersList []Object // everything that's not a type
- // phase 1: non-alias type declarations
- for _, obj := range objList {
- if tname, _ := obj.(*TypeName); tname != nil {
- if check.objMap[tname].tdecl.Assign.IsValid() {
- aliasList = append(aliasList, tname)
+ if check.conf._EnableAlias {
+ // With _Alias nodes we can process declarations in any order.
+ for _, obj := range objList {
+ check.objDecl(obj, nil)
+ }
+ } else {
+ // Without _Alias nodes, we process non-alias type declarations first, followed by
+ // alias declarations, and then everything else. This appears to avoid most situations
+ // where the type of an alias is needed before it is available.
+ // There may still be cases where this is not good enough (see also go.dev/issue/25838).
+ // In those cases Checker.ident will report an error ("invalid use of type alias").
+ var aliasList []*TypeName
+ var othersList []Object // everything that's not a type
+ // phase 1: non-alias type declarations
+ for _, obj := range objList {
+ if tname, _ := obj.(*TypeName); tname != nil {
+ if check.objMap[tname].tdecl.Assign.IsValid() {
+ aliasList = append(aliasList, tname)
+ } else {
+ check.objDecl(obj, nil)
+ }
} else {
- check.objDecl(obj, nil)
+ othersList = append(othersList, obj)
}
- } else {
- othersList = append(othersList, obj)
}
- }
- // phase 2: alias type declarations
- for _, obj := range aliasList {
- check.objDecl(obj, nil)
- }
- // phase 3: all other declarations
- for _, obj := range othersList {
- check.objDecl(obj, nil)
+ // phase 2: alias type declarations
+ for _, obj := range aliasList {
+ check.objDecl(obj, nil)
+ }
+ // phase 3: all other declarations
+ for _, obj := range othersList {
+ check.objDecl(obj, nil)
+ }
}
// At this point we may have a non-empty check.methods map; this means that not all
check.later(func() {
// spec: "The receiver type must be of the form T or *T where T is a type name."
rtyp, _ := deref(recv.typ)
- if !isValid(rtyp) {
+ atyp := _Unalias(rtyp)
+ if !isValid(atyp) {
return // error was reported before
}
// spec: "The type denoted by T is called the receiver base type; it must not
// be a pointer or interface type and it must be declared in the same package
// as the method."
- switch T := rtyp.(type) {
+ switch T := atyp.(type) {
case *Named:
// The receiver type may be an instantiated type referred to
// by an alias (which cannot have receiver parameters for now).
}
}
+ case *_Alias:
+ w.typeName(t.obj)
+ if w.ctxt != nil {
+ // TODO(gri) do we need to print the alias type name, too?
+ w.typ(_Unalias(t.obj.typ))
+ } else {
+ w.string(fmt.Sprintf(" /* = %s */", _Unalias(t.obj.typ)))
+ }
+
default:
// For externally defined implementations of Type.
// Note: In this case cycles won't be caught.
x.mode = constant_
case *TypeName:
- if check.isBrokenAlias(obj) {
+ if !check.conf._EnableAlias && check.isBrokenAlias(obj) {
check.errorf(e, InvalidDeclCycle, "invalid use of type alias %s in recursive type (see go.dev/issue/50729)", obj.name)
return
}
func setDefType(def *TypeName, typ Type) {
if def != nil {
switch t := def.typ.(type) {
- // case *_Alias:
+ case *_Alias:
+ // t.fromRHS should always be set, either to an invalid type
+ // in the beginning, or to typ in certain cyclic declarations.
+ if t.fromRHS != Typ[Invalid] && t.fromRHS != typ {
+ panic(sprintf(nil, nil, true, "t.fromRHS = %s, typ = %s\n", t.fromRHS, typ))
+ }
+ t.fromRHS = typ
case *Basic:
assert(t == Typ[Invalid])
case *Named:
u.depth--
}()
+ x = _Unalias(x)
+ y = _Unalias(y)
+
// nothing to do if x == y
if x == y {
return true
// (say S->F->S) we have an invalid recursive type. The path list is the full
// path of named types in a cycle, it is only needed for error reporting.
func (check *Checker) validType0(typ Type, nest, path []*Named) bool {
- switch t := typ.(type) {
+ switch t := _Unalias(typ).(type) {
case nil:
// We should never see a nil type but be conservative and panic
// only in debug mode.
+// -alias=false
+
// Copyright 2017 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.
--- /dev/null
+// -alias
+
+// Copyright 2017 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
+
+import "unsafe"
+
+// test case from issue #18395
+
+type (
+ A interface { B }
+ B interface { C }
+ C interface { D; F() A }
+ D interface { G() B }
+)
+
+var _ = A(nil).G // G must be found
+
+
+// test case from issue #21804
+
+type sourceBridge interface {
+ listVersions() ([]Version, error)
+}
+
+type Constraint interface {
+ copyTo(*ConstraintMsg)
+}
+
+type ConstraintMsg struct{}
+
+func (m *ConstraintMsg) asUnpairedVersion() UnpairedVersion {
+ return nil
+}
+
+type Version interface {
+ Constraint
+}
+
+type UnpairedVersion interface {
+ Version
+}
+
+var _ Constraint = UnpairedVersion(nil)
+
+
+// derived test case from issue #21804
+
+type (
+ _ interface{ m(B1) }
+ A1 interface{ a(D1) }
+ B1 interface{ A1 }
+ C1 interface{ B1 }
+ D1 interface{ C1 }
+)
+
+var _ A1 = C1(nil)
+
+
+// derived test case from issue #22701
+
+func F(x I4) interface{} {
+ return x.Method()
+}
+
+type Unused interface {
+ RefersToI1(a I1)
+}
+
+type I1 interface {
+ I2
+ I3
+}
+
+type I2 interface {
+ RefersToI4() I4
+}
+
+type I3 interface {
+ Method() interface{}
+}
+
+type I4 interface {
+ I1
+}
+
+
+// check embedding of error interface
+
+type Error interface{ error }
+
+var err Error
+var _ = err.Error()
+
+
+// more esoteric cases
+
+type (
+ T1 interface { T2 }
+ T2 /* ERROR "invalid recursive type" */ T2
+)
+
+type (
+ T3 interface { T4 }
+ T4 /* ERROR "invalid recursive type" */ T5
+ T5 = T6
+ T6 = T7
+ T7 = T4
+)
+
+
+// arbitrary code may appear inside an interface
+
+const n = unsafe.Sizeof(func(){})
+
+type I interface {
+ m([unsafe.Sizeof(func() { I.m(nil, [n]byte{}) })]byte)
+}
+
+
+// test cases for varias alias cycles
+
+type T10 /* ERROR "invalid recursive type" */ = *T10 // issue #25141
+type T11 /* ERROR "invalid recursive type" */ = interface{ f(T11) } // issue #23139
+
+// issue #18640
+type (
+ aa = bb
+ bb struct {
+ *aa
+ }
+)
+
+type (
+ a struct{ *b }
+ b = c
+ c struct{ *b }
+)
+
+// issue #24939
+type (
+ _ interface {
+ M(P)
+ }
+
+ M interface {
+ F() P
+ }
+
+ P = interface {
+ I() M
+ }
+)
+
+// issue #8699
+type T12 /* ERROR "invalid recursive type" */ [len(a12)]int
+var a12 = makeArray()
+func makeArray() (res T12) { return }
+
+// issue #20770
+var r /* ERROR "invalid cycle in declaration of r" */ = newReader()
+func newReader() r
+
+// variations of the theme of #8699 and #20770
+var arr /* ERROR "cycle" */ = f()
+func f() [len(arr)]int
+
+// issue #25790
+func ff(ff /* ERROR "not a type" */ )
+func gg((gg /* ERROR "not a type" */ ))
+
+type T13 /* ERROR "invalid recursive type T13" */ [len(b13)]int
+var b13 T13
+
+func g1() [unsafe.Sizeof(g1)]int
+func g2() [unsafe.Sizeof(x2)]int
+var x2 = g2
+
+// verify that we get the correct sizes for the functions above
+// (note: assert is statically evaluated in go/types test mode)
+func init() {
+ assert(unsafe.Sizeof(g1) == 8)
+ assert(unsafe.Sizeof(x2) == 8)
+}
+
+func h() [h /* ERROR "no value" */ ()[0]]int { panic(0) }
+
+var c14 /* ERROR "cycle" */ T14
+type T14 [uintptr(unsafe.Sizeof(&c14))]byte
+
+// issue #34333
+type T15 /* ERROR "invalid recursive type T15" */ struct {
+ f func() T16
+ b T16
+}
+
+type T16 struct {
+ T15
+}
\ No newline at end of file
)
// alias receiver types
-func (Ai /* ERROR "cannot define new methods on non-local type int" */) m1() {}
+func (Ai /* ERRORx "cannot define new methods on non-local type (int|Ai)" */) m1() {}
func (T0) m1() {}
func (A0) m1 /* ERROR "already declared" */ () {}
func (A0) m2 () {}
B2 = int
)
-func (B0 /* ERROR "cannot define new methods on non-local type int" */ ) m() {}
-func (B1 /* ERROR "cannot define new methods on non-local type int" */ ) n() {}
+func (B0 /* ERRORx "cannot define new methods on non-local type (int|B)" */ ) m() {}
+func (B1 /* ERRORx "cannot define new methods on non-local type (int|B)" */ ) n() {}
// cycles
type (
}
type issue25301c interface {
- notE // ERROR "non-interface type struct{}"
+ notE // ERRORx "non-interface type (struct{}|notE)"
}
type notE = struct{}
P = *T
T P
)
+
+func newA(c funcAlias) A {
+ return A{c: c}
+}
+
+type B struct {
+ a *A
+}
+
+type A struct {
+ c funcAlias
+}
+
+type funcAlias = func(B)
T11 = T
)
-func (T9 /* ERROR "invalid receiver type **T" */ ) m9() {}
+func (T9 /* ERRORx `invalid receiver type (\*\*T|T9)` */ ) m9() {}
func _() { (T{}).m9 /* ERROR "has no field or method m9" */ () }
func _() { (&T{}).m9 /* ERROR "has no field or method m9" */ () }
+// -alias=false
+
// Copyright 2021 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.
--- /dev/null
+// -alias
+
+// Copyright 2021 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
+
+// test case 1
+type T /* ERROR "invalid recursive type" */ [U interface{ M() T[U] }] int
+
+type X int
+
+func (X) M() T[X] { return 0 }
+
+// test case 2
+type A /* ERROR "invalid recursive type" */ [T interface{ A[T] }] interface{}
+
+// test case 3
+// TODO(gri) should report error only once
+type A2 /* ERROR "invalid recursive type" */ /* ERROR "invalid recursive type" */ [U interface{ A2[U] }] interface{ M() A2[U] }
+
+type I interface{ A2[I]; M() A2[I] }
type A2 = T[int]
-func (A2 /* ERROR "cannot define new methods on instantiated type T[int]" */) m3() {}
-func (_ /* ERROR "cannot define new methods on instantiated type T[int]" */ A2) m4() {}
+func (A2 /* ERRORx `cannot define new methods on instantiated type (T\[int\]|A2)` */) m3() {}
+func (_ /* ERRORx `cannot define new methods on instantiated type (T\[int\]|A2)` */ A2) m4() {}
func (T[int]) m5() {} // int is the type parameter name, not an instantiation
func (T[* /* ERROR "must be an identifier" */ int]) m6() {} // syntax error
+// -alias=false
+
// 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.
--- /dev/null
+// -alias
+
+// 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
+}
+
+type C interface {
+ NSR(any) *SR
+}
// and at least one of V or T is not a named type."
// (here a named type is a type with a name)
func _[TP1, TP2 Interface](X1 TP1, X2 TP2) {
- b = B // ERRORx `cannot use B .* as int value`
+ b = B // ERRORx `cannot use B .* as (int|_Basic.*) value`
a = A
l = L
s = S
_ TP0 = C // ERRORx `cannot use C .* as TP0 value`
_ TP1 = c
_ TP1 = C // ERRORx `cannot use C .* as TP1 value`
- _ TP2 = c // ERRORx `.* cannot assign chan int to chan byte`
+ _ TP2 = c // ERRORx `.* cannot assign (chan int|_Chan.*) to chan byte`
)
}
I = X0
c = X1
C = X1 // ERRORx `cannot use X1 .* as Chan value`
- c = X2 // ERRORx `.* cannot assign chan byte \(in TP2\) to chan int`
+ c = X2 // ERRORx `.* cannot assign chan byte \(in TP2\) to (chan int|_Chan.*)`
}
// "x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type"