mirror of
https://github.com/golang/go.git
synced 2026-02-06 18:00:01 +00:00
go/types, types2: remove support for gotypesalias GODEBUG flag
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>
This commit is contained in:
parent
819c53c25f
commit
6edb9f9c51
57 changed files with 161 additions and 1008 deletions
|
|
@ -56,7 +56,6 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info, map[*
|
|||
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]"
|
||||
|
|
|
|||
|
|
@ -21,24 +21,6 @@ import (
|
|||
//
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -175,16 +175,6 @@ type Config struct {
|
|||
// 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.
|
||||
|
|
|
|||
|
|
@ -2979,7 +2979,6 @@ func TestTooNew(t *testing.T) {
|
|||
|
||||
// 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
|
||||
|
|
@ -3005,7 +3004,7 @@ type B = C
|
|||
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"
|
||||
|
|
@ -3014,28 +3013,6 @@ type C = int
|
|||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
"go/constant"
|
||||
. "internal/types/errors"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// nopos indicates an unknown position
|
||||
|
|
@ -24,29 +23,6 @@ const debug = false // leave on during development
|
|||
// 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
|
||||
|
|
@ -200,34 +176,6 @@ 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)
|
||||
}
|
||||
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 {
|
||||
|
|
@ -472,20 +420,6 @@ func (check *Checker) Files(files []*syntax.File) (err error) {
|
|||
// 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()
|
||||
|
|
|
|||
|
|
@ -112,8 +112,6 @@ func parseFlags(src []byte, flags *flag.FlagSet) error {
|
|||
|
||||
// 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
|
||||
|
|
@ -122,16 +120,6 @@ func parseFlags(src []byte, flags *flag.FlagSet) error {
|
|||
//
|
||||
// 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")
|
||||
}
|
||||
|
|
@ -171,12 +159,11 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, colDelta uin
|
|||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
@ -186,11 +173,6 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, colDelta uin
|
|||
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),
|
||||
|
|
|
|||
|
|
@ -199,27 +199,7 @@ loop:
|
|||
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:
|
||||
|
|
@ -282,14 +262,8 @@ func (check *Checker) cycleError(cycle []Object, start int) {
|
|||
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]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -471,45 +445,35 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl) {
|
|||
versionErr = true
|
||||
}
|
||||
|
||||
if check.conf.EnableAlias {
|
||||
alias := check.newAlias(obj, nil)
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
rhs = check.declaredType(tdecl.Type, obj)
|
||||
assert(rhs != nil)
|
||||
alias.fromRHS = 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")
|
||||
// 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)
|
||||
}
|
||||
} else {
|
||||
if !versionErr && tparam0 != nil {
|
||||
check.error(tdecl, UnsupportedFeature, "generic type alias requires GODEBUG=gotypesalias=1 or unset")
|
||||
versionErr = true
|
||||
}
|
||||
}()
|
||||
|
||||
check.brokenAlias(obj)
|
||||
rhs = check.typ(tdecl.Type)
|
||||
check.validAlias(obj, 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)
|
||||
}
|
||||
|
||||
rhs = check.declaredType(tdecl.Type, obj)
|
||||
assert(rhs != nil)
|
||||
alias.fromRHS = 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
|
||||
}
|
||||
|
||||
|
|
@ -520,6 +484,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl) {
|
|||
|
||||
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:
|
||||
//
|
||||
|
|
|
|||
|
|
@ -863,8 +863,7 @@ func TestIssue59944(t *testing.T) {
|
|||
testenv.MustHaveCGO(t)
|
||||
|
||||
// Methods declared on aliases of cgo types are not permitted.
|
||||
const src = `// -gotypesalias=1
|
||||
|
||||
const src = `
|
||||
package p
|
||||
|
||||
/*
|
||||
|
|
@ -1011,8 +1010,7 @@ type A = []int
|
|||
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 {
|
||||
|
|
@ -1155,8 +1153,7 @@ type (
|
|||
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{}"
|
||||
|
|
|
|||
|
|
@ -591,7 +591,7 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
|||
}
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -80,32 +80,31 @@ func TestEmbeddedMethod(t *testing.T) {
|
|||
}
|
||||
|
||||
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},
|
||||
{"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"},
|
||||
|
||||
{"const c = 1.2", "c", "const p.c untyped float", false},
|
||||
{"const c float64 = 3.14", "c", "const p.c float64", false},
|
||||
{"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}", 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}"},
|
||||
{"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}", 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},
|
||||
{"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", 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},
|
||||
{"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) {
|
||||
|
|
@ -114,8 +113,7 @@ 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -646,13 +646,12 @@ func (check *Checker) packageObjects() {
|
|||
}
|
||||
}
|
||||
|
||||
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]
|
||||
|
|
@ -667,7 +666,7 @@ func (check *Checker) packageObjects() {
|
|||
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).
|
||||
|
|
|
|||
|
|
@ -67,21 +67,7 @@ func (s *Scope) Child(i int) *Scope { return s.children[i] }
|
|||
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -332,10 +332,6 @@ func TestStdFixed(t *testing.T) {
|
|||
"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.
|
||||
|
|
@ -402,8 +398,7 @@ func typecheckFiles(path string, filenames []string, importer Importer) (*Packag
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -211,14 +211,6 @@ func (w *typeWriter) typ(typ Type) {
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -82,10 +82,10 @@ var independentTestTypes = []testEntry{
|
|||
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"),
|
||||
|
|
|
|||
|
|
@ -30,14 +30,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, wantType bool) {
|
|||
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
|
||||
}
|
||||
|
|
@ -109,10 +102,6 @@ func (check *Checker) ident(x *operand, e *syntax.Name, wantType bool) {
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -24,9 +24,8 @@ 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
|
||||
)
|
||||
|
||||
|
|
@ -76,44 +75,15 @@ func defPredeclaredTypes() {
|
|||
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)
|
||||
|
|
@ -130,16 +100,17 @@ func defPredeclaredTypes() {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -278,6 +249,7 @@ func init() {
|
|||
universeByte = Universe.Lookup("byte").Type()
|
||||
universeRune = Universe.Lookup("rune").Type()
|
||||
universeError = Universe.Lookup("error").Type()
|
||||
universeAny = Universe.Lookup("any")
|
||||
universeComparable = Universe.Lookup("comparable")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ package gcimporter
|
|||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"internal/godebug"
|
||||
"internal/pkgbits"
|
||||
"slices"
|
||||
"strings"
|
||||
|
|
@ -678,15 +677,8 @@ func pkgScope(pkg *types.Package) *types.Scope {
|
|||
|
||||
// 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,24 +24,6 @@ import (
|
|||
//
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ import (
|
|||
)
|
||||
|
||||
func TestIssue74181(t *testing.T) {
|
||||
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||
|
||||
src := `package p
|
||||
|
||||
type AB = A[B]
|
||||
|
|
@ -56,8 +54,6 @@ type A[T any] struct{}`
|
|||
}
|
||||
|
||||
func TestPartialTypeCheckUndeclaredAliasPanic(t *testing.T) {
|
||||
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||
|
||||
src := `package p
|
||||
|
||||
type A = B // undeclared`
|
||||
|
|
|
|||
|
|
@ -186,16 +186,6 @@ type Config struct {
|
|||
// 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.
|
||||
|
|
|
|||
|
|
@ -2993,7 +2993,6 @@ func TestTooNew(t *testing.T) {
|
|||
|
||||
// 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
|
||||
|
|
@ -3012,7 +3011,6 @@ type B = T[A]
|
|||
}
|
||||
|
||||
func TestAlias_Rhs(t *testing.T) {
|
||||
setGotypesalias(t, true)
|
||||
const src = `package p
|
||||
|
||||
type A = B
|
||||
|
|
@ -3029,51 +3027,6 @@ type C = int
|
|||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -11,10 +11,8 @@ import (
|
|||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"internal/godebug"
|
||||
. "internal/types/errors"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// nopos, noposn indicate an unknown position
|
||||
|
|
@ -27,42 +25,6 @@ const debug = false // leave on during development
|
|||
// 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
|
||||
|
|
@ -220,34 +182,6 @@ 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)
|
||||
}
|
||||
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 {
|
||||
|
|
@ -316,9 +250,6 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
|||
//
|
||||
// (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,
|
||||
|
|
@ -497,20 +428,6 @@ func (check *Checker) Files(files []*ast.File) (err error) {
|
|||
// 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()
|
||||
|
|
|
|||
|
|
@ -123,8 +123,6 @@ func parseFlags(src []byte, flags *flag.FlagSet) error {
|
|||
|
||||
// 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
|
||||
|
|
@ -133,15 +131,6 @@ func parseFlags(src []byte, flags *flag.FlagSet) error {
|
|||
//
|
||||
// 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")
|
||||
}
|
||||
|
|
@ -185,12 +174,11 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, manual bool,
|
|||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
@ -200,11 +188,6 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, manual bool,
|
|||
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),
|
||||
|
|
|
|||
|
|
@ -201,26 +201,7 @@ loop:
|
|||
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:
|
||||
|
|
@ -283,14 +264,8 @@ func (check *Checker) cycleError(cycle []Object, start int) {
|
|||
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]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -546,52 +521,35 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec) {
|
|||
versionErr = true
|
||||
}
|
||||
|
||||
if check.conf._EnableAlias {
|
||||
alias := check.newAlias(obj, nil)
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
rhs = check.declaredType(tdecl.Type, obj)
|
||||
assert(rhs != nil)
|
||||
alias.fromRHS = 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")
|
||||
// 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)
|
||||
}
|
||||
} 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
|
||||
}
|
||||
|
||||
check.brokenAlias(obj)
|
||||
rhs = check.typ(tdecl.Type)
|
||||
check.validAlias(obj, 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)
|
||||
}
|
||||
|
||||
rhs = check.declaredType(tdecl.Type, obj)
|
||||
assert(rhs != nil)
|
||||
alias.fromRHS = 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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"internal/godebug"
|
||||
"internal/testenv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -174,14 +173,8 @@ func TestEvalPos(t *testing.T) {
|
|||
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)
|
||||
|
|
@ -207,9 +200,6 @@ func TestEvalPos(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
|
|||
|
|
@ -875,8 +875,7 @@ func TestIssue59944(t *testing.T) {
|
|||
testenv.MustHaveCGO(t)
|
||||
|
||||
// Methods declared on aliases of cgo types are not permitted.
|
||||
const src = `// -gotypesalias=1
|
||||
|
||||
const src = `
|
||||
package p
|
||||
|
||||
/*
|
||||
|
|
@ -1024,7 +1023,6 @@ type A = []int
|
|||
type S struct{ A }
|
||||
`
|
||||
|
||||
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||
pkg := mustTypecheck(src, nil, nil)
|
||||
|
||||
S := pkg.Scope().Lookup("S")
|
||||
|
|
@ -1168,7 +1166,6 @@ type (
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
|||
}
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -80,32 +80,31 @@ func TestEmbeddedMethod(t *testing.T) {
|
|||
}
|
||||
|
||||
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},
|
||||
{"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"},
|
||||
|
||||
{"const c = 1.2", "c", "const p.c untyped float", false},
|
||||
{"const c float64 = 3.14", "c", "const p.c float64", false},
|
||||
{"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}", 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}"},
|
||||
{"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}", 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},
|
||||
{"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", 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},
|
||||
{"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) {
|
||||
|
|
@ -113,10 +112,6 @@ 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 {
|
||||
|
|
|
|||
|
|
@ -641,13 +641,12 @@ func (check *Checker) packageObjects() {
|
|||
}
|
||||
}
|
||||
|
||||
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]
|
||||
|
|
@ -662,7 +661,7 @@ func (check *Checker) packageObjects() {
|
|||
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).
|
||||
|
|
|
|||
|
|
@ -70,21 +70,7 @@ func (s *Scope) Child(i int) *Scope { return s.children[i] }
|
|||
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -332,10 +332,6 @@ func TestStdFixed(t *testing.T) {
|
|||
"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.
|
||||
|
|
|
|||
|
|
@ -214,14 +214,6 @@ func (w *typeWriter) typ(typ Type) {
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ var independentTestTypes = []testEntry{
|
|||
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"),
|
||||
|
|
|
|||
|
|
@ -30,14 +30,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, wantType bool) {
|
|||
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
|
||||
}
|
||||
|
|
@ -108,10 +101,6 @@ func (check *Checker) ident(x *operand, e *ast.Ident, wantType bool) {
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -27,9 +27,8 @@ 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
|
||||
)
|
||||
|
||||
|
|
@ -79,44 +78,15 @@ func defPredeclaredTypes() {
|
|||
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)
|
||||
|
|
@ -133,16 +103,17 @@ func defPredeclaredTypes() {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -281,6 +252,7 @@ func init() {
|
|||
universeByte = Universe.Lookup("byte").Type()
|
||||
universeRune = Universe.Lookup("rune").Type()
|
||||
universeError = Universe.Lookup("error").Type()
|
||||
universeAny = Universe.Lookup("any")
|
||||
universeComparable = Universe.Lookup("comparable")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ var All = []Info{
|
|||
{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"},
|
||||
|
|
@ -90,6 +89,7 @@ type RemovedInfo struct {
|
|||
// 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.
|
||||
|
|
|
|||
6
src/internal/types/testdata/check/cycles5.go
vendored
6
src/internal/types/testdata/check/cycles5.go
vendored
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
@ -137,7 +135,7 @@ type (
|
|||
type (
|
||||
a struct{ *b }
|
||||
b = c
|
||||
c struct{ *b /* ERROR "invalid use of type alias" */ }
|
||||
c struct{ *b }
|
||||
)
|
||||
|
||||
// issue #24939
|
||||
|
|
@ -147,7 +145,7 @@ type (
|
|||
}
|
||||
|
||||
M interface {
|
||||
F() P // ERROR "invalid use of type alias"
|
||||
F() P
|
||||
}
|
||||
|
||||
P = interface {
|
||||
|
|
|
|||
202
src/internal/types/testdata/check/cycles5a.go
vendored
202
src/internal/types/testdata/check/cycles5a.go
vendored
|
|
@ -1,202 +0,0 @@
|
|||
// -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
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
// -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] }
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
@ -17,7 +15,7 @@ type R[S any, P any] struct{}
|
|||
type SR = R[SS, ST]
|
||||
|
||||
type SS interface {
|
||||
NSR(any) *SR // ERROR "invalid use of type alias SR in recursive type"
|
||||
NSR(any) *SR
|
||||
}
|
||||
|
||||
type C interface {
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
// -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
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// -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.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// -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
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// -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
|
||||
|
|
@ -302,10 +302,6 @@ Below is the full list of supported metrics, ordered lexicographically.
|
|||
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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue