]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: introduce _Alias type node
authorRobert Griesemer <gri@golang.org>
Tue, 22 Aug 2023 21:53:57 +0000 (14:53 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 9 Nov 2023 17:24:42 +0000 (17:24 +0000)
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 <mdempsky@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Griesemer <gri@google.com>

50 files changed:
src/cmd/compile/internal/types2/alias.go
src/cmd/compile/internal/types2/api.go
src/cmd/compile/internal/types2/check.go
src/cmd/compile/internal/types2/check_test.go
src/cmd/compile/internal/types2/decl.go
src/cmd/compile/internal/types2/infer.go
src/cmd/compile/internal/types2/interface.go
src/cmd/compile/internal/types2/issues_test.go
src/cmd/compile/internal/types2/lookup.go
src/cmd/compile/internal/types2/mono.go
src/cmd/compile/internal/types2/named.go
src/cmd/compile/internal/types2/object.go
src/cmd/compile/internal/types2/predicates.go
src/cmd/compile/internal/types2/resolver.go
src/cmd/compile/internal/types2/signature.go
src/cmd/compile/internal/types2/typestring.go
src/cmd/compile/internal/types2/typexpr.go
src/cmd/compile/internal/types2/unify.go
src/cmd/compile/internal/types2/validtype.go
src/go/types/alias.go
src/go/types/api.go
src/go/types/check.go
src/go/types/check_test.go
src/go/types/decl.go
src/go/types/infer.go
src/go/types/interface.go
src/go/types/issues_test.go
src/go/types/lookup.go
src/go/types/mono.go
src/go/types/named.go
src/go/types/object.go
src/go/types/predicates.go
src/go/types/resolver.go
src/go/types/signature.go
src/go/types/typestring.go
src/go/types/typexpr.go
src/go/types/unify.go
src/go/types/validtype.go
src/internal/types/testdata/check/cycles5.go
src/internal/types/testdata/check/cycles5a.go [new file with mode: 0644]
src/internal/types/testdata/check/decls4.go
src/internal/types/testdata/check/issues0.go
src/internal/types/testdata/fixedbugs/issue25838.go
src/internal/types/testdata/fixedbugs/issue28251.go
src/internal/types/testdata/fixedbugs/issue46461.go
src/internal/types/testdata/fixedbugs/issue46461a.go [new file with mode: 0644]
src/internal/types/testdata/fixedbugs/issue47968.go
src/internal/types/testdata/fixedbugs/issue50779.go
src/internal/types/testdata/fixedbugs/issue50779a.go [new file with mode: 0644]
src/internal/types/testdata/spec/assignability.go

index 375046b98351cf90c435569ea63eb6035872117e..c0e646eb1c07bd39bbfe472d45c34bb6f2a6a7e3 100644 (file)
@@ -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)
+}
index 9f2daa7f70769d814b50e0923dfa93be0afd8a06..d807096db599900300dd784714155ba04f38bae0 100644 (file)
@@ -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
index f21704f1083fca72f6991cd40cf75b7ba20e38c9..3748926e402a933d146b24b4fb039c7be9f2c33a 100644 (file)
@@ -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) {
index d70d7e32321a96bb41a10c0b4d92594afdd90aaa..fec3e73126b05ed7510528dae4be5c348a65e88d 100644 (file)
@@ -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)
        }
index ed6b96eb090aa2c790a00b1b851de3d913a5f90a..6fd2c2cad8f112e1bd1fed4ed4b31520c33805cd 100644 (file)
@@ -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
        }
 
index 21a7739fb1de2bab383922441ef0c013518b4991..49d4ed7fe81c47a859d8ed565656274e7fb0b74e 100644 (file)
@@ -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)
 
index a815ae2637bd78a4ca6cafa622db5335d4223ae7..4072098e05234d94fe071f02f2d8e38863778f7c 100644 (file)
@@ -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
                        }
                }
index 32671403c2e9c3a2f133cf7e0f8cd2a2ec7e9442..a66e8eab929ea936efbd432dfab571929ca6ef4a 100644 (file)
@@ -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)
+       }
+}
index 620ad1a70c80b2a9e07368af8febde3d9a2960ce..b8926250cf2a4a98b8bbe91912256f55d69c284a 100644 (file)
@@ -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 {
index 5b68f2aaa47bf66c6365d2ed453b940d9a3fa174..497276083abeda374c46c56ccb156f612acdfe6a 100644 (file)
@@ -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")
 
index 81db4d84c19cac669610a023ffbb5af2cc724804..dcfb20592ca302f3e22cbf1be2b05fb614b3d0a2 100644 (file)
@@ -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) }
 
 // ----------------------------------------------------------------------------
index 591adeac3da31449901247edf80614af75c86b09..23387a1c21f8dabce472a2fa4f914c2c7b7b1772 100644 (file)
@@ -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 {
index 11f543f4751092b7e6d4d3d02ab311d8ee8438df..9ec7d58d6fbab69a18fbf509e0fff95b867dfecf 100644 (file)
@@ -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]
index a76e6d320483055ccd0a09771a52f466bd29487a..e074b7548c05c0a5ac8bcdb48b88ffe445bfaa0b 100644 (file)
@@ -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
index edd8d66f13702026c806eafa65eca23f0b14bfab..f876b16c8f6b4c47a3c0195b0a80d32c7e5084c3 100644 (file)
@@ -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).
index dfa929476c4b073f22d8617afdf132b570d4ef9c..3c2150273ee85c54276cea8d1af37c9b710afb35 100644 (file)
@@ -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.
index b1a03bad3d7763453b6fbdc84b781575edc0f649..cabf4eb856a0d5b5ba96e03ee2b0d54cca029688 100644 (file)
@@ -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:
index e0340a59071af331e44f1fe931e8f22a81349891..000321e4eae2f992c5458ebdd6ed279dec688cc3 100644 (file)
@@ -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
index 8337e0c8c757f84d471073d4916135bf0b4b1b51..07a291b4351faa3108b25fec3dc9209afabee6e4 100644 (file)
@@ -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.
index 7dc7fe9e590d06e02199e5fe3b457c6d4067a5a9..f79d5eaf3a46e7e9d96244ca6ba51c2c6499ea95 100644 (file)
@@ -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)
+}
index 81a98f7e663882e17938176b6797b0eb63d5d568..38cde80085897f61bb6413ba70a090a083242566 100644 (file)
@@ -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
index 89b8ee07a25d9f71049a7be0281a3cdce61e56c7..f9decbf9a0afc1940352cd43c8cf79ec2d52dea7 100644 (file)
@@ -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) {
index ffad095244cc1ec7aab341f27da1cae7b7f7abe0..50233178ec9e9dbc91677b74b79eeef8c8770f92 100644 (file)
@@ -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)
        }
index 7d6a2aa039852c7e2c4b59e4a2c98775f3c72efe..16f250aee2c602a7aa8487f34a972f9dfb2d2d28 100644 (file)
@@ -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
        }
 
index 0a9074e0af3d0e3494fb50cad7942f23da9f5630..962548a9b0f3064555acc1fc539a984784538020 100644 (file)
@@ -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)
 
index 40a0baedd373b74a9d0ab5afcdc0390e1a5a813f..01bbb08e0efe5573f205756b2c1d450913b4d066 100644 (file)
@@ -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
                        }
                }
index 5530fceef6d9aca01d9eaaeb5df84f53b417c288..01d739878840a18fd4951ffa42a87fc7acb01d87 100644 (file)
@@ -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)
+       }
+}
index 4fcae994f906339c2d230a00c6dc0875a18f042f..e6ad72843743ab2a856006e36bedf34904cb1fbf 100644 (file)
@@ -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 {
index ebf4d8cef7e324b351c75d89ef3f342dc2a1539d..ee01435e77275e57a2cf94f067c38a27703408a9 100644 (file)
@@ -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")
 
index 440fc87891120f0d335a59e4579631f6a6bb818c..fed114edc054a564743bf4b48a6d2d077ceb9a18 100644 (file)
@@ -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) }
 
 // ----------------------------------------------------------------------------
index 41fabfd051897a23d0d233720f76b01c1b6eec71..ef20de4b51b126db8704bd5534060a759f36a045 100644 (file)
@@ -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 {
index 01804c69c51cf3f46f9dcc70a877e1b3ae97fc25..5dc775af8d4c33332a46e7b4365ee0e9bed3b80a 100644 (file)
@@ -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]
index 6397b394d1f3e5910d9d5248c62f4d62223f9e62..8b6a1b4b79e0717021a114ab7484aaf021817acd 100644 (file)
@@ -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
index e34db602f3e6c95f72b2b9faf04e767b79460660..e7a348d8510ee713a7bf7ff77046dcb2d45a9ea1 100644 (file)
@@ -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).
index cab36e4c59869f9c4ba0730cae8faab5b5100a61..6eee1af2a81c00c333bd722416035c8123d13ec3 100644 (file)
@@ -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.
index d35c6d1263798854135ad0e014291f91691e1e51..5bc5a1f3ac72565c0e590f34391ecc87fd62ea74 100644 (file)
@@ -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:
index 3c7b782b5a2d460f62e640cc9f870a169e5e8b32..7f08d9f579c0606cfa5dbf3c5082f178787c8977 100644 (file)
@@ -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
index 0c0562b2873aec58a9ad2d98a9e2f00a243b8e9b..dde5cb039d212d083c5a580a5b828c227aba0a14 100644 (file)
@@ -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.
index a863aa8e3996874d9e373e10141591f923513848..f854de0fda0ec4d924bc06eee36822dd27a24a16 100644 (file)
@@ -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 (file)
index 0000000..96234bf
--- /dev/null
@@ -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
index c47a68d525ec858302315955ee0bc6f6a436231a..7c063904c8a0d9dec9868e1b4ab23b952790eab7 100644 (file)
@@ -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 (
index 6039df951e72ef12dcf60db9eb64240d775ed4bd..2f4d266b8a33ea7c0e29da54d7dab371ea3b46fa 100644 (file)
@@ -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{}
index adbd138f165918ea24db41bffe5771bb312af32f..b0ea98ee89609f97bda9d7a0da0a26ccabfb5b72 100644 (file)
@@ -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)
index 77fd369ff977e2f0e6599c75f16a155c81689b3d..71e727e2ac64152ee484586f0b500ed123cc4f71 100644 (file)
@@ -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" */ () }
index ae70048d9be6db4c948abd65e9d055292a9d5c7f..c42b81d0a00b0bbe7596177053eebea63f9cb5f3 100644 (file)
@@ -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 (file)
index 0000000..f1df59b
--- /dev/null
@@ -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] }
index c516eee2da672f5b7841b281f3de1d7bb62a6b1e..83a1786133f297415b83320f4899b63072a972c8 100644 (file)
@@ -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
index 09ddf53ddf805683d4a91ec046cce9265bc1a663..21f0c09b777593195676189fff95a48816ffe2d9 100644 (file)
@@ -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 (file)
index 0000000..3721788
--- /dev/null
@@ -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
+}
index 6670870b325f00a5bc3f59f77ac24b6bdad738e9..2ce9a4a150134f03556d9ebca7f98c7d69cee795 100644 (file)
@@ -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"