mirror of
https://github.com/golang/go
synced 2024-11-26 04:58:00 -07:00
go/types, types2: implement Alias proposal (export API)
This CL exports the previously unexported Alias type and corresponding functions and methods per issue #63223. Whether Alias types are used or not is controlled by the gotypesalias setting with the GODEBUG environment variable. Setting gotypesalias to "1" enables the Alias types: GODEBUG=gotypesalias=1 By default, gotypesalias is not set. Adjust test cases that enable/disable the use of Alias types to use -gotypesalias=1 or -gotypesalias=0 rather than -alias and -alias=false for consistency and to avoid confusion. For #63223. Change-Id: I51308cad3320981afac97dd8c6f6a416fdb0be55 Reviewed-on: https://go-review.googlesource.com/c/go/+/541737 Run-TryBot: Robert Griesemer <gri@google.com> Reviewed-by: Robert Findley <rfindley@google.com> Auto-Submit: Robert Griesemer <gri@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
42bd21be1c
commit
30de0b5ef4
6
api/next/63223.txt
Normal file
6
api/next/63223.txt
Normal file
@ -0,0 +1,6 @@
|
||||
pkg go/types, func NewAlias(*TypeName, Type) *Alias #63223
|
||||
pkg go/types, func Unalias(Type) Type #63223
|
||||
pkg go/types, method (*Alias) Obj() *TypeName #63223
|
||||
pkg go/types, method (*Alias) String() string #63223
|
||||
pkg go/types, method (*Alias) Underlying() Type #63223
|
||||
pkg go/types, type Alias struct #63223
|
@ -143,6 +143,13 @@ patterns and unescape both patterns and request paths by segment.
|
||||
This behavior can be controlled by the
|
||||
[`httpmuxgo121` setting](/pkg/net/http/#ServeMux).
|
||||
|
||||
Go 1.22 added the [Alias type](/pkg/go/types#Alias) to [go/types](/pkg/go/types)
|
||||
for the explicit representation of [type aliases](/ref/spec#Type_declarations).
|
||||
Whether the type checker produces `Alias` types or not is controlled by the
|
||||
[`gotypesalias` setting](/pkg/go/types#Alias).
|
||||
For Go 1.22 it defaults to `gotypesalias=0`.
|
||||
For Go 1.23, `gotypealias=1` will become the default.
|
||||
This setting will be removed in a future release, Go 1.24 at the earliest.
|
||||
|
||||
### Go 1.21
|
||||
|
||||
|
@ -6,39 +6,42 @@ package types2
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Names starting with a _ are intended to be exported eventually
|
||||
// (go.dev/issue/63223).
|
||||
|
||||
// An _Alias represents an alias type.
|
||||
type _Alias struct {
|
||||
// An Alias represents an alias type.
|
||||
// Whether or not Alias types are created is controlled by the
|
||||
// gotypesalias setting with the GODEBUG environment variable.
|
||||
// For gotypesalias=1, alias declarations produce an Alias type.
|
||||
// Otherwise, the alias information is only in the type name,
|
||||
// which points directly to the actual (aliased) type.
|
||||
type Alias struct {
|
||||
obj *TypeName // corresponding declared alias object
|
||||
fromRHS Type // RHS of type alias declaration; may be an alias
|
||||
actual Type // actual (aliased) type; never an alias
|
||||
}
|
||||
|
||||
// _NewAlias creates a new Alias type with the given type name and rhs.
|
||||
// NewAlias creates a new Alias type with the given type name and rhs.
|
||||
// rhs must not be nil.
|
||||
func _NewAlias(obj *TypeName, rhs Type) *_Alias {
|
||||
func NewAlias(obj *TypeName, rhs Type) *Alias {
|
||||
return (*Checker)(nil).newAlias(obj, rhs)
|
||||
}
|
||||
|
||||
func (a *_Alias) Underlying() Type { return a.actual.Underlying() }
|
||||
func (a *_Alias) String() string { return TypeString(a, nil) }
|
||||
func (a *Alias) Obj() *TypeName { return a.obj }
|
||||
func (a *Alias) Underlying() Type { return a.actual.Underlying() }
|
||||
func (a *Alias) String() string { return TypeString(a, nil) }
|
||||
|
||||
// Type accessors
|
||||
|
||||
// _Unalias returns t if it is not an alias type;
|
||||
// Unalias returns t if it is not an alias type;
|
||||
// otherwise it follows t's alias chain until it
|
||||
// reaches a non-alias type which is then returned.
|
||||
// Consequently, the result is never an alias type.
|
||||
func _Unalias(t Type) Type {
|
||||
if a0, _ := t.(*_Alias); a0 != nil {
|
||||
func Unalias(t Type) Type {
|
||||
if a0, _ := t.(*Alias); a0 != nil {
|
||||
if a0.actual != nil {
|
||||
return a0.actual
|
||||
}
|
||||
for a := a0; ; {
|
||||
t = a.fromRHS
|
||||
a, _ = t.(*_Alias)
|
||||
a, _ = t.(*Alias)
|
||||
if a == nil {
|
||||
break
|
||||
}
|
||||
@ -54,15 +57,15 @@ func _Unalias(t Type) Type {
|
||||
// asNamed returns t as *Named if that is t's
|
||||
// actual type. It returns nil otherwise.
|
||||
func asNamed(t Type) *Named {
|
||||
n, _ := _Unalias(t).(*Named)
|
||||
n, _ := Unalias(t).(*Named)
|
||||
return n
|
||||
}
|
||||
|
||||
// newAlias creates a new Alias type with the given type name and rhs.
|
||||
// rhs must not be nil.
|
||||
func (check *Checker) newAlias(obj *TypeName, rhs Type) *_Alias {
|
||||
func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
||||
assert(rhs != nil)
|
||||
a := &_Alias{obj, rhs, nil}
|
||||
a := &Alias{obj, rhs, nil}
|
||||
if obj.typ == nil {
|
||||
obj.typ = a
|
||||
}
|
||||
@ -75,6 +78,6 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *_Alias {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *_Alias) cleanup() {
|
||||
_Unalias(a)
|
||||
func (a *Alias) cleanup() {
|
||||
Unalias(a)
|
||||
}
|
||||
|
@ -170,11 +170,6 @@ type Config struct {
|
||||
// for unused imports.
|
||||
DisableUnusedImportCheck bool
|
||||
|
||||
// If EnableAlias is set, alias declarations produce an _Alias type.
|
||||
// Otherwise the alias information is only in the type name, which
|
||||
// points directly to the actual (aliased) type.
|
||||
_EnableAlias bool
|
||||
|
||||
// If a non-empty ErrorURL format string is provided, it is used
|
||||
// to format an error URL link that is appended to the first line
|
||||
// of an error message. ErrorURL must be a format string containing
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"internal/godebug"
|
||||
"internal/goversion"
|
||||
. "internal/types/errors"
|
||||
)
|
||||
@ -21,6 +22,9 @@ var nopos syntax.Pos
|
||||
// debugging/development support
|
||||
const debug = false // leave on during development
|
||||
|
||||
// gotypesalias controls the use of Alias types.
|
||||
var gotypesalias = godebug.New("gotypesalias")
|
||||
|
||||
// exprInfo stores information about an untyped expression.
|
||||
type exprInfo struct {
|
||||
isLhs bool // expression is lhs operand of a shift with delayed type-check
|
||||
@ -93,6 +97,12 @@ type actionDesc struct {
|
||||
type Checker struct {
|
||||
// package information
|
||||
// (initialized by NewChecker, valid for the life-time of checker)
|
||||
|
||||
// If enableAlias is set, alias declarations produce an Alias type.
|
||||
// Otherwise the alias information is only in the type name, which
|
||||
// points directly to the actual (aliased) type.
|
||||
enableAlias bool
|
||||
|
||||
conf *Config
|
||||
ctxt *Context // context for de-duplicating instances
|
||||
pkg *Package
|
||||
@ -153,13 +163,13 @@ func (check *Checker) addDeclDep(to Object) {
|
||||
}
|
||||
|
||||
// Note: The following three alias-related functions are only used
|
||||
// when _Alias types are not enabled.
|
||||
// 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.
|
||||
// Not used if check.enableAlias is set.
|
||||
func (check *Checker) brokenAlias(alias *TypeName) {
|
||||
assert(!check.conf._EnableAlias)
|
||||
assert(!check.enableAlias)
|
||||
if check.brokenAliases == nil {
|
||||
check.brokenAliases = make(map[*TypeName]bool)
|
||||
}
|
||||
@ -169,14 +179,14 @@ func (check *Checker) brokenAlias(alias *TypeName) {
|
||||
|
||||
// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
|
||||
func (check *Checker) validAlias(alias *TypeName, typ Type) {
|
||||
assert(!check.conf._EnableAlias)
|
||||
assert(!check.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)
|
||||
assert(!check.enableAlias)
|
||||
return check.brokenAliases[alias]
|
||||
}
|
||||
|
||||
@ -246,12 +256,13 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
|
||||
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
|
||||
|
||||
return &Checker{
|
||||
conf: conf,
|
||||
ctxt: conf.Context,
|
||||
pkg: pkg,
|
||||
Info: info,
|
||||
objMap: make(map[Object]*declInfo),
|
||||
impMap: make(map[importKey]*Package),
|
||||
enableAlias: gotypesalias.Value() == "1",
|
||||
conf: conf,
|
||||
ctxt: conf.Context,
|
||||
pkg: pkg,
|
||||
Info: info,
|
||||
objMap: make(map[Object]*declInfo),
|
||||
impMap: make(map[importKey]*Package),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ 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.
|
||||
// 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,9 +122,11 @@ 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)) {
|
||||
// Alias types are disabled by default
|
||||
testFilesImpl(t, filenames, srcs, colDelta, manual, opts...)
|
||||
if !manual {
|
||||
testFilesImpl(t, filenames, srcs, colDelta, manual, append(opts, func(conf *Config) { *boolFieldAddr(conf, "_EnableAlias") = true })...)
|
||||
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||
testFilesImpl(t, filenames, srcs, colDelta, manual, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,15 +170,16 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, colDelta uin
|
||||
}
|
||||
|
||||
// apply flag setting (overrides custom configuration)
|
||||
var goexperiment string
|
||||
var goexperiment, gotypesalias string
|
||||
flags := flag.NewFlagSet("", flag.PanicOnError)
|
||||
flags.StringVar(&conf.GoVersion, "lang", "", "")
|
||||
flags.StringVar(&goexperiment, "goexperiment", "", "")
|
||||
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
|
||||
flags.BoolVar(boolFieldAddr(&conf, "_EnableAlias"), "alias", false, "")
|
||||
flags.StringVar(&gotypesalias, "gotypesalias", "", "")
|
||||
if err := parseFlags(srcs[0], flags); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exp, err := buildcfg.ParseGOEXPERIMENT(runtime.GOOS, runtime.GOARCH, goexperiment)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -187,6 +190,11 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, colDelta uin
|
||||
}()
|
||||
buildcfg.Experiment = *exp
|
||||
|
||||
// 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[syntax.Expr]TypeAndValue),
|
||||
|
@ -251,7 +251,7 @@ loop:
|
||||
// the syntactic information. We should consider storing
|
||||
// this information explicitly in the object.
|
||||
var alias bool
|
||||
if check.conf._EnableAlias {
|
||||
if check.enableAlias {
|
||||
alias = obj.IsAlias()
|
||||
} else {
|
||||
if d := check.objMap[obj]; d != nil {
|
||||
@ -328,7 +328,7 @@ func (check *Checker) cycleError(cycle []Object) {
|
||||
if tname != nil && tname.IsAlias() {
|
||||
// If we use Alias nodes, it is initialized with Typ[Invalid].
|
||||
// TODO(gri) Adjust this code if we initialize with nil.
|
||||
if !check.conf._EnableAlias {
|
||||
if !check.enableAlias {
|
||||
check.validAlias(tname, Typ[Invalid])
|
||||
}
|
||||
}
|
||||
@ -514,7 +514,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *TypeN
|
||||
// alias declaration
|
||||
if aliasDecl {
|
||||
check.verifyVersionf(tdecl, go1_9, "type aliases")
|
||||
if check.conf._EnableAlias {
|
||||
if check.enableAlias {
|
||||
// TODO(gri) Should be able to use nil instead of Typ[Invalid] to mark
|
||||
// the alias as incomplete. Currently this causes problems
|
||||
// with certain cycles. Investigate.
|
||||
@ -523,7 +523,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *TypeN
|
||||
rhs = check.definedType(tdecl.Type, obj)
|
||||
assert(rhs != nil)
|
||||
alias.fromRHS = rhs
|
||||
_Unalias(alias) // resolve alias.actual
|
||||
Unalias(alias) // resolve alias.actual
|
||||
} else {
|
||||
check.brokenAlias(obj)
|
||||
rhs = check.typ(tdecl.Type)
|
||||
|
@ -542,8 +542,8 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
|
||||
case *Basic:
|
||||
// nothing to do
|
||||
|
||||
case *_Alias:
|
||||
return w.isParameterized(_Unalias(t))
|
||||
case *Alias:
|
||||
return w.isParameterized(Unalias(t))
|
||||
|
||||
case *Array:
|
||||
return w.isParameterized(t.elem)
|
||||
@ -696,8 +696,8 @@ func (w *cycleFinder) typ(typ Type) {
|
||||
case *Basic:
|
||||
// nothing to do
|
||||
|
||||
case *_Alias:
|
||||
w.typ(_Unalias(t))
|
||||
case *Alias:
|
||||
w.typ(Unalias(t))
|
||||
|
||||
case *Array:
|
||||
w.typ(t.elem)
|
||||
|
@ -989,9 +989,8 @@ type A = []int
|
||||
type S struct{ A }
|
||||
`
|
||||
|
||||
var conf Config
|
||||
*boolFieldAddr(&conf, "_EnableAlias") = true
|
||||
pkg := mustTypecheck(src, &conf, nil)
|
||||
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||
pkg := mustTypecheck(src, nil, nil)
|
||||
|
||||
S := pkg.Scope().Lookup("S")
|
||||
if S == nil {
|
||||
|
@ -527,7 +527,7 @@ func (check *Checker) newAssertableTo(pos syntax.Pos, V, T Type, cause *string)
|
||||
// with an underlying pointer type!) and returns its base and true.
|
||||
// Otherwise it returns (typ, false).
|
||||
func deref(typ Type) (Type, bool) {
|
||||
if p, _ := _Unalias(typ).(*Pointer); p != nil {
|
||||
if p, _ := Unalias(typ).(*Pointer); p != nil {
|
||||
// p.base should never be nil, but be conservative
|
||||
if p.base == nil {
|
||||
if debug {
|
||||
|
@ -208,7 +208,7 @@ func (w *monoGraph) assign(pkg *Package, pos syntax.Pos, tpar *TypeParam, targ T
|
||||
// type parameters.
|
||||
var do func(typ Type)
|
||||
do = func(typ Type) {
|
||||
switch typ := _Unalias(typ).(type) {
|
||||
switch typ := Unalias(typ).(type) {
|
||||
default:
|
||||
panic("unexpected type")
|
||||
|
||||
|
@ -453,8 +453,8 @@ func (t *Named) AddMethod(m *Func) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gri) Investigate if _Unalias can be moved to where underlying is set.
|
||||
func (t *Named) Underlying() Type { return _Unalias(t.resolve().underlying) }
|
||||
// TODO(gri) Investigate if Unalias can be moved to where underlying is set.
|
||||
func (t *Named) Underlying() Type { return Unalias(t.resolve().underlying) }
|
||||
func (t *Named) String() string { return TypeString(t, nil) }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -285,7 +285,7 @@ func (obj *TypeName) IsAlias() bool {
|
||||
switch t := obj.typ.(type) {
|
||||
case nil:
|
||||
return false
|
||||
// case *_Alias:
|
||||
// case *Alias:
|
||||
// handled by default case
|
||||
case *Basic:
|
||||
// unsafe.Pointer is not an alias.
|
||||
|
@ -7,7 +7,7 @@
|
||||
package types2
|
||||
|
||||
// isValid reports whether t is a valid type.
|
||||
func isValid(t Type) bool { return _Unalias(t) != Typ[Invalid] }
|
||||
func isValid(t Type) bool { return Unalias(t) != Typ[Invalid] }
|
||||
|
||||
// The isX predicates below report whether t is an X.
|
||||
// If t is a type parameter the result is false; i.e.,
|
||||
@ -50,7 +50,7 @@ func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) }
|
||||
// for all specific types of the type parameter's type set.
|
||||
// allBasic(t, info) is an optimized version of isBasic(coreType(t), info).
|
||||
func allBasic(t Type, info BasicInfo) bool {
|
||||
if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil {
|
||||
if tpar, _ := Unalias(t).(*TypeParam); tpar != nil {
|
||||
return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
|
||||
}
|
||||
return isBasic(t, info)
|
||||
@ -60,7 +60,7 @@ func allBasic(t Type, info BasicInfo) bool {
|
||||
// predeclared types, defined types, and type parameters.
|
||||
// hasName may be called with types that are not fully set up.
|
||||
func hasName(t Type) bool {
|
||||
switch _Unalias(t).(type) {
|
||||
switch Unalias(t).(type) {
|
||||
case *Basic, *Named, *TypeParam:
|
||||
return true
|
||||
}
|
||||
@ -71,7 +71,7 @@ func hasName(t Type) bool {
|
||||
// This includes all non-defined types, but also basic types.
|
||||
// isTypeLit may be called with types that are not fully set up.
|
||||
func isTypeLit(t Type) bool {
|
||||
switch _Unalias(t).(type) {
|
||||
switch Unalias(t).(type) {
|
||||
case *Named, *TypeParam:
|
||||
return false
|
||||
}
|
||||
@ -83,7 +83,7 @@ func isTypeLit(t Type) bool {
|
||||
// are not fully set up.
|
||||
func isTyped(t Type) bool {
|
||||
// Alias or Named types cannot denote untyped types,
|
||||
// thus we don't need to call _Unalias or under
|
||||
// thus we don't need to call Unalias or under
|
||||
// (which would be unsafe to do for types that are
|
||||
// not fully set up).
|
||||
b, _ := t.(*Basic)
|
||||
@ -108,7 +108,7 @@ func isNonTypeParamInterface(t Type) bool {
|
||||
|
||||
// isTypeParam reports whether t is a type parameter.
|
||||
func isTypeParam(t Type) bool {
|
||||
_, ok := _Unalias(t).(*TypeParam)
|
||||
_, ok := Unalias(t).(*TypeParam)
|
||||
return ok
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ func isTypeParam(t Type) bool {
|
||||
// use anywhere, but it may report a false negative if the type set has not been
|
||||
// computed yet.
|
||||
func hasEmptyTypeset(t Type) bool {
|
||||
if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil && tpar.bound != nil {
|
||||
if tpar, _ := Unalias(t).(*TypeParam); tpar != nil && tpar.bound != nil {
|
||||
iface, _ := safeUnderlying(tpar.bound).(*Interface)
|
||||
return iface != nil && iface.tset != nil && iface.tset.IsEmpty()
|
||||
}
|
||||
@ -223,8 +223,8 @@ type comparer struct {
|
||||
|
||||
// For changes to this code the corresponding changes should be made to unifier.nify.
|
||||
func (c *comparer) identical(x, y Type, p *ifacePair) bool {
|
||||
x = _Unalias(x)
|
||||
y = _Unalias(y)
|
||||
x = Unalias(x)
|
||||
y = Unalias(y)
|
||||
|
||||
if x == y {
|
||||
return true
|
||||
@ -500,7 +500,7 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool
|
||||
// it returns the incoming type for all other types. The default type
|
||||
// for untyped nil is untyped nil.
|
||||
func Default(t Type) Type {
|
||||
if t, ok := _Unalias(t).(*Basic); ok {
|
||||
if t, ok := Unalias(t).(*Basic); ok {
|
||||
switch t.kind {
|
||||
case UntypedBool:
|
||||
return Typ[Bool]
|
||||
|
@ -677,13 +677,13 @@ func (check *Checker) packageObjects() {
|
||||
}
|
||||
}
|
||||
|
||||
if check.conf._EnableAlias {
|
||||
// With _Alias nodes we can process declarations in any order.
|
||||
if check.enableAlias {
|
||||
// With Alias nodes we can process declarations in any order.
|
||||
for _, obj := range objList {
|
||||
check.objDecl(obj, nil)
|
||||
}
|
||||
} else {
|
||||
// Without _Alias nodes, we process non-alias type declarations first, followed by
|
||||
// Without Alias nodes, we process non-alias type declarations first, followed by
|
||||
// alias declarations, and then everything else. This appears to avoid most situations
|
||||
// where the type of an alias is needed before it is available.
|
||||
// There may still be cases where this is not good enough (see also go.dev/issue/25838).
|
||||
|
@ -208,7 +208,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
|
||||
check.later(func() {
|
||||
// spec: "The receiver type must be of the form T or *T where T is a type name."
|
||||
rtyp, _ := deref(recv.typ)
|
||||
atyp := _Unalias(rtyp)
|
||||
atyp := Unalias(rtyp)
|
||||
if !isValid(atyp) {
|
||||
return // error was reported before
|
||||
}
|
||||
|
@ -326,13 +326,13 @@ func (w *typeWriter) typ(typ Type) {
|
||||
}
|
||||
}
|
||||
|
||||
case *_Alias:
|
||||
case *Alias:
|
||||
w.typeName(t.obj)
|
||||
if w.ctxt != nil {
|
||||
// TODO(gri) do we need to print the alias type name, too?
|
||||
w.typ(_Unalias(t.obj.typ))
|
||||
w.typ(Unalias(t.obj.typ))
|
||||
} else {
|
||||
w.string(fmt.Sprintf(" /* = %s */", _Unalias(t.obj.typ)))
|
||||
w.string(fmt.Sprintf(" /* = %s */", Unalias(t.obj.typ)))
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -94,7 +94,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *TypeName, wantType
|
||||
x.mode = constant_
|
||||
|
||||
case *TypeName:
|
||||
if !check.conf._EnableAlias && check.isBrokenAlias(obj) {
|
||||
if !check.enableAlias && check.isBrokenAlias(obj) {
|
||||
check.errorf(e, InvalidDeclCycle, "invalid use of type alias %s in recursive type (see go.dev/issue/50729)", obj.name)
|
||||
return
|
||||
}
|
||||
@ -403,7 +403,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *TypeName) (T Type) {
|
||||
func setDefType(def *TypeName, typ Type) {
|
||||
if def != nil {
|
||||
switch t := def.typ.(type) {
|
||||
case *_Alias:
|
||||
case *Alias:
|
||||
// t.fromRHS should always be set, either to an invalid type
|
||||
// in the beginning, or to typ in certain cyclic declarations.
|
||||
if t.fromRHS != Typ[Invalid] && t.fromRHS != typ {
|
||||
|
@ -291,8 +291,8 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
||||
u.depth--
|
||||
}()
|
||||
|
||||
x = _Unalias(x)
|
||||
y = _Unalias(y)
|
||||
x = Unalias(x)
|
||||
y = Unalias(y)
|
||||
|
||||
// nothing to do if x == y
|
||||
if x == y {
|
||||
|
@ -23,7 +23,7 @@ func (check *Checker) validType(typ *Named) {
|
||||
// (say S->F->S) we have an invalid recursive type. The path list is the full
|
||||
// path of named types in a cycle, it is only needed for error reporting.
|
||||
func (check *Checker) validType0(typ Type, nest, path []*Named) bool {
|
||||
switch t := _Unalias(typ).(type) {
|
||||
switch t := Unalias(typ).(type) {
|
||||
case nil:
|
||||
// We should never see a nil type but be conservative and panic
|
||||
// only in debug mode.
|
||||
|
@ -8,39 +8,42 @@ package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Names starting with a _ are intended to be exported eventually
|
||||
// (go.dev/issue/63223).
|
||||
|
||||
// An _Alias represents an alias type.
|
||||
type _Alias struct {
|
||||
// An Alias represents an alias type.
|
||||
// Whether or not Alias types are created is controlled by the
|
||||
// gotypesalias setting with the GODEBUG environment variable.
|
||||
// For gotypesalias=1, alias declarations produce an Alias type.
|
||||
// Otherwise, the alias information is only in the type name,
|
||||
// which points directly to the actual (aliased) type.
|
||||
type Alias struct {
|
||||
obj *TypeName // corresponding declared alias object
|
||||
fromRHS Type // RHS of type alias declaration; may be an alias
|
||||
actual Type // actual (aliased) type; never an alias
|
||||
}
|
||||
|
||||
// _NewAlias creates a new Alias type with the given type name and rhs.
|
||||
// NewAlias creates a new Alias type with the given type name and rhs.
|
||||
// rhs must not be nil.
|
||||
func _NewAlias(obj *TypeName, rhs Type) *_Alias {
|
||||
func NewAlias(obj *TypeName, rhs Type) *Alias {
|
||||
return (*Checker)(nil).newAlias(obj, rhs)
|
||||
}
|
||||
|
||||
func (a *_Alias) Underlying() Type { return a.actual.Underlying() }
|
||||
func (a *_Alias) String() string { return TypeString(a, nil) }
|
||||
func (a *Alias) Obj() *TypeName { return a.obj }
|
||||
func (a *Alias) Underlying() Type { return a.actual.Underlying() }
|
||||
func (a *Alias) String() string { return TypeString(a, nil) }
|
||||
|
||||
// Type accessors
|
||||
|
||||
// _Unalias returns t if it is not an alias type;
|
||||
// Unalias returns t if it is not an alias type;
|
||||
// otherwise it follows t's alias chain until it
|
||||
// reaches a non-alias type which is then returned.
|
||||
// Consequently, the result is never an alias type.
|
||||
func _Unalias(t Type) Type {
|
||||
if a0, _ := t.(*_Alias); a0 != nil {
|
||||
func Unalias(t Type) Type {
|
||||
if a0, _ := t.(*Alias); a0 != nil {
|
||||
if a0.actual != nil {
|
||||
return a0.actual
|
||||
}
|
||||
for a := a0; ; {
|
||||
t = a.fromRHS
|
||||
a, _ = t.(*_Alias)
|
||||
a, _ = t.(*Alias)
|
||||
if a == nil {
|
||||
break
|
||||
}
|
||||
@ -56,15 +59,15 @@ func _Unalias(t Type) Type {
|
||||
// asNamed returns t as *Named if that is t's
|
||||
// actual type. It returns nil otherwise.
|
||||
func asNamed(t Type) *Named {
|
||||
n, _ := _Unalias(t).(*Named)
|
||||
n, _ := Unalias(t).(*Named)
|
||||
return n
|
||||
}
|
||||
|
||||
// newAlias creates a new Alias type with the given type name and rhs.
|
||||
// rhs must not be nil.
|
||||
func (check *Checker) newAlias(obj *TypeName, rhs Type) *_Alias {
|
||||
func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
||||
assert(rhs != nil)
|
||||
a := &_Alias{obj, rhs, nil}
|
||||
a := &Alias{obj, rhs, nil}
|
||||
if obj.typ == nil {
|
||||
obj.typ = a
|
||||
}
|
||||
@ -77,6 +80,6 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *_Alias {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *_Alias) cleanup() {
|
||||
_Unalias(a)
|
||||
func (a *Alias) cleanup() {
|
||||
Unalias(a)
|
||||
}
|
||||
|
@ -171,11 +171,6 @@ type Config struct {
|
||||
// for unused imports.
|
||||
DisableUnusedImportCheck bool
|
||||
|
||||
// If EnableAlias is set, alias declarations produce an _Alias type.
|
||||
// Otherwise the alias information is only in the type name, which
|
||||
// points directly to the actual (aliased) type.
|
||||
_EnableAlias bool
|
||||
|
||||
// If a non-empty _ErrorURL format string is provided, it is used
|
||||
// to format an error URL link that is appended to the first line
|
||||
// of an error message. ErrorURL must be a format string containing
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"internal/godebug"
|
||||
"internal/goversion"
|
||||
. "internal/types/errors"
|
||||
)
|
||||
@ -22,6 +23,9 @@ var nopos token.Pos
|
||||
// debugging/development support
|
||||
const debug = false // leave on during development
|
||||
|
||||
// gotypesalias controls the use of Alias types
|
||||
var gotypesalias = godebug.New("gotypesalias")
|
||||
|
||||
// exprInfo stores information about an untyped expression.
|
||||
type exprInfo struct {
|
||||
isLhs bool // expression is lhs operand of a shift with delayed type-check
|
||||
@ -94,6 +98,12 @@ type actionDesc struct {
|
||||
type Checker struct {
|
||||
// package information
|
||||
// (initialized by NewChecker, valid for the life-time of checker)
|
||||
|
||||
// If EnableAlias is set, alias declarations produce an Alias type.
|
||||
// Otherwise the alias information is only in the type name, which
|
||||
// points directly to the actual (aliased) type.
|
||||
enableAlias bool
|
||||
|
||||
conf *Config
|
||||
ctxt *Context // context for de-duplicating instances
|
||||
fset *token.FileSet
|
||||
@ -155,13 +165,13 @@ func (check *Checker) addDeclDep(to Object) {
|
||||
}
|
||||
|
||||
// Note: The following three alias-related functions are only used
|
||||
// when _Alias types are not enabled.
|
||||
// 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.
|
||||
// Not used if check.enableAlias is set.
|
||||
func (check *Checker) brokenAlias(alias *TypeName) {
|
||||
assert(!check.conf._EnableAlias)
|
||||
assert(!check.enableAlias)
|
||||
if check.brokenAliases == nil {
|
||||
check.brokenAliases = make(map[*TypeName]bool)
|
||||
}
|
||||
@ -171,14 +181,14 @@ func (check *Checker) brokenAlias(alias *TypeName) {
|
||||
|
||||
// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
|
||||
func (check *Checker) validAlias(alias *TypeName, typ Type) {
|
||||
assert(!check.conf._EnableAlias)
|
||||
assert(!check.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)
|
||||
assert(!check.enableAlias)
|
||||
return check.brokenAliases[alias]
|
||||
}
|
||||
|
||||
@ -248,13 +258,14 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
||||
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
|
||||
|
||||
return &Checker{
|
||||
conf: conf,
|
||||
ctxt: conf.Context,
|
||||
fset: fset,
|
||||
pkg: pkg,
|
||||
Info: info,
|
||||
objMap: make(map[Object]*declInfo),
|
||||
impMap: make(map[importKey]*Package),
|
||||
enableAlias: gotypesalias.Value() == "1",
|
||||
conf: conf,
|
||||
ctxt: conf.Context,
|
||||
fset: fset,
|
||||
pkg: pkg,
|
||||
Info: info,
|
||||
objMap: make(map[Object]*declInfo),
|
||||
impMap: make(map[importKey]*Package),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ 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.
|
||||
// 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
|
||||
@ -134,9 +134,11 @@ 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 disabled by default
|
||||
testFilesImpl(t, filenames, srcs, manual, opts...)
|
||||
if !manual {
|
||||
testFilesImpl(t, filenames, srcs, manual, append(opts, func(conf *Config) { *boolFieldAddr(conf, "_EnableAlias") = true })...)
|
||||
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||
testFilesImpl(t, filenames, srcs, manual, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,15 +186,16 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, manual bool,
|
||||
}
|
||||
|
||||
// apply flag setting (overrides custom configuration)
|
||||
var goexperiment string
|
||||
var goexperiment, gotypesalias string
|
||||
flags := flag.NewFlagSet("", flag.PanicOnError)
|
||||
flags.StringVar(&conf.GoVersion, "lang", "", "")
|
||||
flags.StringVar(&goexperiment, "goexperiment", "", "")
|
||||
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
|
||||
flags.BoolVar(boolFieldAddr(&conf, "_EnableAlias"), "alias", false, "")
|
||||
flags.StringVar(&gotypesalias, "gotypesalias", "", "")
|
||||
if err := parseFlags(srcs[0], flags); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exp, err := buildcfg.ParseGOEXPERIMENT(runtime.GOOS, runtime.GOARCH, goexperiment)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -203,6 +206,11 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, manual bool,
|
||||
}()
|
||||
buildcfg.Experiment = *exp
|
||||
|
||||
// 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),
|
||||
|
@ -249,7 +249,7 @@ loop:
|
||||
// the syntactic information. We should consider storing
|
||||
// this information explicitly in the object.
|
||||
var alias bool
|
||||
if check.conf._EnableAlias {
|
||||
if check.enableAlias {
|
||||
alias = obj.IsAlias()
|
||||
} else {
|
||||
if d := check.objMap[obj]; d != nil {
|
||||
@ -326,7 +326,7 @@ func (check *Checker) cycleError(cycle []Object) {
|
||||
if tname != nil && tname.IsAlias() {
|
||||
// If we use Alias nodes, it is initialized with Typ[Invalid].
|
||||
// TODO(gri) Adjust this code if we initialize with nil.
|
||||
if !check.conf._EnableAlias {
|
||||
if !check.enableAlias {
|
||||
check.validAlias(tname, Typ[Invalid])
|
||||
}
|
||||
}
|
||||
@ -583,7 +583,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *TypeName
|
||||
// alias declaration
|
||||
if aliasDecl {
|
||||
check.verifyVersionf(atPos(tdecl.Assign), go1_9, "type aliases")
|
||||
if check.conf._EnableAlias {
|
||||
if check.enableAlias {
|
||||
// TODO(gri) Should be able to use nil instead of Typ[Invalid] to mark
|
||||
// the alias as incomplete. Currently this causes problems
|
||||
// with certain cycles. Investigate.
|
||||
@ -592,7 +592,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *TypeName
|
||||
rhs = check.definedType(tdecl.Type, obj)
|
||||
assert(rhs != nil)
|
||||
alias.fromRHS = rhs
|
||||
_Unalias(alias) // resolve alias.actual
|
||||
Unalias(alias) // resolve alias.actual
|
||||
} else {
|
||||
check.brokenAlias(obj)
|
||||
rhs = check.typ(tdecl.Type)
|
||||
|
@ -544,8 +544,8 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
|
||||
case *Basic:
|
||||
// nothing to do
|
||||
|
||||
case *_Alias:
|
||||
return w.isParameterized(_Unalias(t))
|
||||
case *Alias:
|
||||
return w.isParameterized(Unalias(t))
|
||||
|
||||
case *Array:
|
||||
return w.isParameterized(t.elem)
|
||||
@ -698,8 +698,8 @@ func (w *cycleFinder) typ(typ Type) {
|
||||
case *Basic:
|
||||
// nothing to do
|
||||
|
||||
case *_Alias:
|
||||
w.typ(_Unalias(t))
|
||||
case *Alias:
|
||||
w.typ(Unalias(t))
|
||||
|
||||
case *Array:
|
||||
w.typ(t.elem)
|
||||
|
@ -999,9 +999,8 @@ type A = []int
|
||||
type S struct{ A }
|
||||
`
|
||||
|
||||
var conf Config
|
||||
*boolFieldAddr(&conf, "_EnableAlias") = true
|
||||
pkg := mustTypecheck(src, &conf, nil)
|
||||
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||
pkg := mustTypecheck(src, nil, nil)
|
||||
|
||||
S := pkg.Scope().Lookup("S")
|
||||
if S == nil {
|
||||
|
@ -529,7 +529,7 @@ func (check *Checker) newAssertableTo(pos token.Pos, V, T Type, cause *string) b
|
||||
// with an underlying pointer type!) and returns its base and true.
|
||||
// Otherwise it returns (typ, false).
|
||||
func deref(typ Type) (Type, bool) {
|
||||
if p, _ := _Unalias(typ).(*Pointer); p != nil {
|
||||
if p, _ := Unalias(typ).(*Pointer); p != nil {
|
||||
// p.base should never be nil, but be conservative
|
||||
if p.base == nil {
|
||||
if debug {
|
||||
|
@ -206,7 +206,7 @@ func (w *monoGraph) assign(pkg *Package, pos token.Pos, tpar *TypeParam, targ Ty
|
||||
// type parameters.
|
||||
var do func(typ Type)
|
||||
do = func(typ Type) {
|
||||
switch typ := _Unalias(typ).(type) {
|
||||
switch typ := Unalias(typ).(type) {
|
||||
default:
|
||||
panic("unexpected type")
|
||||
|
||||
|
@ -455,8 +455,8 @@ func (t *Named) AddMethod(m *Func) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gri) Investigate if _Unalias can be moved to where underlying is set.
|
||||
func (t *Named) Underlying() Type { return _Unalias(t.resolve().underlying) }
|
||||
// TODO(gri) Investigate if Unalias can be moved to where underlying is set.
|
||||
func (t *Named) Underlying() Type { return Unalias(t.resolve().underlying) }
|
||||
func (t *Named) String() string { return TypeString(t, nil) }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -287,7 +287,7 @@ func (obj *TypeName) IsAlias() bool {
|
||||
switch t := obj.typ.(type) {
|
||||
case nil:
|
||||
return false
|
||||
// case *_Alias:
|
||||
// case *Alias:
|
||||
// handled by default case
|
||||
case *Basic:
|
||||
// unsafe.Pointer is not an alias.
|
||||
|
@ -9,7 +9,7 @@
|
||||
package types
|
||||
|
||||
// isValid reports whether t is a valid type.
|
||||
func isValid(t Type) bool { return _Unalias(t) != Typ[Invalid] }
|
||||
func isValid(t Type) bool { return Unalias(t) != Typ[Invalid] }
|
||||
|
||||
// The isX predicates below report whether t is an X.
|
||||
// If t is a type parameter the result is false; i.e.,
|
||||
@ -52,7 +52,7 @@ func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) }
|
||||
// for all specific types of the type parameter's type set.
|
||||
// allBasic(t, info) is an optimized version of isBasic(coreType(t), info).
|
||||
func allBasic(t Type, info BasicInfo) bool {
|
||||
if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil {
|
||||
if tpar, _ := Unalias(t).(*TypeParam); tpar != nil {
|
||||
return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
|
||||
}
|
||||
return isBasic(t, info)
|
||||
@ -62,7 +62,7 @@ func allBasic(t Type, info BasicInfo) bool {
|
||||
// predeclared types, defined types, and type parameters.
|
||||
// hasName may be called with types that are not fully set up.
|
||||
func hasName(t Type) bool {
|
||||
switch _Unalias(t).(type) {
|
||||
switch Unalias(t).(type) {
|
||||
case *Basic, *Named, *TypeParam:
|
||||
return true
|
||||
}
|
||||
@ -73,7 +73,7 @@ func hasName(t Type) bool {
|
||||
// This includes all non-defined types, but also basic types.
|
||||
// isTypeLit may be called with types that are not fully set up.
|
||||
func isTypeLit(t Type) bool {
|
||||
switch _Unalias(t).(type) {
|
||||
switch Unalias(t).(type) {
|
||||
case *Named, *TypeParam:
|
||||
return false
|
||||
}
|
||||
@ -85,7 +85,7 @@ func isTypeLit(t Type) bool {
|
||||
// are not fully set up.
|
||||
func isTyped(t Type) bool {
|
||||
// Alias or Named types cannot denote untyped types,
|
||||
// thus we don't need to call _Unalias or under
|
||||
// thus we don't need to call Unalias or under
|
||||
// (which would be unsafe to do for types that are
|
||||
// not fully set up).
|
||||
b, _ := t.(*Basic)
|
||||
@ -110,7 +110,7 @@ func isNonTypeParamInterface(t Type) bool {
|
||||
|
||||
// isTypeParam reports whether t is a type parameter.
|
||||
func isTypeParam(t Type) bool {
|
||||
_, ok := _Unalias(t).(*TypeParam)
|
||||
_, ok := Unalias(t).(*TypeParam)
|
||||
return ok
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ func isTypeParam(t Type) bool {
|
||||
// use anywhere, but it may report a false negative if the type set has not been
|
||||
// computed yet.
|
||||
func hasEmptyTypeset(t Type) bool {
|
||||
if tpar, _ := _Unalias(t).(*TypeParam); tpar != nil && tpar.bound != nil {
|
||||
if tpar, _ := Unalias(t).(*TypeParam); tpar != nil && tpar.bound != nil {
|
||||
iface, _ := safeUnderlying(tpar.bound).(*Interface)
|
||||
return iface != nil && iface.tset != nil && iface.tset.IsEmpty()
|
||||
}
|
||||
@ -225,8 +225,8 @@ type comparer struct {
|
||||
|
||||
// For changes to this code the corresponding changes should be made to unifier.nify.
|
||||
func (c *comparer) identical(x, y Type, p *ifacePair) bool {
|
||||
x = _Unalias(x)
|
||||
y = _Unalias(y)
|
||||
x = Unalias(x)
|
||||
y = Unalias(y)
|
||||
|
||||
if x == y {
|
||||
return true
|
||||
@ -502,7 +502,7 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool
|
||||
// it returns the incoming type for all other types. The default type
|
||||
// for untyped nil is untyped nil.
|
||||
func Default(t Type) Type {
|
||||
if t, ok := _Unalias(t).(*Basic); ok {
|
||||
if t, ok := Unalias(t).(*Basic); ok {
|
||||
switch t.kind {
|
||||
case UntypedBool:
|
||||
return Typ[Bool]
|
||||
|
@ -659,13 +659,13 @@ func (check *Checker) packageObjects() {
|
||||
}
|
||||
}
|
||||
|
||||
if check.conf._EnableAlias {
|
||||
// With _Alias nodes we can process declarations in any order.
|
||||
if check.enableAlias {
|
||||
// With Alias nodes we can process declarations in any order.
|
||||
for _, obj := range objList {
|
||||
check.objDecl(obj, nil)
|
||||
}
|
||||
} else {
|
||||
// Without _Alias nodes, we process non-alias type declarations first, followed by
|
||||
// Without Alias nodes, we process non-alias type declarations first, followed by
|
||||
// alias declarations, and then everything else. This appears to avoid most situations
|
||||
// where the type of an alias is needed before it is available.
|
||||
// There may still be cases where this is not good enough (see also go.dev/issue/25838).
|
||||
|
@ -211,7 +211,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
||||
check.later(func() {
|
||||
// spec: "The receiver type must be of the form T or *T where T is a type name."
|
||||
rtyp, _ := deref(recv.typ)
|
||||
atyp := _Unalias(rtyp)
|
||||
atyp := Unalias(rtyp)
|
||||
if !isValid(atyp) {
|
||||
return // error was reported before
|
||||
}
|
||||
|
@ -329,13 +329,13 @@ func (w *typeWriter) typ(typ Type) {
|
||||
}
|
||||
}
|
||||
|
||||
case *_Alias:
|
||||
case *Alias:
|
||||
w.typeName(t.obj)
|
||||
if w.ctxt != nil {
|
||||
// TODO(gri) do we need to print the alias type name, too?
|
||||
w.typ(_Unalias(t.obj.typ))
|
||||
w.typ(Unalias(t.obj.typ))
|
||||
} else {
|
||||
w.string(fmt.Sprintf(" /* = %s */", _Unalias(t.obj.typ)))
|
||||
w.string(fmt.Sprintf(" /* = %s */", Unalias(t.obj.typ)))
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -95,7 +95,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *TypeName, wantType bo
|
||||
x.mode = constant_
|
||||
|
||||
case *TypeName:
|
||||
if !check.conf._EnableAlias && check.isBrokenAlias(obj) {
|
||||
if !check.enableAlias && check.isBrokenAlias(obj) {
|
||||
check.errorf(e, InvalidDeclCycle, "invalid use of type alias %s in recursive type (see go.dev/issue/50729)", obj.name)
|
||||
return
|
||||
}
|
||||
@ -394,7 +394,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *TypeName) (T Type) {
|
||||
func setDefType(def *TypeName, typ Type) {
|
||||
if def != nil {
|
||||
switch t := def.typ.(type) {
|
||||
case *_Alias:
|
||||
case *Alias:
|
||||
// t.fromRHS should always be set, either to an invalid type
|
||||
// in the beginning, or to typ in certain cyclic declarations.
|
||||
if t.fromRHS != Typ[Invalid] && t.fromRHS != typ {
|
||||
|
@ -293,8 +293,8 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
||||
u.depth--
|
||||
}()
|
||||
|
||||
x = _Unalias(x)
|
||||
y = _Unalias(y)
|
||||
x = Unalias(x)
|
||||
y = Unalias(y)
|
||||
|
||||
// nothing to do if x == y
|
||||
if x == y {
|
||||
|
@ -25,7 +25,7 @@ func (check *Checker) validType(typ *Named) {
|
||||
// (say S->F->S) we have an invalid recursive type. The path list is the full
|
||||
// path of named types in a cycle, it is only needed for error reporting.
|
||||
func (check *Checker) validType0(typ Type, nest, path []*Named) bool {
|
||||
switch t := _Unalias(typ).(type) {
|
||||
switch t := Unalias(typ).(type) {
|
||||
case nil:
|
||||
// We should never see a nil type but be conservative and panic
|
||||
// only in debug mode.
|
||||
|
@ -29,6 +29,7 @@ var All = []Info{
|
||||
{Name: "gocachehash", Package: "cmd/go"},
|
||||
{Name: "gocachetest", Package: "cmd/go"},
|
||||
{Name: "gocacheverify", Package: "cmd/go"},
|
||||
{Name: "gotypesalias", Package: "go/types"},
|
||||
{Name: "http2client", Package: "net/http"},
|
||||
{Name: "http2debug", Package: "net/http", Opaque: true},
|
||||
{Name: "http2server", Package: "net/http"},
|
||||
|
2
src/internal/types/testdata/check/cycles5.go
vendored
2
src/internal/types/testdata/check/cycles5.go
vendored
@ -1,4 +1,4 @@
|
||||
// -alias=false
|
||||
// -gotypesalias=0
|
||||
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
@ -1,4 +1,4 @@
|
||||
// -alias
|
||||
// -gotypesalias=1
|
||||
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
@ -1,4 +1,4 @@
|
||||
// -alias=false
|
||||
// -gotypesalias=0
|
||||
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
@ -1,4 +1,4 @@
|
||||
// -alias
|
||||
// -gotypesalias=1
|
||||
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
@ -1,4 +1,4 @@
|
||||
// -alias=false
|
||||
// -gotypesalias=0
|
||||
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
@ -1,4 +1,4 @@
|
||||
// -alias
|
||||
// -gotypesalias=1
|
||||
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
@ -246,6 +246,10 @@ Below is the full list of supported metrics, ordered lexicographically.
|
||||
The number of non-default behaviors executed by the cmd/go
|
||||
package due to a non-default GODEBUG=gocacheverify=... 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…
Reference in New Issue
Block a user