type anyType struct{}
func (t anyType) Underlying() types2.Type { return t }
-func (t anyType) Under() types2.Type { return t }
func (t anyType) String() string { return "any" }
mode := invalid
var typ Type
var val constant.Value
- switch typ = implicitArrayDeref(optype(x.typ.Under())); t := typ.(type) {
+ switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) {
case *Basic:
if isString(t) && id == _Len {
if x.mode == constant_ {
case *Sum:
if t.is(func(t Type) bool {
- switch t := t.Under().(type) {
+ switch t := under(t).(type) {
case *Basic:
if isString(t) && id == _Len {
return true
return
}
var src Type
- switch t := optype(y.typ.Under()).(type) {
+ switch t := optype(y.typ).(type) {
case *Basic:
if isString(y.typ) {
src = universeByte
var valid func(t Type) bool
valid = func(t Type) bool {
var m int
- switch t := optype(t.Under()).(type) {
+ switch t := optype(t).(type) {
case *Slice:
m = 2
case *Map, *Chan:
// "x's type and T have identical underlying types if tags are ignored"
V := x.typ
- Vu := V.Under()
- Tu := T.Under()
+ Vu := under(V)
+ Tu := under(T)
if check.identicalIgnoreTags(Vu, Tu) {
return true
}
// have identical underlying types if tags are ignored"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
- if check.identicalIgnoreTags(V.base.Under(), T.base.Under()) {
+ if check.identicalIgnoreTags(under(V.base), under(T.base)) {
return true
}
}
func isUnsafePointer(typ Type) bool {
// TODO(gri): Is this asBasic(typ) instead of typ.(*Basic) correct?
- // (The former calls typ.Under(), while the latter doesn't.)
+ // (The former calls under(), while the latter doesn't.)
// The spec does not say so, but gc claims it is. See also
// issue 6326.
t := asBasic(typ)
if !isConstType(t) {
// don't report an error if the type is an invalid C (defined) type
// (issue #22090)
- if t.Under() != Typ[Invalid] {
+ if under(t) != Typ[Invalid] {
check.errorf(typ, "invalid constant type %s", t)
}
obj.typ = Typ[Invalid]
check.initVars(lhs, []syntax.Expr{init}, nopos)
}
-// Under returns the expanded underlying type of n0; possibly by following
+// under returns the expanded underlying type of n0; possibly by following
// forward chains of named types. If an underlying type is found, resolve
// the chain by setting the underlying type for each defined type in the
// chain before returning it. If no underlying type is found or a cycle
// is detected, the result is Typ[Invalid]. If a cycle is detected and
// n0.check != nil, the cycle is reported.
-func (n0 *Named) Under() Type {
+func (n0 *Named) under() Type {
u := n0.underlying
if u == nil {
return Typ[Invalid]
if i, ok := seen[n]; ok {
// cycle
+ // TODO(gri) revert this to a method on Checker. Having a possibly
+ // nil Checker on Named and TypeParam is too subtle.
if n0.check != nil {
n0.check.cycleError(path[i:])
}
// any forward chain.
// TODO(gri) Investigate if we can just use named.origin here
// and rely on lazy computation of the underlying type.
- named.underlying = named.Under()
+ named.underlying = under(named)
}
}
// we may not have a complete interface yet:
// type C(type T C) interface {}
// (issue #39724).
- if _, ok := bound.Under().(*Interface); ok {
+ if _, ok := under(bound).(*Interface); ok {
// set the type bounds
for i < j {
tparams[i].typ.(*TypeParam).bound = bound
}
// typed target
- switch t := optype(target.Under()).(type) {
+ switch t := optype(target).(type) {
case *Basic:
if x.mode == constant_ {
check.representable(x, t)
case hint != nil:
// no composite literal type present - use hint (element type of enclosing type)
typ = hint
- base, _ = deref(typ.Under()) // *T implies &T{}
+ base, _ = deref(under(typ)) // *T implies &T{}
default:
// TODO(gri) provide better error messages depending on context
goto Error
}
- switch utyp := optype(base.Under()).(type) {
+ switch utyp := optype(base).(type) {
case *Struct:
if len(e.ElemList) == 0 {
break
// ordinary index expression
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ.Under()).(type) {
+ switch typ := optype(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
nmaps := 0 // number of map types in sum type
if typ.is(func(t Type) bool {
var e Type
- switch t := t.Under().(type) {
+ switch t := under(t).(type) {
case *Basic:
if isString(t) {
e = universeByte
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ.Under()).(type) {
+ switch typ := optype(x.typ).(type) {
case *Basic:
if isString(typ) {
if e.Full {
if x.mode == invalid {
goto Error
}
- xtyp, _ := x.typ.Under().(*Interface)
+ xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
check.errorf(x, "%s is not an interface type", x)
goto Error
// Unify type parameters with their structural constraints, if any.
for _, tpar := range tparams {
typ := tpar.typ.(*TypeParam)
- sbound := check.structuralType(typ.bound.Under())
+ sbound := check.structuralType(typ.bound)
if sbound != nil {
if !u.unify(typ, sbound) {
check.errorf(tpar.pos, "%s does not match %s", tpar, sbound)
// structuralType returns the structural type of a constraint, if any.
func (check *Checker) structuralType(constraint Type) Type {
- if iface, _ := constraint.(*Interface); iface != nil {
+ if iface, _ := under(constraint).(*Interface); iface != nil {
check.completeInterface(nopos, iface)
types := unpack(iface.allTypes)
if len(types) == 1 {
// continue with underlying type, but only if it's not a type parameter
// TODO(gri) is this what we want to do for type parameters? (spec question)
- typ = named.Under()
+ typ = under(named)
if asTypeParam(typ) != nil {
continue
}
if tname.IsAlias() {
buf.WriteString(" =")
} else {
- typ = typ.Under()
+ typ = under(typ)
}
}
return true
}
- Vu := optype(V.Under())
- Tu := optype(T.Under())
+ Vu := optype(V)
+ Tu := optype(T)
// x is an untyped value representable by a value of type T
// TODO(gri) This is borrowing from checker.convertUntyped and
}
func is(typ Type, what BasicInfo) bool {
- switch t := optype(typ.Under()).(type) {
+ switch t := optype(typ).(type) {
case *Basic:
return t.info&what != 0
case *Sum:
func isConstType(typ Type) bool {
// Type parameters are never const types.
- t, _ := typ.Under().(*Basic)
+ t, _ := under(typ).(*Basic)
return t != nil && t.info&IsConstType != 0
}
return t.Bound().IsComparable()
}
- switch t := optype(T.Under()).(type) {
+ switch t := optype(T).(type) {
case *Basic:
// assume invalid types to be comparable
// to avoid follow-up errors
// hasNil reports whether a type includes the nil value.
func hasNil(typ Type) bool {
- switch t := optype(typ.Under()).(type) {
+ switch t := optype(typ).(type) {
case *Basic:
return t.kind == UnsafePointer
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
func (s *StdSizes) Alignof(T Type) int64 {
// For arrays and structs, alignment is defined in terms
// of alignment of the elements and fields, respectively.
- switch t := optype(T.Under()).(type) {
+ switch t := optype(T).(type) {
case *Array:
// spec: "For a variable x of array type: unsafe.Alignof(x)
// is the same as unsafe.Alignof(x[0]), but at least 1."
}
func (s *StdSizes) Sizeof(T Type) int64 {
- switch t := optype(T.Under()).(type) {
+ switch t := optype(T).(type) {
case *Basic:
assert(isTyped(T))
k := t.kind
if x.mode == invalid {
return
}
- xtyp, _ := x.typ.Under().(*Interface)
+ xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
check.errorf(&x, "%s is not an interface type", &x)
return
// determine key/value types
var key, val Type
if x.mode != invalid {
- typ := optype(x.typ.Under())
+ typ := optype(x.typ)
if _, ok := typ.(*Chan); ok && sValue != nil {
// TODO(gri) this also needs to happen for channels in generic variables
check.softErrorf(sValue, "range over %s permits only one iteration variable", &x)
var key, val Type
var msg string
typ.is(func(t Type) bool {
- k, v, m := rangeKeyVal(t.Under(), wantKey, wantVal)
+ k, v, m := rangeKeyVal(under(t), wantKey, wantVal)
if k == nil || m != "" {
key, val, msg = k, v, m
return false
check.indent--
var under Type
if res != nil {
- // Calling Under() here may lead to endless instantiations.
+ // Calling under() here may lead to endless instantiations.
// Test case: type T[P any] T[P]
// TODO(gri) investigate if that's a bug or to be expected.
under = res.Underlying()
break
}
for _, t := range unpack(targBound.allTypes) {
- if !iface.isSatisfiedBy(t.Under()) {
+ if !iface.isSatisfiedBy(t) {
// TODO(gri) match this error message with the one below (or vice versa)
check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
break
// Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
if !iface.isSatisfiedBy(targ) {
- check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, targ.Under(), iface.allTypes)
+ check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, under(targ), iface.allTypes)
break
}
}
// client packages (here for backward-compatibility).
Underlying() Type
- // Under returns the true expanded underlying type.
- // If it doesn't exist, the result is Typ[Invalid].
- // Under must only be called when a type is known
- // to be fully set up.
- Under() Type
-
// String returns a string representation of a type.
String() string
}
-// aType implements default type behavior
-type aType struct{}
-
-// These methods must be implemented by each type.
-func (aType) Underlying() Type { panic("unreachable") }
-func (aType) Under() Type { panic("unreachable") }
-func (aType) String() string { panic("unreachable") }
-
// BasicKind describes the kind of basic type.
type BasicKind int
kind BasicKind
info BasicInfo
name string
- aType
}
// Kind returns the kind of basic type b.
type Array struct {
len int64
elem Type
- aType
}
// NewArray returns a new array type for the given element type and length.
// A Slice represents a slice type.
type Slice struct {
elem Type
- aType
}
// NewSlice returns a new slice type for the given element type.
type Struct struct {
fields []*Var
tags []string // field tags; nil if there are no tags
- aType
}
// NewStruct returns a new struct with the given fields and corresponding field tags.
// A Pointer represents a pointer type.
type Pointer struct {
base Type // element type
- aType
}
// NewPointer returns a new pointer type for the given element (base) type.
// assignments; they are not first class types of Go.
type Tuple struct {
vars []*Var
- aType
}
-// TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer;
-// it's too subtle and causes problems. Use a singleton instead.
-
// NewTuple returns a new tuple for the given variables.
func NewTuple(x ...*Var) *Tuple {
if len(x) > 0 {
return &Tuple{vars: x}
}
+ // TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer;
+ // it's too subtle and causes problems.
return nil
}
params *Tuple // (incoming) parameters from left to right; or nil
results *Tuple // (outgoing) results from left to right; or nil
variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
- aType
}
// NewSignature returns a new function type for the given receiver, parameters,
// first class types of Go.
type Sum struct {
types []Type // types are unique
- aType
}
// NewSum returns a new Sum type consisting of the provided
allTypes Type // intersection of all embedded and locally declared types (TODO(gri) need better field name)
obj Object // type declaration defining this interface; or nil (for better error messages)
-
- aType
}
// unpack unpacks a type into a list of types.
return len(t.allMethods) == 0 && t.allTypes == nil
}
return !t.iterate(func(t *Interface) bool {
- if len(t.methods) > 0 || t.types != nil {
- return true
- }
- return false
+ return len(t.methods) > 0 || t.types != nil
}, nil)
}
}
return t.iterate(func(t *Interface) bool {
- if t.types != nil {
- return true
- }
- return false
+ return t.types != nil
}, nil)
}
return true
}
types := unpack(t.allTypes)
- return includes(types, typ) || includes(types, typ.Under())
+ return includes(types, typ) || includes(types, under(typ))
}
// Complete computes the interface's method set. It must be called by users of
allTypes := t.types
for _, typ := range t.embeddeds {
- utyp := typ.Under()
+ utyp := under(typ)
etyp := asInterface(utyp)
if etyp == nil {
if utyp != Typ[Invalid] {
// A Map represents a map type.
type Map struct {
key, elem Type
- aType
}
// NewMap returns a new map for the given key and element types.
type Chan struct {
dir ChanDir
elem Type
- aType
}
// A ChanDir value indicates a channel direction.
// A Named represents a named (defined) type.
type Named struct {
- check *Checker // for Named.Under implementation
+ check *Checker // for Named.under implementation
info typeInfo // for cycle detection
obj *TypeName // corresponding declared object
orig Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
tparams []*TypeName // type parameters, or nil
targs []Type // type arguments (after instantiation), or nil
methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
- aType
}
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
obj *TypeName // corresponding type name
index int // parameter index
bound Type // *Named or *Interface; underlying type is always *Interface
- aType
}
func (t *TypeParam) Obj() *TypeName {
// optype returns a type's operational type. Except for
// type parameters, the operational type is the same
-// as the underlying type (as returned by Under). For
+// as the underlying type (as returned by under). For
// Type parameters, the operational type is determined
// by the corresponding type bound's type list. The
// result may be the bottom or top type, but it is never
// (type T interface { type T }).
// See also issue #39680.
if u := t.Bound().allTypes; u != nil && u != typ {
- // u != typ and u is a type parameter => u.Under() != typ, so this is ok
- return u.Under()
+ // u != typ and u is a type parameter => under(u) != typ, so this is ok
+ return under(u)
}
return theTop
}
- return typ.Under()
+ return under(typ)
}
// An instance represents an instantiated generic type syntactically
targs []Type // type arguments
poslist []syntax.Pos // position of each targ; for error reporting only
value Type // base(targs...) after instantiation or Typ[Invalid]; nil if not yet set
- aType
}
// expand returns the instantiated (= expanded) type of t.
// It is the underlying type of a type parameter that
// cannot be satisfied by any type, usually because
// the intersection of type constraints left nothing).
-type bottom struct {
- aType
-}
+type bottom struct{}
// theBottom is the singleton bottom type.
var theBottom = &bottom{}
// can be satisfied by any type (ignoring methods),
// usually because the type constraint has no type
// list.
-type top struct {
- aType
-}
+type top struct{}
// theTop is the singleton top type.
var theTop = &top{}
func (t *bottom) Underlying() Type { return t }
func (t *top) Underlying() Type { return t }
-// Type-specific implementations of Under.
-func (t *Basic) Under() Type { return t }
-func (t *Array) Under() Type { return t }
-func (t *Slice) Under() Type { return t }
-func (t *Struct) Under() Type { return t }
-func (t *Pointer) Under() Type { return t }
-func (t *Tuple) Under() Type { return t }
-func (t *Signature) Under() Type { return t }
-func (t *Sum) Under() Type { return t } // TODO(gri) is this correct?
-func (t *Interface) Under() Type { return t }
-func (t *Map) Under() Type { return t }
-func (t *Chan) Under() Type { return t }
-
-// see decl.go for implementation of Named.Under
-func (t *TypeParam) Under() Type { return t }
-func (t *instance) Under() Type { return t.expand().Under() }
-func (t *bottom) Under() Type { return t }
-func (t *top) Under() Type { return t }
-
// Type-specific implementations of String.
func (t *Basic) String() string { return TypeString(t, nil) }
func (t *Array) String() string { return TypeString(t, nil) }
func (t *bottom) String() string { return TypeString(t, nil) }
func (t *top) String() string { return TypeString(t, nil) }
+// under returns the true expanded underlying type.
+// If it doesn't exist, the result is Typ[Invalid].
+// under must only be called when a type is known
+// to be fully set up.
+//
+// under is set to underf to avoid an initialization cycle.
+// TODO(gri) this doesn't happen in go/types - investigate
+var under func(Type) Type
+
+func init() {
+ under = underf
+}
+
+func underf(t Type) Type {
+ // TODO(gri) is this correct for *Sum?
+ if n := asNamed(t); n != nil {
+ return n.under()
+ }
+ return t
+}
+
// Converters
//
// A converter must only be called when a type is
}
func asTypeParam(t Type) *TypeParam {
- u, _ := t.Under().(*TypeParam)
+ u, _ := under(t).(*TypeParam)
return u
}
// ordinaryType reports an error if typ is an interface type containing
// type lists or is (or embeds) the predeclared type comparable.
func (check *Checker) ordinaryType(pos syntax.Pos, typ Type) {
- // We don't want to call Under() (via Interface) or complete interfaces while we
+ // We don't want to call under() (via Interface) or complete interfaces while we
// are in the middle of type-checking parameter declarations that might belong to
// interface methods. Delay this check to the end of type-checking.
check.atEnd(func() {
err = ""
}
} else {
- switch u := optype(T.Under()).(type) {
+ switch u := optype(T).(type) {
case *Basic:
// unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer {
check.indent--
var under Type
if T != nil {
- // Calling Under() here may lead to endless instantiations.
+ // Calling under() here may lead to endless instantiations.
// Test case: type T[P any] *T[P]
// TODO(gri) investigate if that's a bug or to be expected
// (see also analogous comment in Checker.instantiate).
posList := check.posMap[ityp]
for i, typ := range ityp.embeddeds {
pos := posList[i] // embedding position
- utyp := typ.Under()
+ utyp := under(typ)
etyp := asInterface(utyp)
if etyp == nil {
if utyp != Typ[Invalid] {
// Because we have a name, typ must be of the form T or *T, where T is the name
// of a (named or alias) type, and t (= deref(typ)) must be the type of T.
// We must delay this check to the end because we don't want to instantiate
- // (via t.Under()) a possibly incomplete type.
+ // (via under(t)) a possibly incomplete type.
embeddedTyp := typ // for closure below
embeddedPos := pos
check.atEnd(func() {
t, isPtr := deref(embeddedTyp)
- switch t := optype(t.Under()).(type) {
+ switch t := optype(t).(type) {
case *Basic:
if t == Typ[Invalid] {
// error was reported before
// want *Named types.)
switch {
case !isNamed(x) && y != nil && asNamed(y) != nil:
- return u.nify(x, y.Under(), p)
+ return u.nify(x, under(y), p)
case x != nil && asNamed(x) != nil && !isNamed(y):
- return u.nify(x.Under(), y, p)
+ return u.nify(under(x), y, p)
}
}
// Use Universe.Lookup("byte").Type() to obtain the specific
// alias basic type named "byte" (and analogous for "rune").
var Typ = [...]*Basic{
- Invalid: {Invalid, 0, "invalid type", aType{}},
-
- Bool: {Bool, IsBoolean, "bool", aType{}},
- Int: {Int, IsInteger, "int", aType{}},
- Int8: {Int8, IsInteger, "int8", aType{}},
- Int16: {Int16, IsInteger, "int16", aType{}},
- Int32: {Int32, IsInteger, "int32", aType{}},
- Int64: {Int64, IsInteger, "int64", aType{}},
- Uint: {Uint, IsInteger | IsUnsigned, "uint", aType{}},
- Uint8: {Uint8, IsInteger | IsUnsigned, "uint8", aType{}},
- Uint16: {Uint16, IsInteger | IsUnsigned, "uint16", aType{}},
- Uint32: {Uint32, IsInteger | IsUnsigned, "uint32", aType{}},
- Uint64: {Uint64, IsInteger | IsUnsigned, "uint64", aType{}},
- Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr", aType{}},
- Float32: {Float32, IsFloat, "float32", aType{}},
- Float64: {Float64, IsFloat, "float64", aType{}},
- Complex64: {Complex64, IsComplex, "complex64", aType{}},
- Complex128: {Complex128, IsComplex, "complex128", aType{}},
- String: {String, IsString, "string", aType{}},
- UnsafePointer: {UnsafePointer, 0, "Pointer", aType{}},
-
- UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool", aType{}},
- UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int", aType{}},
- UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune", aType{}},
- UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float", aType{}},
- UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex", aType{}},
- UntypedString: {UntypedString, IsString | IsUntyped, "untyped string", aType{}},
- UntypedNil: {UntypedNil, IsUntyped, "untyped nil", aType{}},
+ Invalid: {Invalid, 0, "invalid type"},
+
+ Bool: {Bool, IsBoolean, "bool"},
+ Int: {Int, IsInteger, "int"},
+ Int8: {Int8, IsInteger, "int8"},
+ Int16: {Int16, IsInteger, "int16"},
+ Int32: {Int32, IsInteger, "int32"},
+ Int64: {Int64, IsInteger, "int64"},
+ Uint: {Uint, IsInteger | IsUnsigned, "uint"},
+ Uint8: {Uint8, IsInteger | IsUnsigned, "uint8"},
+ Uint16: {Uint16, IsInteger | IsUnsigned, "uint16"},
+ Uint32: {Uint32, IsInteger | IsUnsigned, "uint32"},
+ Uint64: {Uint64, IsInteger | IsUnsigned, "uint64"},
+ Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr"},
+ Float32: {Float32, IsFloat, "float32"},
+ Float64: {Float64, IsFloat, "float64"},
+ Complex64: {Complex64, IsComplex, "complex64"},
+ Complex128: {Complex128, IsComplex, "complex128"},
+ String: {String, IsString, "string"},
+ UnsafePointer: {UnsafePointer, 0, "Pointer"},
+
+ UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool"},
+ UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int"},
+ UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune"},
+ UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float"},
+ UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
+ UntypedString: {UntypedString, IsString | IsUntyped, "untyped string"},
+ UntypedNil: {UntypedNil, IsUntyped, "untyped nil"},
}
var aliases = [...]*Basic{
- {Byte, IsInteger | IsUnsigned, "byte", aType{}},
- {Rune, IsInteger, "rune", aType{}},
+ {Byte, IsInteger | IsUnsigned, "byte"},
+ {Rune, IsInteger, "rune"},
}
func defPredeclaredTypes() {