package types
import (
+ "fmt"
"go/ast"
"go/constant"
"go/token"
"sort"
"strconv"
+ "strings"
)
// ident type-checks identifier e and initializes x with the value or type of e.
}
// typ type-checks the type expression e and returns its type, or Typ[Invalid].
+// The type must not be an (uninstantiated) generic type.
func (check *Checker) typ(e ast.Expr) Type {
return check.definedType(e, nil)
}
+// varType type-checks the type expression e and returns its type, or Typ[Invalid].
+// The type must not be an (uninstantiated) generic type and it must be ordinary
+// (see ordinaryType).
+func (check *Checker) varType(e ast.Expr) Type {
+ typ := check.definedType(e, nil)
+ check.ordinaryType(e, typ)
+ return typ
+}
+
+// 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 positioner, typ Type) {
+ // We don't want to call under() (via asInterface) 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() {
+ if t := asInterface(typ); t != nil {
+ check.completeInterface(pos.Pos(), t) // TODO(gri) is this the correct position?
+ if t.allTypes != nil {
+ check.softErrorf(pos, _Todo, "interface contains type constraints (%s)", t.allTypes)
+ return
+ }
+ if t.IsComparable() {
+ check.softErrorf(pos, _Todo, "interface is (or embeds) comparable")
+ }
+ }
+ })
+}
+
+// anyType type-checks the type expression e and returns its type, or Typ[Invalid].
+// The type may be generic or instantiated.
+func (check *Checker) anyType(e ast.Expr) Type {
+ typ := check.typInternal(e, nil)
+ assert(isTyped(typ))
+ check.recordTypeAndValue(e, typexpr, typ, nil)
+ return typ
+}
+
// definedType is like typ but also accepts a type name def.
// If def != nil, e is the type specification for the defined type def, declared
// in a type declaration, and def.underlying will be set to the type of e before
// any components of e are type-checked.
//
-func (check *Checker) definedType(e ast.Expr, def *Named) (T Type) {
- if trace {
- check.trace(e.Pos(), "%s", e)
- check.indent++
- defer func() {
- check.indent--
- check.trace(e.Pos(), "=> %s", T)
- }()
+func (check *Checker) definedType(e ast.Expr, def *Named) Type {
+ typ := check.typInternal(e, def)
+ assert(isTyped(typ))
+ if isGeneric(typ) {
+ check.errorf(e, _Todo, "cannot use generic type %s without instantiation", typ)
+ typ = Typ[Invalid]
}
+ check.recordTypeAndValue(e, typexpr, typ, nil)
+ return typ
+}
- T = check.typInternal(e, def)
- assert(isTyped(T))
- check.recordTypeAndValue(e, typexpr, T, nil)
+// genericType is like typ but the type must be an (uninstantiated) generic type.
+func (check *Checker) genericType(e ast.Expr, reportErr bool) Type {
+ typ := check.typInternal(e, nil)
+ assert(isTyped(typ))
+ if typ != Typ[Invalid] && !isGeneric(typ) {
+ if reportErr {
+ check.errorf(e, _Todo, "%s is not a generic type", typ)
+ }
+ typ = Typ[Invalid]
+ }
+ // TODO(gri) what is the correct call below?
+ check.recordTypeAndValue(e, typexpr, typ, nil)
+ return typ
+}
- return
+// isubst returns an x with identifiers substituted per the substitution map smap.
+// isubst only handles the case of (valid) method receiver type expressions correctly.
+func isubst(x ast.Expr, smap map[*ast.Ident]*ast.Ident) ast.Expr {
+ switch n := x.(type) {
+ case *ast.Ident:
+ if alt := smap[n]; alt != nil {
+ return alt
+ }
+ case *ast.StarExpr:
+ X := isubst(n.X, smap)
+ if X != n.X {
+ new := *n
+ new.X = X
+ return &new
+ }
+ case *ast.CallExpr:
+ var args []ast.Expr
+ for i, arg := range n.Args {
+ new := isubst(arg, smap)
+ if new != arg {
+ if args == nil {
+ args = make([]ast.Expr, len(n.Args))
+ copy(args, n.Args)
+ }
+ args[i] = new
+ }
+ }
+ if args != nil {
+ new := *n
+ new.Args = args
+ return &new
+ }
+ case *ast.ParenExpr:
+ return isubst(n.X, smap) // no need to keep parentheses
+ default:
+ // Other receiver type expressions are invalid.
+ // It's fine to ignore those here as they will
+ // be checked elsewhere.
+ }
+ return x
}
// funcType type-checks a function or method type.
func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) {
- scope := NewScope(check.scope, token.NoPos, token.NoPos, "function")
- scope.isFunc = true
- check.recordScope(ftyp, scope)
+ check.openScope(ftyp, "function")
+ check.scope.isFunc = true
+ check.recordScope(ftyp, check.scope)
+ sig.scope = check.scope
+ defer check.closeScope()
+
+ var recvTyp ast.Expr // rewritten receiver type; valid if != nil
+ if recvPar != nil && len(recvPar.List) > 0 {
+ // collect generic receiver type parameters, if any
+ // - a receiver type parameter is like any other type parameter, except that it is declared implicitly
+ // - the receiver specification acts as local declaration for its type parameters, which may be blank
+ _, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true)
+ if len(rparams) > 0 {
+ // Blank identifiers don't get declared and regular type-checking of the instantiated
+ // parameterized receiver type expression fails in Checker.collectParams of receiver.
+ // Identify blank type parameters and substitute each with a unique new identifier named
+ // "n_" (where n is the parameter index) and which cannot conflict with any user-defined
+ // name.
+ var smap map[*ast.Ident]*ast.Ident // substitution map from "_" to "n_" identifiers
+ for i, p := range rparams {
+ if p.Name == "_" {
+ new := *p
+ new.Name = fmt.Sprintf("%d_", i)
+ rparams[i] = &new // use n_ identifier instead of _ so it can be looked up
+ if smap == nil {
+ smap = make(map[*ast.Ident]*ast.Ident)
+ }
+ smap[p] = &new
+ }
+ }
+ if smap != nil {
+ // blank identifiers were found => use rewritten receiver type
+ recvTyp = isubst(recvPar.List[0].Type, smap)
+ }
+ sig.rparams = check.declareTypeParams(nil, rparams)
+ // determine receiver type to get its type parameters
+ // and the respective type parameter bounds
+ var recvTParams []*TypeName
+ if rname != nil {
+ // recv should be a Named type (otherwise an error is reported elsewhere)
+ // Also: Don't report an error via genericType since it will be reported
+ // again when we type-check the signature.
+ // TODO(gri) maybe the receiver should be marked as invalid instead?
+ if recv := asNamed(check.genericType(rname, false)); recv != nil {
+ recvTParams = recv.tparams
+ }
+ }
+ // provide type parameter bounds
+ // - only do this if we have the right number (otherwise an error is reported elsewhere)
+ if len(sig.rparams) == len(recvTParams) {
+ // We have a list of *TypeNames but we need a list of Types.
+ list := make([]Type, len(sig.rparams))
+ for i, t := range sig.rparams {
+ list[i] = t.typ
+ }
+ smap := makeSubstMap(recvTParams, list)
+ for i, tname := range sig.rparams {
+ bound := recvTParams[i].typ.(*TypeParam).bound
+ // bound is (possibly) parameterized in the context of the
+ // receiver type declaration. Substitute parameters for the
+ // current context.
+ // TODO(gri) should we assume now that bounds always exist?
+ // (no bound == empty interface)
+ if bound != nil {
+ bound = check.subst(tname.pos, bound, smap)
+ tname.typ.(*TypeParam).bound = bound
+ }
+ }
+ }
+ }
+ }
+
+ if ftyp.TParams != nil {
+ sig.tparams = check.collectTypeParams(ftyp.TParams)
+ // Always type-check method type parameters but complain that they are not allowed.
+ // (A separate check is needed when type-checking interface method signatures because
+ // they don't have a receiver specification.)
+ if recvPar != nil {
+ check.errorf(ftyp.TParams, _Todo, "methods cannot have type parameters")
+ }
+ }
- recvList, _ := check.collectParams(scope, recvPar, false)
- params, variadic := check.collectParams(scope, ftyp.Params, true)
- results, _ := check.collectParams(scope, ftyp.Results, false)
+ // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
+ // declarations and then squash that scope into the parent scope (and report any redeclarations at
+ // that time).
+ scope := NewScope(check.scope, token.NoPos, token.NoPos, "function body (temp. scope)")
+ recvList, _ := check.collectParams(scope, recvPar, recvTyp, false) // use rewritten receiver type, if any
+ params, variadic := check.collectParams(scope, ftyp.Params, nil, true)
+ results, _ := check.collectParams(scope, ftyp.Results, nil, false)
+ scope.Squash(func(obj, alt Object) {
+ check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
+ })
if recvPar != nil {
// recv parameter list present (may be empty)
var recv *Var
switch len(recvList) {
case 0:
- check.error(recvPar, _BadRecv, "method is missing receiver")
+ // error reported by resolver
recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
default:
// more than one receiver
case 1:
recv = recvList[0]
}
+
+ // TODO(gri) We should delay rtyp expansion to when we actually need the
+ // receiver; thus all checks here should be delayed to later.
+ rtyp, _ := deref(recv.typ)
+ rtyp = expand(rtyp)
+
// spec: "The receiver type must be of the form T or *T where T is a type name."
// (ignore invalid types - error was reported before)
- if t, _ := deref(recv.typ); t != Typ[Invalid] {
+ if t := rtyp; t != Typ[Invalid] {
var err string
- if T, _ := t.(*Named); T != nil {
+ if T := asNamed(t); T != nil {
// spec: "The type denoted by T is called the receiver base type; it must not
// be a pointer or interface type and it must be declared in the same package
// as the method."
if T.obj.pkg != check.pkg {
err = "type not defined in this package"
} else {
- // TODO(gri) This is not correct if the underlying type is unknown yet.
- switch u := T.underlying.(type) {
+ switch u := optype(T).(type) {
case *Basic:
// unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer {
sig.recv = recv
}
- sig.scope = scope
sig.params = NewTuple(params...)
sig.results = NewTuple(results...)
sig.variadic = variadic
}
+// goTypeName returns the Go type name for typ and
+// removes any occurences of "types." from that name.
+func goTypeName(typ Type) string {
+ return strings.ReplaceAll(fmt.Sprintf("%T", typ), "types.", "")
+}
+
// typInternal drives type checking of types.
-// Must only be called by definedType.
+// Must only be called by definedType or genericType.
//
-func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
- switch e := e.(type) {
+func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
+ if trace {
+ check.trace(e0.Pos(), "type %s", e0)
+ check.indent++
+ defer func() {
+ check.indent--
+ var under Type
+ if T != nil {
+ // 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).
+ under = T.Underlying()
+ }
+ if T == under {
+ check.trace(e0.Pos(), "=> %s // %s", T, goTypeName(T))
+ } else {
+ check.trace(e0.Pos(), "=> %s (under = %s) // %s", T, under, goTypeName(T))
+ }
+ }()
+ }
+
+ switch e := e0.(type) {
case *ast.BadExpr:
// ignore - error reported before
check.errorf(&x, _NotAType, "%s is not a type", &x)
}
+ case *ast.IndexExpr:
+ return check.instantiatedType(e.X, []ast.Expr{e.Index}, def)
+
+ case *ast.CallExpr:
+ if e.Brackets {
+ return check.instantiatedType(e.Fun, e.Args, def)
+ } else {
+ check.errorf(e0, _NotAType, "%s is not a type", e0)
+ }
+
case *ast.ParenExpr:
+ // Generic types must be instantiated before they can be used in any form.
+ // Consequently, generic types cannot be parenthesized.
return check.definedType(e.X, def)
case *ast.ArrayType:
typ := new(Array)
def.setUnderlying(typ)
typ.len = check.arrayLength(e.Len)
- typ.elem = check.typ(e.Elt)
- return typ
-
- } else {
- typ := new(Slice)
- def.setUnderlying(typ)
- typ.elem = check.typ(e.Elt)
+ typ.elem = check.varType(e.Elt)
return typ
}
+ typ := new(Slice)
+ def.setUnderlying(typ)
+ typ.elem = check.varType(e.Elt)
+ return typ
+
+ case *ast.Ellipsis:
+ // dots are handled explicitly where they are legal
+ // (array composite literals and parameter lists)
+ check.error(e, _InvalidDotDotDot, "invalid use of '...'")
+ check.use(e.Elt)
+
case *ast.StructType:
typ := new(Struct)
def.setUnderlying(typ)
case *ast.StarExpr:
typ := new(Pointer)
def.setUnderlying(typ)
- typ.base = check.typ(e.X)
+ typ.base = check.varType(e.X)
return typ
case *ast.FuncType:
case *ast.InterfaceType:
typ := new(Interface)
def.setUnderlying(typ)
+ if def != nil {
+ typ.obj = def.obj
+ }
check.interfaceType(typ, e, def)
return typ
typ := new(Map)
def.setUnderlying(typ)
- typ.key = check.typ(e.Key)
- typ.elem = check.typ(e.Value)
+ typ.key = check.varType(e.Key)
+ typ.elem = check.varType(e.Value)
// spec: "The comparison operators == and != must be fully defined
// for operands of the key type; thus the key type must not be a
// it is safe to continue in any case (was issue 6667).
check.atEnd(func() {
if !Comparable(typ.key) {
- check.errorf(e.Key, _IncomparableMapKey, "incomparable map key type %s", typ.key)
+ var why string
+ if asTypeParam(typ.key) != nil {
+ why = " (missing comparable constraint)"
+ }
+ check.errorf(e.Key, _IncomparableMapKey, "incomparable map key type %s%s", typ.key, why)
}
})
}
typ.dir = dir
- typ.elem = check.typ(e.Value)
+ typ.elem = check.varType(e.Value)
return typ
default:
- check.errorf(e, _NotAType, "%s is not a type", e)
+ check.errorf(e0, _NotAType, "%s is not a type", e0)
}
typ := Typ[Invalid]
}
// typeOrNil type-checks the type expression (or nil value) e
-// and returns the typ of e, or nil.
-// If e is neither a type nor nil, typOrNil returns Typ[Invalid].
-//
-func (check *Checker) typOrNil(e ast.Expr) Type {
+// and returns the type of e, or nil. If e is a type, it must
+// not be an (uninstantiated) generic type.
+// If e is neither a type nor nil, typeOrNil returns Typ[Invalid].
+// TODO(gri) should we also disallow non-var types?
+func (check *Checker) typeOrNil(e ast.Expr) Type {
var x operand
check.rawExpr(&x, e, nil)
switch x.mode {
case novalue:
check.errorf(&x, _NotAType, "%s used as type", &x)
case typexpr:
+ check.instantiatedOperand(&x)
return x.typ
case value:
if x.isNil() {
return Typ[Invalid]
}
+func (check *Checker) instantiatedType(x ast.Expr, targs []ast.Expr, def *Named) Type {
+ b := check.genericType(x, true) // TODO(gri) what about cycles?
+ if b == Typ[Invalid] {
+ return b // error already reported
+ }
+ base := asNamed(b)
+ if base == nil {
+ unreachable() // should have been caught by genericType
+ }
+
+ // create a new type instance rather than instantiate the type
+ // TODO(gri) should do argument number check here rather than
+ // when instantiating the type?
+ typ := new(instance)
+ def.setUnderlying(typ)
+
+ typ.check = check
+ typ.pos = x.Pos()
+ typ.base = base
+
+ // evaluate arguments (always)
+ typ.targs = check.typeList(targs)
+ if typ.targs == nil {
+ def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation
+ return Typ[Invalid]
+ }
+
+ // determine argument positions (for error reporting)
+ typ.poslist = make([]token.Pos, len(targs))
+ for i, arg := range targs {
+ typ.poslist[i] = arg.Pos()
+ }
+
+ // make sure we check instantiation works at least once
+ // and that the resulting type is valid
+ check.atEnd(func() {
+ t := typ.expand()
+ check.validType(t, nil)
+ })
+
+ return typ
+}
+
// arrayLength type-checks the array length expression e
// and returns the constant length >= 0, or a value < 0
// to indicate an error (and thus an unknown length).
return -1
}
-func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
+// typeList provides the list of types corresponding to the incoming expression list.
+// If an error occured, the result is nil, but all list elements were type-checked.
+func (check *Checker) typeList(list []ast.Expr) []Type {
+ res := make([]Type, len(list)) // res != nil even if len(list) == 0
+ for i, x := range list {
+ t := check.varType(x)
+ if t == Typ[Invalid] {
+ res = nil
+ }
+ if res != nil {
+ res[i] = t
+ }
+ }
+ return res
+}
+
+// collectParams declares the parameters of list in scope and returns the corresponding
+// variable list. If type0 != nil, it is used instead of the the first type in list.
+func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, type0 ast.Expr, variadicOk bool) (params []*Var, variadic bool) {
if list == nil {
return
}
var named, anonymous bool
for i, field := range list.List {
ftype := field.Type
+ if i == 0 && type0 != nil {
+ ftype = type0
+ }
if t, _ := ftype.(*ast.Ellipsis); t != nil {
ftype = t.Elt
if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 {
// ignore ... and continue
}
}
- typ := check.typ(ftype)
+ typ := check.varType(ftype)
// The parser ensures that f.Tag is nil and we don't
// care if a constructed AST contains a non-nil tag.
if len(field.Names) > 0 {
}
func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) {
+ var tlist *ast.Ident // "type" name of first entry in a type list declaration
+ var types []ast.Expr
for _, f := range iface.Methods.List {
if len(f.Names) > 0 {
- // We have a method with name f.Names[0].
+ // We have a method with name f.Names[0], or a type
+ // of a type list (name.Name == "type").
// (The parser ensures that there's only one method
// and we don't care if a constructed AST has more.)
name := f.Names[0]
continue // ignore
}
+ if name.Name == "type" {
+ // Always collect all type list entries, even from
+ // different type lists, under the assumption that
+ // the author intended to include all types.
+ types = append(types, f.Type)
+ if tlist != nil && tlist != name {
+ check.errorf(name, _Todo, "cannot have multiple type lists in an interface")
+ }
+ tlist = name
+ continue
+ }
+
typ := check.typ(f.Type)
sig, _ := typ.(*Signature)
if sig == nil {
continue // ignore
}
+ // Always type-check method type parameters but complain if they are not enabled.
+ // (This extra check is needed here because interface method signatures don't have
+ // a receiver specification.)
+ if sig.tparams != nil {
+ check.errorf(f.Type.(*ast.FuncType).TParams, _Todo, "methods cannot have type parameters")
+ }
+
// use named receiver type if available (for better error messages)
var recvTyp Type = ityp
if def != nil {
check.recordDef(name, m)
ityp.methods = append(ityp.methods, m)
} else {
- // We have an embedded interface and f.Type is its
- // (possibly qualified) embedded type name. Collect
- // it if it's a valid interface.
- typ := check.typ(f.Type)
-
- utyp := check.underlying(typ)
- if _, ok := utyp.(*Interface); !ok {
- if utyp != Typ[Invalid] {
- check.errorf(f.Type, _InvalidIfaceEmbed, "%s is not an interface", typ)
- }
- continue
- }
-
- ityp.embeddeds = append(ityp.embeddeds, typ)
+ // We have an embedded type. completeInterface will
+ // eventually verify that we have an interface.
+ ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type))
check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
}
}
- if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 {
+ // type constraints
+ ityp.types = NewSum(check.collectTypeConstraints(iface.Pos(), types))
+
+ if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 {
// empty interface
ityp.allMethods = markComplete
return
sortMethods(ityp.methods)
sortTypes(ityp.embeddeds)
- check.later(func() { check.completeInterface(ityp) })
+ check.later(func() { check.completeInterface(iface.Pos(), ityp) })
}
-func (check *Checker) completeInterface(ityp *Interface) {
+func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
if ityp.allMethods != nil {
return
}
}
if trace {
- check.trace(token.NoPos, "complete %s", ityp)
+ // Types don't generally have position information.
+ // If we don't have a valid pos provided, try to use
+ // one close enough.
+ if !pos.IsValid() && len(ityp.methods) > 0 {
+ pos = ityp.methods[0].pos
+ }
+
+ check.trace(pos, "complete %s", ityp)
check.indent++
defer func() {
check.indent--
- check.trace(token.NoPos, "=> %s", ityp)
+ check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes)
}()
}
addMethod(m.pos, m, true)
}
+ // collect types
+ allTypes := ityp.types
+
posList := check.posMap[ityp]
for i, typ := range ityp.embeddeds {
pos := posList[i] // embedding position
- typ, ok := check.underlying(typ).(*Interface)
- if !ok {
- // An error was reported when collecting the embedded types.
- // Ignore it.
+ utyp := under(typ)
+ etyp := asInterface(utyp)
+ if etyp == nil {
+ if utyp != Typ[Invalid] {
+ var format string
+ if _, ok := utyp.(*TypeParam); ok {
+ format = "%s is a type parameter, not an interface"
+ } else {
+ format = "%s is not an interface"
+ }
+ // TODO: correct error code.
+ check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ)
+ }
continue
}
- check.completeInterface(typ)
- for _, m := range typ.allMethods {
+ check.completeInterface(pos, etyp)
+ for _, m := range etyp.allMethods {
addMethod(pos, m, false) // use embedding position pos rather than m.pos
}
+ allTypes = intersect(allTypes, etyp.allTypes)
}
if methods != nil {
sort.Sort(byUniqueMethodName(methods))
ityp.allMethods = methods
}
+ ityp.allTypes = allTypes
+}
+
+// intersect computes the intersection of the types x and y.
+// Note: A incomming nil type stands for the top type. A top
+// type result is returned as nil.
+func intersect(x, y Type) (r Type) {
+ defer func() {
+ if r == theTop {
+ r = nil
+ }
+ }()
+
+ switch {
+ case x == theBottom || y == theBottom:
+ return theBottom
+ case x == nil || x == theTop:
+ return y
+ case y == nil || x == theTop:
+ return x
+ }
+
+ xtypes := unpackType(x)
+ ytypes := unpackType(y)
+ // Compute the list rtypes which includes only
+ // types that are in both xtypes and ytypes.
+ // Quadratic algorithm, but good enough for now.
+ // TODO(gri) fix this
+ var rtypes []Type
+ for _, x := range xtypes {
+ if includes(ytypes, x) {
+ rtypes = append(rtypes, x)
+ }
+ }
+
+ if rtypes == nil {
+ return theBottom
+ }
+ return NewSum(rtypes)
}
func sortTypes(list []Type) {
func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func sortName(t Type) string {
- if named, _ := t.(*Named); named != nil {
+ if named := asNamed(t); named != nil {
return named.obj.Id()
}
return ""
}
for _, f := range list.List {
- typ = check.typ(f.Type)
+ typ = check.varType(f.Type)
tag = check.tag(f.Tag)
if len(f.Names) > 0 {
// named fields
}
} else {
// embedded field
- // spec: "An embedded type must be specified as a type name T or as a pointer
- // to a non-interface type name *T, and T itself may not be a pointer type."
+ // spec: "An embedded type must be specified as a type name T or as a
+ // pointer to a non-interface type name *T, and T itself may not be a
+ // pointer type."
pos := f.Type.Pos()
name := embeddedFieldIdent(f.Type)
if name == nil {
- check.invalidAST(f.Type, "embedded field type %s has no name", f.Type)
+ // TODO(rFindley): using invalidAST here causes test failures (all
+ // errors should have codes). Clean this up.
+ check.errorf(f.Type, _Todo, "invalid AST: embedded field type %s has no name", f.Type)
name = ast.NewIdent("_")
name.NamePos = pos
addInvalid(name, pos)
continue
}
- t, isPtr := deref(typ)
+ add(name, true, pos)
+
// 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.
- switch t := t.Underlying().(type) {
- case *Basic:
- if t == Typ[Invalid] {
- // error was reported before
- addInvalid(name, pos)
- continue
- }
-
- // unsafe.Pointer is treated like a regular pointer
- if t.kind == UnsafePointer {
- check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
- addInvalid(name, pos)
- continue
- }
+ // We must delay this check to the end because we don't want to instantiate
+ // (via under(t)) a possibly incomplete type.
- case *Pointer:
- check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
- addInvalid(name, pos)
- continue
+ // for use in the closure below
+ embeddedTyp := typ
+ embeddedPos := f.Type
- case *Interface:
- if isPtr {
- check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
- addInvalid(name, pos)
- continue
+ check.atEnd(func() {
+ t, isPtr := deref(embeddedTyp)
+ switch t := optype(t).(type) {
+ case *Basic:
+ if t == Typ[Invalid] {
+ // error was reported before
+ return
+ }
+ // unsafe.Pointer is treated like a regular pointer
+ if t.kind == UnsafePointer {
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
+ }
+ case *Pointer:
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
+ case *Interface:
+ if isPtr {
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
+ }
}
- }
- add(name, true, pos)
+ })
}
}
}
case *ast.SelectorExpr:
return e.Sel
+ case *ast.IndexExpr:
+ return embeddedFieldIdent(e.X)
+ case *ast.CallExpr:
+ if e.Brackets {
+ return embeddedFieldIdent(e.Fun)
+ }
}
return nil // invalid embedded field
}
+
+func (check *Checker) collectTypeConstraints(pos token.Pos, types []ast.Expr) []Type {
+ list := make([]Type, 0, len(types)) // assume all types are correct
+ for _, texpr := range types {
+ if texpr == nil {
+ check.invalidAST(atPos(pos), "missing type constraint")
+ continue
+ }
+ list = append(list, check.varType(texpr))
+ }
+
+ // Ensure that each type is only present once in the type list. Types may be
+ // interfaces, which may not be complete yet. It's ok to do this check at the
+ // end because it's not a requirement for correctness of the code.
+ // Note: This is a quadratic algorithm, but type lists tend to be short.
+ check.atEnd(func() {
+ for i, t := range list {
+ if t := asInterface(t); t != nil {
+ check.completeInterface(types[i].Pos(), t)
+ }
+ if includes(list[:i], t) {
+ check.softErrorf(types[i], _Todo, "duplicate type %s in type list", t)
+ }
+ }
+ })
+
+ return list
+}
+
+// includes reports whether typ is in list.
+func includes(list []Type, typ Type) bool {
+ for _, e := range list {
+ if Identical(typ, e) {
+ return true
+ }
+ }
+ return false
+}