From 0e286579c5c49660ff01fc7db5f2747958869ce9 Mon Sep 17 00:00:00 2001 From: Rob Findley Date: Wed, 16 Dec 2020 17:02:07 -0500 Subject: [PATCH] [dev.typeparams] go/types: import typexpr.go from dev.go2go Changes from dev.go2go (compare with patchset 1): + Update stale comments. + Fix a bug in structType where check.atEnd closed over the loop variable, resulting in incorrect error positions. + Fix a bug in the CallExpr clause of typInternal where it didn't check e.Brackets before checking instantiatedType. + Remove support for parenthesized embedded type names. + Add an IndexExpr clause to embeddedFieldIdent. + Lift the substMap construction out of the loop in funcType when substituting receiver type parameters. + Minor simplification in collectTypeConstraints. Compare with patchset 1 to see these changes. Change-Id: I24f10e8615a0bbcd56c86ecf3490ce6a99cfebd6 Reviewed-on: https://go-review.googlesource.com/c/go/+/278916 Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer Trust: Robert Griesemer Trust: Robert Findley --- src/go/types/call.go | 8 + src/go/types/stmt.go | 2 +- src/go/types/typexpr.go | 550 +++++++++++++++++++++++++++++++++------- 3 files changed, 464 insertions(+), 96 deletions(-) diff --git a/src/go/types/call.go b/src/go/types/call.go index 6765b17bf3..61a7f0926d 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -567,3 +567,11 @@ Error: x.mode = invalid x.expr = e } + +// instantiatedOperand reports an error of x is an uninstantiated (generic) type and sets x.typ to Typ[Invalid]. +func (check *Checker) instantiatedOperand(x *operand) { + if x.mode == typexpr && isGeneric(x.typ) { + check.errorf(x, 0, "cannot use generic type %s without instantiation", x.typ) + x.typ = Typ[Invalid] + } +} diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index a36ca43016..0162368a64 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -269,7 +269,7 @@ L: func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) { L: for _, e := range types { - T = check.typOrNil(e) + T = check.typeOrNil(e) if T == Typ[Invalid] { continue L } diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 24df33965d..42d8f691d0 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -118,6 +118,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) } // 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) } @@ -166,32 +167,169 @@ func (check *Checker) anyType(e ast.Expr) Type { // 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, 0, "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, 0, "%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, 0, "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) @@ -200,9 +338,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast var recv *Var switch len(recvList) { case 0: - // TODO(rFindley) this is now redundant with resolver.go. Clean up when - // importing remaining typexpr.go changes. - // 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 @@ -211,19 +347,24 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast 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 { @@ -244,7 +385,6 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast sig.recv = recv } - sig.scope = scope sig.params = NewTuple(params...) sig.results = NewTuple(results...) sig.variadic = variadic @@ -257,10 +397,31 @@ func goTypeName(typ Type) string { } // 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 @@ -298,7 +459,19 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { 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: @@ -306,16 +479,15 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { 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.StructType: typ := new(Struct) def.setUnderlying(typ) @@ -325,7 +497,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { 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: @@ -337,6 +509,9 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { case *ast.InterfaceType: typ := new(Interface) def.setUnderlying(typ) + if def != nil { + typ.obj = def.obj + } check.interfaceType(typ, e, def) return typ @@ -344,8 +519,8 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { 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 @@ -355,7 +530,11 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { // 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) } }) @@ -379,11 +558,11 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { } 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] @@ -392,10 +571,11 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { } // 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 { @@ -404,6 +584,7 @@ func (check *Checker) typOrNil(e ast.Expr) Type { case novalue: check.errorf(&x, _NotAType, "%s used as type", &x) case typexpr: + check.instantiatedOperand(&x) return x.typ case value: if x.isNil() { @@ -416,6 +597,49 @@ func (check *Checker) typOrNil(e ast.Expr) Type { 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). @@ -443,7 +667,25 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { 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 } @@ -451,6 +693,9 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicO 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 { @@ -460,7 +705,7 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicO // 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 { @@ -511,9 +756,12 @@ func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool } 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] @@ -522,6 +770,18 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d 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, 0, "cannot have multiple type lists in an interface") + } + tlist = name + continue + } + typ := check.typ(f.Type) sig, _ := typ.(*Signature) if sig == nil { @@ -531,6 +791,13 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d 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, 0, "methods cannot have type parameters") + } + // use named receiver type if available (for better error messages) var recvTyp Type = ityp if def != nil { @@ -542,25 +809,17 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d 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 := under(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 @@ -665,7 +924,7 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) { format = "%s is not an interface" } // TODO: correct error code. - check.errorf(atPos(pos), 0, format, typ) + check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ) } continue } @@ -729,7 +988,7 @@ func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName 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 "" @@ -798,7 +1057,7 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType) { } 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 @@ -807,8 +1066,9 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType) { } } 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 { @@ -818,37 +1078,37 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType) { 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 - } + // We must delay this check to the end because we don't want to instantiate + // (via under(t)) a possibly incomplete type. - // 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 - } - - 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) + }) } } @@ -867,10 +1127,54 @@ func embeddedFieldIdent(e ast.Expr) *ast.Ident { } 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 + } + typ := check.varType(texpr) + // A type constraint may be a predeclared type or a composite type composed + // of only predeclared types. + // TODO(gri) If we enable this again it also must run at the end. + const restricted = false + var why string + if restricted && !check.typeConstraint(typ, &why) { + check.errorf(texpr, 0, "invalid type constraint %s (%s)", typ, why) + continue + } + list = append(list, typ) + } + + // 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], 0, "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 { @@ -880,3 +1184,59 @@ func includes(list []Type, typ Type) bool { } return false } + +// typeConstraint checks that typ may be used in a type list. +// For now this just checks for the absence of defined (*Named) types. +func (check *Checker) typeConstraint(typ Type, why *string) bool { + switch t := typ.(type) { + case *Basic: + // ok + case *Array: + return check.typeConstraint(t.elem, why) + case *Slice: + return check.typeConstraint(t.elem, why) + case *Struct: + for _, f := range t.fields { + if !check.typeConstraint(f.typ, why) { + return false + } + } + case *Pointer: + return check.typeConstraint(t.base, why) + case *Tuple: + if t == nil { + return true + } + for _, v := range t.vars { + if !check.typeConstraint(v.typ, why) { + return false + } + } + case *Signature: + if len(t.tparams) != 0 { + panic("type parameter in function type") + } + return (t.recv == nil || check.typeConstraint(t.recv.typ, why)) && + check.typeConstraint(t.params, why) && + check.typeConstraint(t.results, why) + case *Interface: + t.assertCompleteness() + for _, m := range t.allMethods { + if !check.typeConstraint(m.typ, why) { + return false + } + } + case *Map: + return check.typeConstraint(t.key, why) && check.typeConstraint(t.elem, why) + case *Chan: + return check.typeConstraint(t.elem, why) + case *Named: + *why = check.sprintf("contains defined type %s", t) + return false + case *TypeParam: + // ok, e.g.: func f (type T interface { type T }) () + default: + unreachable() + } + return true +} -- 2.50.0