From: Robert Griesemer Date: Tue, 22 Aug 2023 21:53:57 +0000 (-0700) Subject: go/types, types2: introduce _Alias type node X-Git-Tag: go1.22rc1~371 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=bea55136b2d50fc514ddfba4380311295975a660;p=gostls13.git 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 Reviewed-by: Robert Findley Auto-Submit: Robert Griesemer Reviewed-by: Robert Griesemer TryBot-Result: Gopher Robot Run-TryBot: Robert Griesemer --- diff --git a/src/cmd/compile/internal/types2/alias.go b/src/cmd/compile/internal/types2/alias.go index 375046b983..c0e646eb1c 100644 --- a/src/cmd/compile/internal/types2/alias.go +++ b/src/cmd/compile/internal/types2/alias.go @@ -4,14 +4,77 @@ 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) +} diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go index 9f2daa7f70..d807096db5 100644 --- a/src/cmd/compile/internal/types2/api.go +++ b/src/cmd/compile/internal/types2/api.go @@ -170,6 +170,11 @@ type Config struct { // 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 diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index f21704f108..3748926e40 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -152,9 +152,14 @@ func (check *Checker) addDeclDep(to Object) { 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) } @@ -164,13 +169,15 @@ func (check *Checker) brokenAlias(alias *TypeName) { // 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) { diff --git a/src/cmd/compile/internal/types2/check_test.go b/src/cmd/compile/internal/types2/check_test.go index d70d7e3232..fec3e73126 100644 --- a/src/cmd/compile/internal/types2/check_test.go +++ b/src/cmd/compile/internal/types2/check_test.go @@ -51,6 +51,7 @@ import ( 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) { @@ -130,6 +131,7 @@ func testFiles(t *testing.T, filenames []string, srcs [][]byte, colDelta uint, m 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) } diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index ed6b96eb09..6fd2c2cad8 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -251,10 +251,14 @@ loop: // 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++ @@ -322,7 +326,11 @@ func (check *Checker) cycleError(cycle []Object) { // 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 @@ -495,20 +503,32 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *TypeN _ = 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 } diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index 21a7739fb1..49d4ed7fe8 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -542,6 +542,9 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { case *Basic: // nothing to do + case *_Alias: + return w.isParameterized(_Unalias(t)) + case *Array: return w.isParameterized(t.elem) @@ -693,6 +696,9 @@ func (w *cycleFinder) typ(typ Type) { case *Basic: // nothing to do + case *_Alias: + w.typ(_Unalias(t)) + case *Array: w.typ(t.elem) diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go index a815ae2637..4072098e05 100644 --- a/src/cmd/compile/internal/types2/interface.go +++ b/src/cmd/compile/internal/types2/interface.go @@ -152,7 +152,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType // 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 } } diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go index 32671403c2..a66e8eab92 100644 --- a/src/cmd/compile/internal/types2/issues_test.go +++ b/src/cmd/compile/internal/types2/issues_test.go @@ -980,3 +980,27 @@ func f[I *T, T any]() { 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) + } +} diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 620ad1a70c..b8926250cf 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -527,7 +527,7 @@ func (check *Checker) newAssertableTo(pos syntax.Pos, V, T Type, cause *string) // 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 { diff --git a/src/cmd/compile/internal/types2/mono.go b/src/cmd/compile/internal/types2/mono.go index 5b68f2aaa4..497276083a 100644 --- a/src/cmd/compile/internal/types2/mono.go +++ b/src/cmd/compile/internal/types2/mono.go @@ -208,7 +208,7 @@ func (w *monoGraph) assign(pkg *Package, pos syntax.Pos, tpar *TypeParam, targ T // type parameters. var do func(typ Type) do = func(typ Type) { - switch typ := typ.(type) { + switch typ := _Unalias(typ).(type) { default: panic("unexpected type") diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 81db4d84c1..dcfb20592c 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -453,7 +453,8 @@ func (t *Named) AddMethod(m *Func) { } } -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) } // ---------------------------------------------------------------------------- diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index 591adeac3d..23387a1c21 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -285,6 +285,8 @@ func (obj *TypeName) IsAlias() bool { 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 { diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index 11f543f475..9ec7d58d6f 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -7,7 +7,7 @@ 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., @@ -50,7 +50,7 @@ func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) } // 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) @@ -60,7 +60,7 @@ func allBasic(t Type, info BasicInfo) bool { // 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 } @@ -71,7 +71,7 @@ func hasName(t Type) bool { // 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 } @@ -82,8 +82,10 @@ func isTypeLit(t Type) bool { // 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 } @@ -106,7 +108,7 @@ func isNonTypeParamInterface(t Type) bool { // isTypeParam reports whether t is a type parameter. func isTypeParam(t Type) bool { - _, ok := t.(*TypeParam) + _, ok := _Unalias(t).(*TypeParam) return ok } @@ -115,7 +117,7 @@ func isTypeParam(t Type) bool { // 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() } @@ -221,6 +223,9 @@ type comparer struct { // 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 } @@ -495,7 +500,7 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool // 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] diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go index a76e6d3204..e074b7548c 100644 --- a/src/cmd/compile/internal/types2/resolver.go +++ b/src/cmd/compile/internal/types2/resolver.go @@ -677,32 +677,39 @@ func (check *Checker) packageObjects() { } } - // 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 diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index edd8d66f13..f876b16c8f 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -208,13 +208,14 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] 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). diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index dfa929476c..3c2150273e 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -322,10 +322,19 @@ func (w *typeWriter) typ(typ Type) { // 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. diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index b1a03bad3d..cabf4eb856 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -94,7 +94,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *TypeName, wantType 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 } @@ -403,7 +403,13 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *TypeName) (T Type) { 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: diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index e0340a5907..000321e4ea 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -291,6 +291,9 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) { u.depth-- }() + x = _Unalias(x) + y = _Unalias(y) + // nothing to do if x == y if x == y { return true diff --git a/src/cmd/compile/internal/types2/validtype.go b/src/cmd/compile/internal/types2/validtype.go index 8337e0c8c7..07a291b435 100644 --- a/src/cmd/compile/internal/types2/validtype.go +++ b/src/cmd/compile/internal/types2/validtype.go @@ -23,7 +23,7 @@ func (check *Checker) validType(typ *Named) { // (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. diff --git a/src/go/types/alias.go b/src/go/types/alias.go index 7dc7fe9e59..f79d5eaf3a 100644 --- a/src/go/types/alias.go +++ b/src/go/types/alias.go @@ -6,14 +6,77 @@ 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) +} diff --git a/src/go/types/api.go b/src/go/types/api.go index 81a98f7e66..38cde80085 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -171,6 +171,11 @@ type Config struct { // 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 diff --git a/src/go/types/check.go b/src/go/types/check.go index 89b8ee07a2..f9decbf9a0 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -154,9 +154,14 @@ func (check *Checker) addDeclDep(to Object) { 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) } @@ -166,13 +171,15 @@ func (check *Checker) brokenAlias(alias *TypeName) { // 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) { diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index ffad095244..50233178ec 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -56,6 +56,7 @@ import ( 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() @@ -141,6 +142,7 @@ func testFiles(t *testing.T, filenames []string, srcs [][]byte, manual bool, opt 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) } diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 7d6a2aa039..16f250aee2 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -249,10 +249,14 @@ loop: // 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++ @@ -320,7 +324,11 @@ func (check *Checker) cycleError(cycle []Object) { // 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 @@ -564,20 +572,32 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *TypeName _ = 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 } diff --git a/src/go/types/infer.go b/src/go/types/infer.go index 0a9074e0af..962548a9b0 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -544,6 +544,9 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { case *Basic: // nothing to do + case *_Alias: + return w.isParameterized(_Unalias(t)) + case *Array: return w.isParameterized(t.elem) @@ -695,6 +698,9 @@ func (w *cycleFinder) typ(typ Type) { case *Basic: // nothing to do + case *_Alias: + w.typ(_Unalias(t)) + case *Array: w.typ(t.elem) diff --git a/src/go/types/interface.go b/src/go/types/interface.go index 40a0baedd3..01bbb08e0e 100644 --- a/src/go/types/interface.go +++ b/src/go/types/interface.go @@ -200,7 +200,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d // 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 } } diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index 5530fceef6..01d7398788 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -990,3 +990,27 @@ func f[I *T, T any]() { 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) + } +} diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index 4fcae994f9..e6ad728437 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -529,7 +529,7 @@ func (check *Checker) newAssertableTo(pos token.Pos, V, T Type, cause *string) b // 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 { diff --git a/src/go/types/mono.go b/src/go/types/mono.go index ebf4d8cef7..ee01435e77 100644 --- a/src/go/types/mono.go +++ b/src/go/types/mono.go @@ -206,7 +206,7 @@ func (w *monoGraph) assign(pkg *Package, pos token.Pos, tpar *TypeParam, targ Ty // type parameters. var do func(typ Type) do = func(typ Type) { - switch typ := typ.(type) { + switch typ := _Unalias(typ).(type) { default: panic("unexpected type") diff --git a/src/go/types/named.go b/src/go/types/named.go index 440fc87891..fed114edc0 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -455,7 +455,8 @@ func (t *Named) AddMethod(m *Func) { } } -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) } // ---------------------------------------------------------------------------- diff --git a/src/go/types/object.go b/src/go/types/object.go index 41fabfd051..ef20de4b51 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -287,6 +287,8 @@ func (obj *TypeName) IsAlias() bool { 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 { diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 01804c69c5..5dc775af8d 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -9,7 +9,7 @@ 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., @@ -52,7 +52,7 @@ func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) } // 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) @@ -62,7 +62,7 @@ func allBasic(t Type, info BasicInfo) bool { // 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 } @@ -73,7 +73,7 @@ func hasName(t Type) bool { // 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 } @@ -84,8 +84,10 @@ func isTypeLit(t Type) bool { // 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 } @@ -108,7 +110,7 @@ func isNonTypeParamInterface(t Type) bool { // isTypeParam reports whether t is a type parameter. func isTypeParam(t Type) bool { - _, ok := t.(*TypeParam) + _, ok := _Unalias(t).(*TypeParam) return ok } @@ -117,7 +119,7 @@ func isTypeParam(t Type) bool { // 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() } @@ -223,6 +225,9 @@ type comparer struct { // 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 } @@ -497,7 +502,7 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool // 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] diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index 6397b394d1..8b6a1b4b79 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -659,32 +659,39 @@ func (check *Checker) packageObjects() { } } - // 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 diff --git a/src/go/types/signature.go b/src/go/types/signature.go index e34db602f3..e7a348d851 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -211,13 +211,14 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast 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). diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index cab36e4c59..6eee1af2a8 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -329,6 +329,15 @@ func (w *typeWriter) typ(typ Type) { } } + 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. diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index d35c6d1263..5bc5a1f3ac 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -95,7 +95,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *TypeName, wantType bo 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 } @@ -394,7 +394,13 @@ func (check *Checker) typInternal(e0 ast.Expr, def *TypeName) (T Type) { 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: diff --git a/src/go/types/unify.go b/src/go/types/unify.go index 3c7b782b5a..7f08d9f579 100644 --- a/src/go/types/unify.go +++ b/src/go/types/unify.go @@ -293,6 +293,9 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) { u.depth-- }() + x = _Unalias(x) + y = _Unalias(y) + // nothing to do if x == y if x == y { return true diff --git a/src/go/types/validtype.go b/src/go/types/validtype.go index 0c0562b287..dde5cb039d 100644 --- a/src/go/types/validtype.go +++ b/src/go/types/validtype.go @@ -25,7 +25,7 @@ func (check *Checker) validType(typ *Named) { // (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. diff --git a/src/internal/types/testdata/check/cycles5.go b/src/internal/types/testdata/check/cycles5.go index a863aa8e39..f854de0fda 100644 --- a/src/internal/types/testdata/check/cycles5.go +++ b/src/internal/types/testdata/check/cycles5.go @@ -1,3 +1,5 @@ +// -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. diff --git a/src/internal/types/testdata/check/cycles5a.go b/src/internal/types/testdata/check/cycles5a.go new file mode 100644 index 0000000000..96234bf1b9 --- /dev/null +++ b/src/internal/types/testdata/check/cycles5a.go @@ -0,0 +1,202 @@ +// -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 diff --git a/src/internal/types/testdata/check/decls4.go b/src/internal/types/testdata/check/decls4.go index c47a68d525..7c063904c8 100644 --- a/src/internal/types/testdata/check/decls4.go +++ b/src/internal/types/testdata/check/decls4.go @@ -59,7 +59,7 @@ var ( ) // 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 () {} @@ -115,8 +115,8 @@ type ( 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 ( diff --git a/src/internal/types/testdata/check/issues0.go b/src/internal/types/testdata/check/issues0.go index 6039df951e..2f4d266b8a 100644 --- a/src/internal/types/testdata/check/issues0.go +++ b/src/internal/types/testdata/check/issues0.go @@ -282,7 +282,7 @@ type issue25301b /* ERROR "invalid recursive type" */ = interface { } type issue25301c interface { - notE // ERROR "non-interface type struct{}" + notE // ERRORx "non-interface type (struct{}|notE)" } type notE = struct{} diff --git a/src/internal/types/testdata/fixedbugs/issue25838.go b/src/internal/types/testdata/fixedbugs/issue25838.go index adbd138f16..b0ea98ee89 100644 --- a/src/internal/types/testdata/fixedbugs/issue25838.go +++ b/src/internal/types/testdata/fixedbugs/issue25838.go @@ -24,3 +24,17 @@ type ( 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) diff --git a/src/internal/types/testdata/fixedbugs/issue28251.go b/src/internal/types/testdata/fixedbugs/issue28251.go index 77fd369ff9..71e727e2ac 100644 --- a/src/internal/types/testdata/fixedbugs/issue28251.go +++ b/src/internal/types/testdata/fixedbugs/issue28251.go @@ -60,6 +60,6 @@ type ( 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" */ () } diff --git a/src/internal/types/testdata/fixedbugs/issue46461.go b/src/internal/types/testdata/fixedbugs/issue46461.go index ae70048d9b..c42b81d0a0 100644 --- a/src/internal/types/testdata/fixedbugs/issue46461.go +++ b/src/internal/types/testdata/fixedbugs/issue46461.go @@ -1,3 +1,5 @@ +// -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. diff --git a/src/internal/types/testdata/fixedbugs/issue46461a.go b/src/internal/types/testdata/fixedbugs/issue46461a.go new file mode 100644 index 0000000000..f1df59b7bd --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue46461a.go @@ -0,0 +1,23 @@ +// -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] } diff --git a/src/internal/types/testdata/fixedbugs/issue47968.go b/src/internal/types/testdata/fixedbugs/issue47968.go index c516eee2da..83a1786133 100644 --- a/src/internal/types/testdata/fixedbugs/issue47968.go +++ b/src/internal/types/testdata/fixedbugs/issue47968.go @@ -14,8 +14,8 @@ func (A1[P]) m2() {} 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 diff --git a/src/internal/types/testdata/fixedbugs/issue50779.go b/src/internal/types/testdata/fixedbugs/issue50779.go index 09ddf53ddf..21f0c09b77 100644 --- a/src/internal/types/testdata/fixedbugs/issue50779.go +++ b/src/internal/types/testdata/fixedbugs/issue50779.go @@ -1,3 +1,5 @@ +// -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. diff --git a/src/internal/types/testdata/fixedbugs/issue50779a.go b/src/internal/types/testdata/fixedbugs/issue50779a.go new file mode 100644 index 0000000000..37217887bf --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50779a.go @@ -0,0 +1,25 @@ +// -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 +} diff --git a/src/internal/types/testdata/spec/assignability.go b/src/internal/types/testdata/spec/assignability.go index 6670870b32..2ce9a4a150 100644 --- a/src/internal/types/testdata/spec/assignability.go +++ b/src/internal/types/testdata/spec/assignability.go @@ -37,7 +37,7 @@ func _[TP any](X TP) { // 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 @@ -134,7 +134,7 @@ func _[ _ 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` ) } @@ -148,7 +148,7 @@ func _[ 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"