For #76472.
Change-Id: Ia51b41639637b1de916625e73c26f625382be305
Reviewed-on: https://go-review.googlesource.com/c/go/+/736441
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Commit-Queue: Robert Griesemer <gri@google.com>
IgnoreBranchErrors: true, // parser already checked via syntax.CheckBranches mode
Importer: &importer,
Sizes: types2.SizesFor("gc", buildcfg.GOARCH),
- EnableAlias: true,
}
if base.Flag.ErrorURL {
conf.ErrorURL = " [go.dev/e/%s]"
//
// Like a defined ([Named]) type, an alias type has a name.
// Use the [Alias.Obj] method to access its [TypeName] object.
-//
-// Historically, Alias types were not materialized so that, in the example
-// above, A's type was represented by a Basic (int), not an Alias
-// whose [Alias.Rhs] is int. But Go 1.24 allows you to declare an
-// alias type with type parameters or arguments:
-//
-// type Set[K comparable] = map[K]bool
-// s := make(Set[String])
-//
-// and this requires that Alias types be materialized. Use the
-// [Alias.TypeParams] and [Alias.TypeArgs] methods to access them.
-//
-// To ease the transition, the Alias type was introduced in go1.22,
-// but the type-checker would not construct values of this type unless
-// the GODEBUG=gotypesalias=1 environment variable was provided.
-// Starting in go1.23, this variable is enabled by default.
-// This setting also causes the predeclared type "any" to be
-// represented as an Alias, not a bare [Interface].
type Alias struct {
obj *TypeName // corresponding declared alias object
orig *Alias // original, uninstantiated alias
// of an error message. ErrorURL must be a format string containing
// exactly one "%s" format, e.g. "[go.dev/e/%s]".
ErrorURL string
-
- // 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.
- //
- // This setting must not differ among concurrent type-checking operations,
- // since it affects the behavior of Universe.Lookup("any").
- //
- // This flag will eventually be removed (with Go 1.24 at the earliest).
- EnableAlias bool
}
// Info holds result type information for a type-checked package.
// This is a regression test for #66704.
func TestUnaliasTooSoonInCycle(t *testing.T) {
- t.Setenv("GODEBUG", "gotypesalias=1")
const src = `package a
var x T[B] // this appears to cause Unalias to be called on B while still Invalid
type C = int
`
- pkg := mustTypecheck(src, &Config{EnableAlias: true}, nil)
+ pkg := mustTypecheck(src, nil, nil)
A := pkg.Scope().Lookup("A")
got, want := A.Type().(*Alias).Rhs().String(), "p.B"
}
}
-// Test the hijacking described of "any" described in golang/go#66921, for
-// (concurrent) type checking.
-func TestAnyHijacking_Check(t *testing.T) {
- for _, enableAlias := range []bool{false, true} {
- t.Run(fmt.Sprintf("EnableAlias=%t", enableAlias), func(t *testing.T) {
- var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- pkg := mustTypecheck("package p; var x any", &Config{EnableAlias: enableAlias}, nil)
- x := pkg.Scope().Lookup("x")
- if _, gotAlias := x.Type().(*Alias); gotAlias != enableAlias {
- t.Errorf(`Lookup("x").Type() is %T: got Alias: %t, want %t`, x.Type(), gotAlias, enableAlias)
- }
- }()
- }
- wg.Wait()
- })
- }
-}
-
// This test function only exists for go/types.
// func TestVersionIssue69477(t *testing.T)
"go/constant"
. "internal/types/errors"
"os"
- "sync/atomic"
)
// nopos indicates an unknown position
// position tracing for panics during type checking
const tracePos = true
-// _aliasAny changes the behavior of [Scope.Lookup] for "any" in the
-// [Universe] scope.
-//
-// This is necessary because while Alias creation is controlled by
-// [Config.EnableAlias], the representation of "any" is a global. In
-// [Scope.Lookup], we select this global representation based on the result of
-// [aliasAny], but as a result need to guard against this behavior changing
-// during the type checking pass. Therefore we implement the following rule:
-// any number of goroutines can type check concurrently with the same
-// EnableAlias value, but if any goroutine tries to type check concurrently
-// with a different EnableAlias value, we panic.
-//
-// To achieve this, _aliasAny is a state machine:
-//
-// 0: no type checking is occurring
-// negative: type checking is occurring without EnableAlias set
-// positive: type checking is occurring with EnableAlias set
-var _aliasAny int32
-
-func aliasAny() bool {
- return atomic.LoadInt32(&_aliasAny) >= 0 // default true
-}
-
// exprInfo stores information about an untyped expression.
type exprInfo struct {
isLhs bool // expression is lhs operand of a shift with delayed type-check
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)
- }
- check.brokenAliases[alias] = true
- alias.typ = Typ[Invalid]
-}
-
-// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
-func (check *Checker) validAlias(alias *TypeName, typ Type) {
- 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 {
- assert(!check.conf.EnableAlias)
- return check.brokenAliases[alias]
-}
-
func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
m := check.untyped
if m == nil {
// syntax is properly type annotated even in a package containing
// errors.
func (check *Checker) checkFiles(files []*syntax.File) {
- // Ensure that EnableAlias is consistent among concurrent type checking
- // operations. See the documentation of [_aliasAny] for details.
- if check.conf.EnableAlias {
- if atomic.AddInt32(&_aliasAny, 1) <= 0 {
- panic("EnableAlias set while !EnableAlias type checking is ongoing")
- }
- defer atomic.AddInt32(&_aliasAny, -1)
- } else {
- if atomic.AddInt32(&_aliasAny, -1) >= 0 {
- panic("!EnableAlias set while EnableAlias type checking is ongoing")
- }
- defer atomic.AddInt32(&_aliasAny, 1)
- }
-
print := func(msg string) {
if check.conf.Trace {
fmt.Println()
// testFiles type-checks the package consisting of the given files, and
// compares the resulting errors with the ERROR annotations in the source.
-// Except for manual tests, each package is type-checked twice, once without
-// use of Alias types, and once with Alias types.
//
// The srcs slice contains the file content for the files named in the
// filenames slice. The colDelta parameter specifies the tolerance for position
//
// If provided, opts may be used to mutate the Config before type-checking.
func testFiles(t *testing.T, filenames []string, srcs [][]byte, colDelta uint, manual bool, opts ...func(*Config)) {
- enableAlias := true
- opts = append(opts, func(conf *Config) { conf.EnableAlias = enableAlias })
- testFilesImpl(t, filenames, srcs, colDelta, manual, opts...)
- if !manual {
- enableAlias = false
- testFilesImpl(t, filenames, srcs, colDelta, manual, opts...)
- }
-}
-
-func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, colDelta uint, manual bool, opts ...func(*Config)) {
if len(filenames) == 0 {
t.Fatal("no source files")
}
}
// apply flag setting (overrides custom configuration)
- var goexperiment, gotypesalias string
+ var goexperiment string
flags := flag.NewFlagSet("", flag.PanicOnError)
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.StringVar(&goexperiment, "goexperiment", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
- flags.StringVar(&gotypesalias, "gotypesalias", "", "")
if err := parseFlags(srcs[0], flags); err != nil {
t.Fatal(err)
}
defer revert()
}
- // By default, gotypesalias is not set.
- if gotypesalias != "" {
- conf.EnableAlias = gotypesalias != "0"
- }
-
// Provide Config.Info with all maps so that info recording is tested.
info := Info{
Types: make(map[syntax.Expr]TypeAndValue),
tparCycle = true
break loop
}
-
- // Determine if the type name is an alias or not. For
- // package-level objects, use the object map which
- // provides syntactic information (which doesn't rely
- // on the order in which the objects are set up). For
- // local objects, we can rely on the order, so use
- // the object's predicate.
- // TODO(gri) It would be less fragile to always access
- // the syntactic information. We should consider storing
- // this information explicitly in the object.
- var alias bool
- if check.conf.EnableAlias {
- alias = obj.IsAlias()
- } else {
- if d := check.objMap[obj]; d != nil {
- alias = d.tdecl.Alias // package-level object
- } else {
- alias = obj.IsAlias() // function local object
- }
- }
- if !alias {
+ if !obj.IsAlias() {
ndef++
}
case *Func:
obj := cycle[start]
tname, _ := obj.(*TypeName)
if tname != nil {
- if check.conf.EnableAlias {
- if a, ok := tname.Type().(*Alias); ok {
- a.fromRHS = Typ[Invalid]
- }
- } else {
- if tname.IsAlias() {
- check.validAlias(tname, Typ[Invalid])
- }
+ if a, ok := tname.Type().(*Alias); ok {
+ a.fromRHS = Typ[Invalid]
}
}
versionErr = true
}
- if check.conf.EnableAlias {
- alias := check.newAlias(obj, nil)
-
- // If we could not type the RHS, set it to invalid. This should
- // only ever happen if we panic before setting.
- defer func() {
- if alias.fromRHS == nil {
- alias.fromRHS = Typ[Invalid]
- unalias(alias)
- }
- }()
+ alias := check.newAlias(obj, nil)
- // handle type parameters even if not allowed (Alias type is supported)
- if tparam0 != nil {
- check.openScope(tdecl, "type parameters")
- defer check.closeScope()
- check.collectTypeParams(&alias.tparams, tdecl.TParamList)
+ // If we could not type the RHS, set it to invalid. This should
+ // only ever happen if we panic before setting.
+ defer func() {
+ if alias.fromRHS == nil {
+ alias.fromRHS = Typ[Invalid]
+ unalias(alias)
}
+ }()
- rhs = check.declaredType(tdecl.Type, obj)
- assert(rhs != nil)
- alias.fromRHS = rhs
+ // handle type parameters even if not allowed (Alias type is supported)
+ if tparam0 != nil {
+ check.openScope(tdecl, "type parameters")
+ defer check.closeScope()
+ check.collectTypeParams(&alias.tparams, tdecl.TParamList)
+ }
- // spec: In an alias declaration the given type cannot be a type parameter declared in the same declaration."
- // (see also go.dev/issue/75884, go.dev/issue/#75885)
- if tpar, ok := rhs.(*TypeParam); ok && alias.tparams != nil && slices.Index(alias.tparams.list(), tpar) >= 0 {
- check.error(tdecl.Type, MisplacedTypeParam, "cannot use type parameter declared in alias declaration as RHS")
- alias.fromRHS = Typ[Invalid]
- }
- } else {
- if !versionErr && tparam0 != nil {
- check.error(tdecl, UnsupportedFeature, "generic type alias requires GODEBUG=gotypesalias=1 or unset")
- versionErr = true
- }
+ rhs = check.declaredType(tdecl.Type, obj)
+ assert(rhs != nil)
+ alias.fromRHS = rhs
- check.brokenAlias(obj)
- rhs = check.typ(tdecl.Type)
- check.validAlias(obj, rhs)
+ // spec: In an alias declaration the given type cannot be a type parameter declared in the same declaration."
+ // (see also go.dev/issue/75884, go.dev/issue/#75885)
+ if tpar, ok := rhs.(*TypeParam); ok && alias.tparams != nil && slices.Index(alias.tparams.list(), tpar) >= 0 {
+ check.error(tdecl.Type, MisplacedTypeParam, "cannot use type parameter declared in alias declaration as RHS")
+ alias.fromRHS = Typ[Invalid]
}
+
return
}
named := check.newNamed(obj, nil, nil)
+ // TODO: adjust this comment (gotypesalias) as needed if we don't need allowNilRHS anymore.
// The RHS of a named N can be nil if, for example, N is defined as a cycle of aliases with
// gotypesalias=0. Consider:
//
testenv.MustHaveCGO(t)
// Methods declared on aliases of cgo types are not permitted.
- const src = `// -gotypesalias=1
-
+ const src = `
package p
/*
type S struct{ A }
`
- conf := Config{EnableAlias: true}
- pkg := mustTypecheck(src, &conf, nil)
+ pkg := mustTypecheck(src, nil, nil)
S := pkg.Scope().Lookup("S")
if S == nil {
T A
)`
- conf := Config{EnableAlias: true}
- pkg := mustTypecheck(src, &conf, nil)
+ pkg := mustTypecheck(src, nil, nil)
T := pkg.Scope().Lookup("T").(*TypeName)
got := T.String() // this must not panic (was issue)
const want = "type p.T struct{}"
}
if tname.IsAlias() {
buf.WriteString(" =")
- if alias, ok := typ.(*Alias); ok { // materialized? (gotypesalias=1)
+ if alias, ok := typ.(*Alias); ok { // materialized? TODO(gri) Do we still need this (e.g. for byte, rune)?
typ = alias.fromRHS
}
} else if t, _ := typ.(*TypeParam); t != nil {
}
var testObjects = []struct {
- src string
- obj string
- want string
- alias bool // needs materialized (and possibly generic) aliases
+ src string
+ obj string
+ want string
}{
- {"import \"io\"; var r io.Reader", "r", "var p.r io.Reader", false},
-
- {"const c = 1.2", "c", "const p.c untyped float", false},
- {"const c float64 = 3.14", "c", "const p.c float64", false},
-
- {"type t struct{f int}", "t", "type p.t struct{f int}", false},
- {"type t func(int)", "t", "type p.t func(int)", false},
- {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}", false},
- {"type t[P any] struct{f P}", "t.P", "type parameter P any", false},
- {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C", false},
-
- {"type t = struct{f int}", "t", "type p.t = struct{f int}", false},
- {"type t = func(int)", "t", "type p.t = func(int)", false},
- {"type A = B; type B = int", "A", "type p.A = p.B", true},
- {"type A[P ~int] = struct{}", "A", "type p.A[P ~int] = struct{}", true},
- {"var v int", "v", "var p.v int", false},
-
- {"func f(int) string", "f", "func p.f(int) string", false},
- {"func g[P any](x P){}", "g", "func p.g[P any](x P)", false},
- {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}", false},
- {"", "any", "type any = interface{}", false},
+ {"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"},
+
+ {"const c = 1.2", "c", "const p.c untyped float"},
+ {"const c float64 = 3.14", "c", "const p.c float64"},
+
+ {"type t struct{f int}", "t", "type p.t struct{f int}"},
+ {"type t func(int)", "t", "type p.t func(int)"},
+ {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"},
+ {"type t[P any] struct{f P}", "t.P", "type parameter P any"},
+ {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"},
+
+ {"type t = struct{f int}", "t", "type p.t = struct{f int}"},
+ {"type t = func(int)", "t", "type p.t = func(int)"},
+ {"type A = B; type B = int", "A", "type p.A = p.B"},
+ {"type A[P ~int] = struct{}", "A", "type p.A[P ~int] = struct{}"},
+ {"var v int", "v", "var p.v int"},
+
+ {"func f(int) string", "f", "func p.f(int) string"},
+ {"func g[P any](x P){}", "g", "func p.g[P any](x P)"},
+ {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"},
+ {"", "any", "type any = interface{}"},
}
func TestObjectString(t *testing.T) {
for i, test := range testObjects {
t.Run(fmt.Sprint(i), func(t *testing.T) {
src := "package p; " + test.src
- conf := Config{Error: func(error) {}, Importer: defaultImporter(), EnableAlias: test.alias}
- pkg, err := typecheck(src, &conf, nil)
+ pkg, err := typecheck(src, nil, nil)
if err != nil {
t.Fatalf("%s: %s", src, err)
}
}
}
- if false && check.conf.EnableAlias {
- // With Alias nodes we can process declarations in any order.
+ if false {
+ // TODO: determine if we can enable this code now or
+ // if there are still problems with cycles and
+ // aliases.
//
- // TODO(adonovan): unfortunately, Alias nodes
- // (GODEBUG=gotypesalias=1) don't entirely resolve
- // problems with cycles. For example, in
- // GOROOT/test/typeparam/issue50259.go,
+ // For example, in GOROOT/test/typeparam/issue50259.go,
//
// type T[_ any] struct{}
// type A T[B]
check.objDecl(obj)
}
} else {
- // Without Alias nodes, we process non-alias type declarations first, followed by
+ // To avoid problems with cycles, 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).
// Lookup returns the object in scope s with the given name if such an
// object exists; otherwise the result is nil.
-func (s *Scope) Lookup(name string) Object {
- obj := resolve(name, s.elems[name])
- // Hijack Lookup for "any": with gotypesalias=1, we want the Universe to
- // return an Alias for "any", and with gotypesalias=0 we want to return
- // the legacy representation of aliases.
- //
- // This is rather tricky, but works out after auditing of the usage of
- // s.elems. The only external API to access scope elements is Lookup.
- //
- // TODO: remove this once gotypesalias=0 is no longer supported.
- if obj == universeAnyAlias && !aliasAny() {
- return universeAnyNoAlias
- }
- return obj
-}
+func (s *Scope) Lookup(name string) Object { return resolve(name, s.elems[name]) }
// lookupIgnoringCase returns the objects in scope s whose names match
// the given name ignoring case. If exported is set, only exported names
"issue49814.go", // go/types does not have constraints on array size
"issue56103.go", // anonymous interface cycles; will be a type checker error in 1.22
"issue52697.go", // types2 does not have constraints on stack size
- "issue68054.go", // this test requires GODEBUG=gotypesalias=1
- "issue68580.go", // this test requires GODEBUG=gotypesalias=1
- "issue73309.go", // this test requires GODEBUG=gotypesalias=1
- "issue73309b.go", // this test requires GODEBUG=gotypesalias=1
// These tests requires runtime/cgo.Incomplete, which is only available on some platforms.
// However, types2 does not know about build constraints.
Error: func(err error) {
errs = append(errs, err)
},
- Importer: importer,
- EnableAlias: true,
+ Importer: importer,
}
info := Info{Uses: make(map[*syntax.Name]Object)}
pkg, _ := conf.Check(path, files, &info)
case *Interface:
if w.ctxt == nil {
- if t == universeAnyAlias.Type().Underlying() {
- // When not hashing, we can try to improve type strings by writing "any"
- // for a type that is pointer-identical to universeAny.
- // TODO(rfindley): this logic should not be necessary with
- // gotypesalias=1. Remove once that is always the case.
- w.string("any")
- break
- }
if t == asNamed(universeComparable.Type()).underlying {
w.string("interface{comparable}")
break
dup(`interface{String() string; m(int) float32}`),
dup("interface{int | float32 | complex128}"),
dup("interface{int | ~float32 | ~complex128}"),
- dup("any"),
dup("interface{comparable}"),
- {"comparable", "interface{comparable}"},
{"error", "interface{Error() string}"},
+ {"any", "interface{}"},
+ {"comparable", "interface{comparable}"},
// maps
dup("map[string]int"),
check.errorf(e, UndeclaredName, "undefined: %s", e.Value)
}
return
- case universeComparable:
- if !check.verifyVersionf(e, go1_18, "predeclared %s", e.Value) {
- return // avoid follow-on errors
- }
- }
- // Because the representation of any depends on gotypesalias, we don't check
- // pointer identity here.
- if obj.Name() == "any" && obj.Parent() == Universe {
+ case universeAny, universeComparable:
if !check.verifyVersionf(e, go1_18, "predeclared %s", e.Value) {
return // avoid follow-on errors
}
x.mode = constant_
case *TypeName:
- 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
- }
x.mode = typexpr
case *Var:
universeBool Type
universeByte Type // uint8 alias, but has name "byte"
universeRune Type // int32 alias, but has name "rune"
- universeAnyNoAlias *TypeName
- universeAnyAlias *TypeName
universeError Type
+ universeAny Object
universeComparable Object
)
for _, t := range Typ {
def(NewTypeName(nopos, nil, t.name, t))
}
+
for _, t := range basicAliases {
def(NewTypeName(nopos, nil, t.name, t))
}
- // type any = interface{}
- //
- // Implement two representations of any: one for the legacy gotypesalias=0,
- // and one for gotypesalias=1. This is necessary for consistent
- // representation of interface aliases during type checking, and is
- // implemented via hijacking [Scope.Lookup] for the [Universe] scope.
- //
- // Both representations use the same distinguished pointer for their RHS
- // interface type, allowing us to detect any (even with the legacy
- // representation), and format it as "any" rather than interface{}, which
- // clarifies user-facing error messages significantly.
- //
- // TODO(rfindley): once the gotypesalias GODEBUG variable is obsolete (and we
- // consistently use the Alias node), we should be able to clarify user facing
- // error messages without using a distinguished pointer for the any
- // interface.
- {
- universeAnyNoAlias = NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})
- // ensure that the any TypeName reports a consistent Parent, after
- // hijacking Universe.Lookup with gotypesalias=0.
- universeAnyNoAlias.setParent(Universe)
-
- // It shouldn't matter which representation of any is actually inserted
- // into the Universe, but we lean toward the future and insert the Alias
- // representation.
- universeAnyAlias = NewTypeName(nopos, nil, "any", nil)
- _ = NewAlias(universeAnyAlias, universeAnyNoAlias.Type().Underlying()) // Link TypeName and Alias
- def(universeAnyAlias)
- }
-
// type error interface{ Error() string }
{
obj := NewTypeName(nopos, nil, "error", nil)
- typ := (*Checker)(nil).newNamed(obj, nil, nil)
+ typ := NewNamed(obj, nil, nil)
// error.Error() string
recv := newVar(RecvVar, nopos, nil, "", typ)
def(obj)
}
+ // type any = interface{}
+ {
+ obj := NewTypeName(nopos, nil, "any", nil)
+ NewAlias(obj, &emptyInterface)
+ def(obj)
+ }
+
// type comparable interface{} // marked as comparable
{
obj := NewTypeName(nopos, nil, "comparable", nil)
- typ := (*Checker)(nil).newNamed(obj, nil, nil)
-
- // interface{} // marked as comparable
- ityp := &Interface{complete: true, tset: &_TypeSet{nil, allTermlist, true}}
-
- typ.fromRHS = ityp
- typ.Underlying()
+ NewNamed(obj, &Interface{complete: true, tset: &_TypeSet{nil, allTermlist, true}}, nil)
def(obj)
}
}
universeByte = Universe.Lookup("byte").Type()
universeRune = Universe.Lookup("rune").Type()
universeError = Universe.Lookup("error").Type()
+ universeAny = Universe.Lookup("any")
universeComparable = Universe.Lookup("comparable")
}
import (
"go/token"
"go/types"
- "internal/godebug"
"internal/pkgbits"
"slices"
"strings"
// newAliasTypeName returns a new TypeName, with a materialized *types.Alias if supported.
func newAliasTypeName(pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName {
- // When GODEBUG=gotypesalias=1 or unset, the Type() of the return value is a
- // *types.Alias. Copied from x/tools/internal/aliases.NewAlias.
- switch godebug.New("gotypesalias").Value() {
- case "", "1":
- tname := types.NewTypeName(pos, pkg, name, nil)
- a := types.NewAlias(tname, rhs) // form TypeName -> Alias cycle
- a.SetTypeParams(tparams)
- return tname
- }
- assert(len(tparams) == 0)
- return types.NewTypeName(pos, pkg, name, rhs)
+ tname := types.NewTypeName(pos, pkg, name, nil)
+ a := types.NewAlias(tname, rhs) // form TypeName -> Alias cycle
+ a.SetTypeParams(tparams)
+ return tname
}
//
// Like a defined ([Named]) type, an alias type has a name.
// Use the [Alias.Obj] method to access its [TypeName] object.
-//
-// Historically, Alias types were not materialized so that, in the example
-// above, A's type was represented by a Basic (int), not an Alias
-// whose [Alias.Rhs] is int. But Go 1.24 allows you to declare an
-// alias type with type parameters or arguments:
-//
-// type Set[K comparable] = map[K]bool
-// s := make(Set[String])
-//
-// and this requires that Alias types be materialized. Use the
-// [Alias.TypeParams] and [Alias.TypeArgs] methods to access them.
-//
-// To ease the transition, the Alias type was introduced in go1.22,
-// but the type-checker would not construct values of this type unless
-// the GODEBUG=gotypesalias=1 environment variable was provided.
-// Starting in go1.23, this variable is enabled by default.
-// This setting also causes the predeclared type "any" to be
-// represented as an Alias, not a bare [Interface].
type Alias struct {
obj *TypeName // corresponding declared alias object
orig *Alias // original, uninstantiated alias
)
func TestIssue74181(t *testing.T) {
- t.Setenv("GODEBUG", "gotypesalias=1")
-
src := `package p
type AB = A[B]
}
func TestPartialTypeCheckUndeclaredAliasPanic(t *testing.T) {
- t.Setenv("GODEBUG", "gotypesalias=1")
-
src := `package p
type A = B // undeclared`
// of an error message. ErrorURL must be a format string containing
// exactly one "%s" format, e.g. "[go.dev/e/%s]".
_ErrorURL string
-
- // 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.
- //
- // This setting must not differ among concurrent type-checking operations,
- // since it affects the behavior of Universe.Lookup("any").
- //
- // This flag will eventually be removed (with Go 1.24 at the earliest).
- _EnableAlias bool
}
// Linkname for use from srcimporter.
// This is a regression test for #66704.
func TestUnaliasTooSoonInCycle(t *testing.T) {
- setGotypesalias(t, true)
const src = `package a
var x T[B] // this appears to cause Unalias to be called on B while still Invalid
}
func TestAlias_Rhs(t *testing.T) {
- setGotypesalias(t, true)
const src = `package p
type A = B
}
}
-// Test the hijacking described of "any" described in golang/go#66921, for type
-// checking.
-func TestAnyHijacking_Check(t *testing.T) {
- for _, enableAlias := range []bool{false, true} {
- t.Run(fmt.Sprintf("EnableAlias=%t", enableAlias), func(t *testing.T) {
- setGotypesalias(t, enableAlias)
- var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- pkg := mustTypecheck("package p; var x any", nil, nil)
- x := pkg.Scope().Lookup("x")
- if _, gotAlias := x.Type().(*Alias); gotAlias != enableAlias {
- t.Errorf(`Lookup("x").Type() is %T: got Alias: %t, want %t`, x.Type(), gotAlias, enableAlias)
- }
- }()
- }
- wg.Wait()
- })
- }
-}
-
-// Test the hijacking described of "any" described in golang/go#66921, for
-// Scope.Lookup outside of type checking.
-func TestAnyHijacking_Lookup(t *testing.T) {
- for _, enableAlias := range []bool{false, true} {
- t.Run(fmt.Sprintf("EnableAlias=%t", enableAlias), func(t *testing.T) {
- setGotypesalias(t, enableAlias)
- a := Universe.Lookup("any")
- if _, gotAlias := a.Type().(*Alias); gotAlias != enableAlias {
- t.Errorf(`Lookup("x").Type() is %T: got Alias: %t, want %t`, a.Type(), gotAlias, enableAlias)
- }
- })
- }
-}
-
-func setGotypesalias(t *testing.T, enable bool) {
- if enable {
- t.Setenv("GODEBUG", "gotypesalias=1")
- } else {
- t.Setenv("GODEBUG", "gotypesalias=0")
- }
-}
-
// TestVersionIssue69477 is a regression test for issue #69477,
// in which the type checker would panic while attempting
// to compute which file it is "in" based on syntax position.
"go/ast"
"go/constant"
"go/token"
- "internal/godebug"
. "internal/types/errors"
"os"
- "sync/atomic"
)
// nopos, noposn indicate an unknown position
// position tracing for panics during type checking
const tracePos = true
-// gotypesalias controls the use of Alias types.
-// As of Apr 16 2024 they are used by default.
-// To disable their use, set GODEBUG to gotypesalias=0.
-// This GODEBUG flag will be removed in the near future (tentatively Go 1.24).
-var gotypesalias = godebug.New("gotypesalias")
-
-// _aliasAny changes the behavior of [Scope.Lookup] for "any" in the
-// [Universe] scope.
-//
-// This is necessary because while Alias creation is controlled by
-// [Config._EnableAlias], based on the gotypealias variable, the representation
-// of "any" is a global. In [Scope.Lookup], we select this global
-// representation based on the result of [aliasAny], but as a result need to
-// guard against this behavior changing during the type checking pass.
-// Therefore we implement the following rule: any number of goroutines can type
-// check concurrently with the same EnableAlias value, but if any goroutine
-// tries to type check concurrently with a different EnableAlias value, we
-// panic.
-//
-// To achieve this, _aliasAny is a state machine:
-//
-// 0: no type checking is occurring
-// negative: type checking is occurring without _EnableAlias set
-// positive: type checking is occurring with _EnableAlias set
-var _aliasAny int32
-
-func aliasAny() bool {
- v := gotypesalias.Value()
- useAlias := v != "0"
- inuse := atomic.LoadInt32(&_aliasAny)
- if inuse != 0 && useAlias != (inuse > 0) {
- panic(fmt.Sprintf("gotypealias mutated during type checking, gotypesalias=%s, inuse=%d", v, inuse))
- }
- return useAlias
-}
-
// exprInfo stores information about an untyped expression.
type exprInfo struct {
isLhs bool // expression is lhs operand of a shift with delayed type-check
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)
- }
- check.brokenAliases[alias] = true
- alias.typ = Typ[Invalid]
-}
-
-// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
-func (check *Checker) validAlias(alias *TypeName, typ Type) {
- 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 {
- assert(!check.conf._EnableAlias)
- return check.brokenAliases[alias]
-}
-
func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
m := check.untyped
if m == nil {
//
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
- // In go/types, conf._EnableAlias is controlled by gotypesalias.
- conf._EnableAlias = gotypesalias.Value() != "0"
-
return &Checker{
conf: conf,
ctxt: conf.Context,
// syntax is properly type annotated even in a package containing
// errors.
func (check *Checker) checkFiles(files []*ast.File) {
- // Ensure that _EnableAlias is consistent among concurrent type checking
- // operations. See the documentation of [_aliasAny] for details.
- if check.conf._EnableAlias {
- if atomic.AddInt32(&_aliasAny, 1) <= 0 {
- panic("EnableAlias set while !EnableAlias type checking is ongoing")
- }
- defer atomic.AddInt32(&_aliasAny, -1)
- } else {
- if atomic.AddInt32(&_aliasAny, -1) >= 0 {
- panic("!EnableAlias set while EnableAlias type checking is ongoing")
- }
- defer atomic.AddInt32(&_aliasAny, 1)
- }
-
print := func(msg string) {
if check.conf._Trace {
fmt.Println()
// testFiles type-checks the package consisting of the given files, and
// compares the resulting errors with the ERROR annotations in the source.
-// Except for manual tests, each package is type-checked twice, once without
-// use of Alias types, and once with Alias types.
//
// The srcs slice contains the file content for the files named in the
// filenames slice. The colDelta parameter specifies the tolerance for position
//
// If provided, opts may be used to mutate the Config before type-checking.
func testFiles(t *testing.T, filenames []string, srcs [][]byte, manual bool, opts ...func(*Config)) {
- // Alias types are enabled by default
- testFilesImpl(t, filenames, srcs, manual, opts...)
- if !manual {
- t.Setenv("GODEBUG", "gotypesalias=0")
- testFilesImpl(t, filenames, srcs, manual, opts...)
- }
-}
-
-func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, manual bool, opts ...func(*Config)) {
if len(filenames) == 0 {
t.Fatal("no source files")
}
}
// apply flag setting (overrides custom configuration)
- var goexperiment, gotypesalias string
+ var goexperiment string
flags := flag.NewFlagSet("", flag.PanicOnError)
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.StringVar(&goexperiment, "goexperiment", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
- flags.StringVar(&gotypesalias, "gotypesalias", "", "")
if err := parseFlags(srcs[0], flags); err != nil {
t.Fatal(err)
}
defer revert()
}
- // By default, gotypesalias is not set.
- if gotypesalias != "" {
- t.Setenv("GODEBUG", "gotypesalias="+gotypesalias)
- }
-
// Provide Config.Info with all maps so that info recording is tested.
info := Info{
Types: make(map[ast.Expr]TypeAndValue),
break loop
}
- // Determine if the type name is an alias or not. For
- // package-level objects, use the object map which
- // provides syntactic information (which doesn't rely
- // on the order in which the objects are set up). For
- // local objects, we can rely on the order, so use
- // the object's predicate.
- // TODO(gri) It would be less fragile to always access
- // the syntactic information. We should consider storing
- // this information explicitly in the object.
- var alias bool
- if check.conf._EnableAlias {
- alias = obj.IsAlias()
- } else {
- if d := check.objMap[obj]; d != nil {
- alias = d.tdecl.Assign.IsValid() // package-level object
- } else {
- alias = obj.IsAlias() // function local object
- }
- }
- if !alias {
+ if !obj.IsAlias() {
ndef++
}
case *Func:
obj := cycle[start]
tname, _ := obj.(*TypeName)
if tname != nil {
- if check.conf._EnableAlias {
- if a, ok := tname.Type().(*Alias); ok {
- a.fromRHS = Typ[Invalid]
- }
- } else {
- if tname.IsAlias() {
- check.validAlias(tname, Typ[Invalid])
- }
+ if a, ok := tname.Type().(*Alias); ok {
+ a.fromRHS = Typ[Invalid]
}
}
versionErr = true
}
- if check.conf._EnableAlias {
- alias := check.newAlias(obj, nil)
-
- // If we could not type the RHS, set it to invalid. This should
- // only ever happen if we panic before setting.
- defer func() {
- if alias.fromRHS == nil {
- alias.fromRHS = Typ[Invalid]
- unalias(alias)
- }
- }()
+ alias := check.newAlias(obj, nil)
- // handle type parameters even if not allowed (Alias type is supported)
- if tparam0 != nil {
- check.openScope(tdecl, "type parameters")
- defer check.closeScope()
- check.collectTypeParams(&alias.tparams, tdecl.TypeParams)
+ // If we could not type the RHS, set it to invalid. This should
+ // only ever happen if we panic before setting.
+ defer func() {
+ if alias.fromRHS == nil {
+ alias.fromRHS = Typ[Invalid]
+ unalias(alias)
}
+ }()
- rhs = check.declaredType(tdecl.Type, obj)
- assert(rhs != nil)
- alias.fromRHS = rhs
+ // handle type parameters even if not allowed (Alias type is supported)
+ if tparam0 != nil {
+ check.openScope(tdecl, "type parameters")
+ defer check.closeScope()
+ check.collectTypeParams(&alias.tparams, tdecl.TypeParams)
+ }
- // spec: In an alias declaration the given type cannot be a type parameter declared in the same declaration."
- // (see also go.dev/issue/75884, go.dev/issue/#75885)
- if tpar, ok := rhs.(*TypeParam); ok && alias.tparams != nil && slices.Index(alias.tparams.list(), tpar) >= 0 {
- check.error(tdecl.Type, MisplacedTypeParam, "cannot use type parameter declared in alias declaration as RHS")
- alias.fromRHS = Typ[Invalid]
- }
- } else {
- // With Go1.23, the default behavior is to use Alias nodes,
- // reflected by check.enableAlias. Signal non-default behavior.
- //
- // TODO(gri) Testing runs tests in both modes. Do we need to exclude
- // tracking of non-default behavior for tests?
- gotypesalias.IncNonDefault()
-
- if !versionErr && tparam0 != nil {
- check.error(tdecl, UnsupportedFeature, "generic type alias requires GODEBUG=gotypesalias=1 or unset")
- versionErr = true
- }
+ rhs = check.declaredType(tdecl.Type, obj)
+ assert(rhs != nil)
+ alias.fromRHS = rhs
- check.brokenAlias(obj)
- rhs = check.typ(tdecl.Type)
- check.validAlias(obj, rhs)
+ // spec: In an alias declaration the given type cannot be a type parameter declared in the same declaration."
+ // (see also go.dev/issue/75884, go.dev/issue/#75885)
+ if tpar, ok := rhs.(*TypeParam); ok && alias.tparams != nil && slices.Index(alias.tparams.list(), tpar) >= 0 {
+ check.error(tdecl.Type, MisplacedTypeParam, "cannot use type parameter declared in alias declaration as RHS")
+ alias.fromRHS = Typ[Invalid]
}
+
return
}
"go/parser"
"go/token"
"go/types"
- "internal/godebug"
"internal/testenv"
"strings"
"testing"
t.Fatalf("could not parse file %d: %s", i, err)
}
- // Materialized aliases give a different (better)
- // result for the final test, so skip it for now.
- // TODO(adonovan): reenable when gotypesalias=1 is the default.
- switch gotypesalias.Value() {
- case "", "1":
- if strings.Contains(src, "interface{R}.Read") {
- continue
- }
+ if strings.Contains(src, "interface{R}.Read") {
+ continue
}
files = append(files, file)
}
}
-// gotypesalias controls the use of Alias types.
-var gotypesalias = godebug.New("gotypesalias")
-
// split splits string s at the first occurrence of s, trimming spaces.
func split(s, sep string) (string, string) {
before, after, _ := strings.Cut(s, sep)
testenv.MustHaveCGO(t)
// Methods declared on aliases of cgo types are not permitted.
- const src = `// -gotypesalias=1
-
+ const src = `
package p
/*
type S struct{ A }
`
- t.Setenv("GODEBUG", "gotypesalias=1")
pkg := mustTypecheck(src, nil, nil)
S := pkg.Scope().Lookup("S")
T A
)`
- t.Setenv("GODEBUG", "gotypesalias=1")
pkg := mustTypecheck(src, nil, nil)
T := pkg.Scope().Lookup("T").(*TypeName)
got := T.String() // this must not panic (was issue)
}
if tname.IsAlias() {
buf.WriteString(" =")
- if alias, ok := typ.(*Alias); ok { // materialized? (gotypesalias=1)
+ if alias, ok := typ.(*Alias); ok { // materialized? TODO(gri) Do we still need this (e.g. for byte, rune)?
typ = alias.fromRHS
}
} else if t, _ := typ.(*TypeParam); t != nil {
}
var testObjects = []struct {
- src string
- obj string
- want string
- alias bool // needs materialized (and possibly generic) aliases
+ src string
+ obj string
+ want string
}{
- {"import \"io\"; var r io.Reader", "r", "var p.r io.Reader", false},
-
- {"const c = 1.2", "c", "const p.c untyped float", false},
- {"const c float64 = 3.14", "c", "const p.c float64", false},
-
- {"type t struct{f int}", "t", "type p.t struct{f int}", false},
- {"type t func(int)", "t", "type p.t func(int)", false},
- {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}", false},
- {"type t[P any] struct{f P}", "t.P", "type parameter P any", false},
- {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C", false},
-
- {"type t = struct{f int}", "t", "type p.t = struct{f int}", false},
- {"type t = func(int)", "t", "type p.t = func(int)", false},
- {"type A = B; type B = int", "A", "type p.A = p.B", true},
- {"type A[P ~int] = struct{}", "A", "type p.A[P ~int] = struct{}", true},
- {"var v int", "v", "var p.v int", false},
-
- {"func f(int) string", "f", "func p.f(int) string", false},
- {"func g[P any](x P){}", "g", "func p.g[P any](x P)", false},
- {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}", false},
- {"", "any", "type any = interface{}", false},
+ {"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"},
+
+ {"const c = 1.2", "c", "const p.c untyped float"},
+ {"const c float64 = 3.14", "c", "const p.c float64"},
+
+ {"type t struct{f int}", "t", "type p.t struct{f int}"},
+ {"type t func(int)", "t", "type p.t func(int)"},
+ {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"},
+ {"type t[P any] struct{f P}", "t.P", "type parameter P any"},
+ {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"},
+
+ {"type t = struct{f int}", "t", "type p.t = struct{f int}"},
+ {"type t = func(int)", "t", "type p.t = func(int)"},
+ {"type A = B; type B = int", "A", "type p.A = p.B"},
+ {"type A[P ~int] = struct{}", "A", "type p.A[P ~int] = struct{}"},
+ {"var v int", "v", "var p.v int"},
+
+ {"func f(int) string", "f", "func p.f(int) string"},
+ {"func g[P any](x P){}", "g", "func p.g[P any](x P)"},
+ {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"},
+ {"", "any", "type any = interface{}"},
}
func TestObjectString(t *testing.T) {
for i, test := range testObjects {
t.Run(fmt.Sprint(i), func(t *testing.T) {
- if test.alias {
- t.Setenv("GODEBUG", "gotypesalias=1")
- }
-
src := "package p; " + test.src
pkg, err := typecheck(src, nil, nil)
if err != nil {
}
}
- if false && check.conf._EnableAlias {
- // With Alias nodes we can process declarations in any order.
+ if false {
+ // TODO: determine if we can enable this code now or
+ // if there are still problems with cycles and
+ // aliases.
//
- // TODO(adonovan): unfortunately, Alias nodes
- // (GODEBUG=gotypesalias=1) don't entirely resolve
- // problems with cycles. For example, in
- // GOROOT/test/typeparam/issue50259.go,
+ // For example, in GOROOT/test/typeparam/issue50259.go,
//
// type T[_ any] struct{}
// type A T[B]
check.objDecl(obj)
}
} else {
- // Without Alias nodes, we process non-alias type declarations first, followed by
+ // To avoid problems with cycles, 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).
// Lookup returns the object in scope s with the given name if such an
// object exists; otherwise the result is nil.
-func (s *Scope) Lookup(name string) Object {
- obj := resolve(name, s.elems[name])
- // Hijack Lookup for "any": with gotypesalias=1, we want the Universe to
- // return an Alias for "any", and with gotypesalias=0 we want to return
- // the legacy representation of aliases.
- //
- // This is rather tricky, but works out after auditing of the usage of
- // s.elems. The only external API to access scope elements is Lookup.
- //
- // TODO: remove this once gotypesalias=0 is no longer supported.
- if obj == universeAnyAlias && !aliasAny() {
- return universeAnyNoAlias
- }
- return obj
-}
+func (s *Scope) Lookup(name string) Object { return resolve(name, s.elems[name]) }
// lookupIgnoringCase returns the objects in scope s whose names match
// the given name ignoring case. If exported is set, only exported names
"issue49814.go", // go/types does not have constraints on array size
"issue56103.go", // anonymous interface cycles; will be a type checker error in 1.22
"issue52697.go", // go/types does not have constraints on stack size
- "issue68054.go", // this test requires GODEBUG=gotypesalias=1
- "issue68580.go", // this test requires GODEBUG=gotypesalias=1
- "issue73309.go", // this test requires GODEBUG=gotypesalias=1
- "issue73309b.go", // this test requires GODEBUG=gotypesalias=1
// These tests requires runtime/cgo.Incomplete, which is only available on some platforms.
// However, go/types does not know about build constraints.
case *Interface:
if w.ctxt == nil {
- if t == universeAnyAlias.Type().Underlying() {
- // When not hashing, we can try to improve type strings by writing "any"
- // for a type that is pointer-identical to universeAny.
- // TODO(rfindley): this logic should not be necessary with
- // gotypesalias=1. Remove once that is always the case.
- w.string("any")
- break
- }
if t == asNamed(universeComparable.Type()).underlying {
w.string("interface{comparable}")
break
dup(`interface{String() string; m(int) float32}`),
dup("interface{int | float32 | complex128}"),
dup("interface{int | ~float32 | ~complex128}"),
- dup("any"),
dup("interface{comparable}"),
// TODO(gri) adjust test for EvalCompositeTest
- // {"comparable", "interface{comparable}"},
// {"error", "interface{Error() string}"},
+ // {"any", "interface{}"},
+ // {"comparable", "interface{comparable}"},
// maps
dup("map[string]int"),
check.errorf(e, UndeclaredName, "undefined: %s", e.Name)
}
return
- case universeComparable:
- if !check.verifyVersionf(e, go1_18, "predeclared %s", e.Name) {
- return // avoid follow-on errors
- }
- }
- // Because the representation of any depends on gotypesalias, we don't check
- // pointer identity here.
- if obj.Name() == "any" && obj.Parent() == Universe {
+ case universeAny, universeComparable:
if !check.verifyVersionf(e, go1_18, "predeclared %s", e.Name) {
return // avoid follow-on errors
}
x.mode = constant_
case *TypeName:
- 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
- }
x.mode = typexpr
case *Var:
universeBool Type
universeByte Type // uint8 alias, but has name "byte"
universeRune Type // int32 alias, but has name "rune"
- universeAnyNoAlias *TypeName
- universeAnyAlias *TypeName
universeError Type
+ universeAny Object
universeComparable Object
)
for _, t := range Typ {
def(NewTypeName(nopos, nil, t.name, t))
}
+
for _, t := range basicAliases {
def(NewTypeName(nopos, nil, t.name, t))
}
- // type any = interface{}
- //
- // Implement two representations of any: one for the legacy gotypesalias=0,
- // and one for gotypesalias=1. This is necessary for consistent
- // representation of interface aliases during type checking, and is
- // implemented via hijacking [Scope.Lookup] for the [Universe] scope.
- //
- // Both representations use the same distinguished pointer for their RHS
- // interface type, allowing us to detect any (even with the legacy
- // representation), and format it as "any" rather than interface{}, which
- // clarifies user-facing error messages significantly.
- //
- // TODO(rfindley): once the gotypesalias GODEBUG variable is obsolete (and we
- // consistently use the Alias node), we should be able to clarify user facing
- // error messages without using a distinguished pointer for the any
- // interface.
- {
- universeAnyNoAlias = NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})
- // ensure that the any TypeName reports a consistent Parent, after
- // hijacking Universe.Lookup with gotypesalias=0.
- universeAnyNoAlias.setParent(Universe)
-
- // It shouldn't matter which representation of any is actually inserted
- // into the Universe, but we lean toward the future and insert the Alias
- // representation.
- universeAnyAlias = NewTypeName(nopos, nil, "any", nil)
- _ = NewAlias(universeAnyAlias, universeAnyNoAlias.Type().Underlying()) // Link TypeName and Alias
- def(universeAnyAlias)
- }
-
// type error interface{ Error() string }
{
obj := NewTypeName(nopos, nil, "error", nil)
- typ := (*Checker)(nil).newNamed(obj, nil, nil)
+ typ := NewNamed(obj, nil, nil)
// error.Error() string
recv := newVar(RecvVar, nopos, nil, "", typ)
def(obj)
}
+ // type any = interface{}
+ {
+ obj := NewTypeName(nopos, nil, "any", nil)
+ NewAlias(obj, &emptyInterface)
+ def(obj)
+ }
+
// type comparable interface{} // marked as comparable
{
obj := NewTypeName(nopos, nil, "comparable", nil)
- typ := (*Checker)(nil).newNamed(obj, nil, nil)
-
- // interface{} // marked as comparable
- ityp := &Interface{complete: true, tset: &_TypeSet{nil, allTermlist, true}}
-
- typ.fromRHS = ityp
- typ.Underlying()
+ NewNamed(obj, &Interface{complete: true, tset: &_TypeSet{nil, allTermlist, true}}, nil)
def(obj)
}
}
universeByte = Universe.Lookup("byte").Type()
universeRune = Universe.Lookup("rune").Type()
universeError = Universe.Lookup("error").Type()
+ universeAny = Universe.Lookup("any")
universeComparable = Universe.Lookup("comparable")
}
{Name: "gocachetest", Package: "cmd/go"},
{Name: "gocacheverify", Package: "cmd/go"},
{Name: "gotestjsonbuildtext", Package: "cmd/go", Changed: 24, Old: "1"},
- {Name: "gotypesalias", Package: "go/types", Changed: 23, Old: "0"},
{Name: "http2client", Package: "net/http"},
{Name: "http2debug", Package: "net/http", Opaque: true},
{Name: "http2server", Package: "net/http"},
// Removed contains all GODEBUGs that we have removed.
var Removed = []RemovedInfo{
{Name: "x509sha1", Removed: 24},
+ {Name: "gotypesalias", Removed: 27},
}
// Lookup returns the Info with the given name.
-// -gotypesalias=0
-
// 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.
type (
a struct{ *b }
b = c
- c struct{ *b /* ERROR "invalid use of type alias" */ }
+ c struct{ *b }
)
// issue #24939
}
M interface {
- F() P // ERROR "invalid use of type alias"
+ F() P
}
P = interface {
+++ /dev/null
-// -gotypesalias=1
-
-// 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 = newReader()
-func newReader() r // ERROR "r (package-level variable) is not a type"
-
-// 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
-// -gotypesalias=0
-
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+++ /dev/null
-// -gotypesalias=1
-
-// 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[U interface{ M() T[U] }] int
-
-type X int
-
-func (X) M() T[X] { return 0 }
-
-// test case 2
-type A[T interface{ A[T] }] interface{}
-
-// test case 3
-type A2[U interface{ A2[U] }] interface{ M() A2[U] }
-
-type I interface{ A2[I]; M() A2[I] }
-// -gotypesalias=1
-
// 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.
-// -gotypesalias=1
-
// Copyright 2023 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.
-// -gotypesalias=0
-
// 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.
type SR = R[SS, ST]
type SS interface {
- NSR(any) *SR // ERROR "invalid use of type alias SR in recursive type"
+ NSR(any) *SR
}
type C interface {
+++ /dev/null
-// -gotypesalias=1
-
-// 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
-}
-// -gotypesalias=1
-
// Copyright 2024 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.
-// -gotypesalias=1
-
// Copyright 2024 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.
-// -gotypesalias=1
-
// Copyright 2024 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.
-// -gotypesalias=1
-
// Copyright 2024 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.
-// -gotypesalias=1
-
// Copyright 2024 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.
-// -gotypesalias=1
-
// Copyright 2024 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.
-// -gotypesalias=1
-
// Copyright 2025 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.
-// -gotypesalias=1
-
// Copyright 2025 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.
-// -gotypesalias=1
-
// Copyright 2024 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.
-// -lang=go1.23 -gotypesalias=1
+// -lang=go1.23
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
+++ /dev/null
-// -lang=go1.23 -gotypesalias=0
-
-// Copyright 2024 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 aliasTypes
-
-type _ = int
-type _ /* ERROR "generic type alias requires GODEBUG=gotypesalias=1" */ [P any] = int
package due to a non-default GODEBUG=gotestjsonbuildtext=...
setting.
- /godebug/non-default-behavior/gotypesalias:events
- The number of non-default behaviors executed by the go/types
- package due to a non-default GODEBUG=gotypesalias=... setting.
-
/godebug/non-default-behavior/http2client:events
The number of non-default behaviors executed by the net/http
package due to a non-default GODEBUG=http2client=... setting.