1
0
mirror of https://github.com/golang/go synced 2024-11-26 06:27:58 -07:00

[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 <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Griesemer <gri@golang.org>
Trust: Robert Findley <rfindley@google.com>
This commit is contained in:
Rob Findley 2020-12-16 17:02:07 -05:00 committed by Robert Findley
parent 9546596d77
commit 0e286579c5
3 changed files with 464 additions and 96 deletions

View File

@ -567,3 +567,11 @@ Error:
x.mode = invalid x.mode = invalid
x.expr = e 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]
}
}

View File

@ -269,7 +269,7 @@ L:
func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) { func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
L: L:
for _, e := range types { for _, e := range types {
T = check.typOrNil(e) T = check.typeOrNil(e)
if T == Typ[Invalid] { if T == Typ[Invalid] {
continue L continue L
} }

View File

@ -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]. // 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 { func (check *Checker) typ(e ast.Expr) Type {
return check.definedType(e, nil) 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 // in a type declaration, and def.underlying will be set to the type of e before
// any components of e are type-checked. // any components of e are type-checked.
// //
func (check *Checker) definedType(e ast.Expr, def *Named) (T Type) { func (check *Checker) definedType(e ast.Expr, def *Named) Type {
if trace { typ := check.typInternal(e, def)
check.trace(e.Pos(), "%s", e) assert(isTyped(typ))
check.indent++ if isGeneric(typ) {
defer func() { check.errorf(e, 0, "cannot use generic type %s without instantiation", typ)
check.indent-- typ = Typ[Invalid]
check.trace(e.Pos(), "=> %s", T)
}()
} }
check.recordTypeAndValue(e, typexpr, typ, nil)
return typ
}
T = check.typInternal(e, def) // genericType is like typ but the type must be an (uninstantiated) generic type.
assert(isTyped(T)) func (check *Checker) genericType(e ast.Expr, reportErr bool) Type {
check.recordTypeAndValue(e, typexpr, T, nil) 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. // funcType type-checks a function or method type.
func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) { func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) {
scope := NewScope(check.scope, token.NoPos, token.NoPos, "function") check.openScope(ftyp, "function")
scope.isFunc = true check.scope.isFunc = true
check.recordScope(ftyp, scope) check.recordScope(ftyp, check.scope)
sig.scope = check.scope
defer check.closeScope()
recvList, _ := check.collectParams(scope, recvPar, false) var recvTyp ast.Expr // rewritten receiver type; valid if != nil
params, variadic := check.collectParams(scope, ftyp.Params, true) if recvPar != nil && len(recvPar.List) > 0 {
results, _ := check.collectParams(scope, ftyp.Results, false) // 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")
}
}
// 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 { if recvPar != nil {
// recv parameter list present (may be empty) // 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 var recv *Var
switch len(recvList) { switch len(recvList) {
case 0: case 0:
// TODO(rFindley) this is now redundant with resolver.go. Clean up when // error reported by resolver
// importing remaining typexpr.go changes.
// check.error(recvPar, _BadRecv, "method is missing receiver")
recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
default: default:
// more than one receiver // more than one receiver
@ -211,19 +347,24 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
case 1: case 1:
recv = recvList[0] 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." // 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) // (ignore invalid types - error was reported before)
if t, _ := deref(recv.typ); t != Typ[Invalid] { if t := rtyp; t != Typ[Invalid] {
var err string 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 // 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 // be a pointer or interface type and it must be declared in the same package
// as the method." // as the method."
if T.obj.pkg != check.pkg { if T.obj.pkg != check.pkg {
err = "type not defined in this package" err = "type not defined in this package"
} else { } else {
// TODO(gri) This is not correct if the underlying type is unknown yet. switch u := optype(T).(type) {
switch u := T.underlying.(type) {
case *Basic: case *Basic:
// unsafe.Pointer is treated like a regular pointer // unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer { if u.kind == UnsafePointer {
@ -244,7 +385,6 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
sig.recv = recv sig.recv = recv
} }
sig.scope = scope
sig.params = NewTuple(params...) sig.params = NewTuple(params...)
sig.results = NewTuple(results...) sig.results = NewTuple(results...)
sig.variadic = variadic sig.variadic = variadic
@ -257,10 +397,31 @@ func goTypeName(typ Type) string {
} }
// typInternal drives type checking of 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 { func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
switch e := e.(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: case *ast.BadExpr:
// ignore - error reported before // 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) 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: 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) return check.definedType(e.X, def)
case *ast.ArrayType: case *ast.ArrayType:
@ -306,16 +479,15 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
typ := new(Array) typ := new(Array)
def.setUnderlying(typ) def.setUnderlying(typ)
typ.len = check.arrayLength(e.Len) typ.len = check.arrayLength(e.Len)
typ.elem = check.typ(e.Elt) typ.elem = check.varType(e.Elt)
return typ
} else {
typ := new(Slice)
def.setUnderlying(typ)
typ.elem = check.typ(e.Elt)
return typ return typ
} }
typ := new(Slice)
def.setUnderlying(typ)
typ.elem = check.varType(e.Elt)
return typ
case *ast.StructType: case *ast.StructType:
typ := new(Struct) typ := new(Struct)
def.setUnderlying(typ) def.setUnderlying(typ)
@ -325,7 +497,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
case *ast.StarExpr: case *ast.StarExpr:
typ := new(Pointer) typ := new(Pointer)
def.setUnderlying(typ) def.setUnderlying(typ)
typ.base = check.typ(e.X) typ.base = check.varType(e.X)
return typ return typ
case *ast.FuncType: case *ast.FuncType:
@ -337,6 +509,9 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
case *ast.InterfaceType: case *ast.InterfaceType:
typ := new(Interface) typ := new(Interface)
def.setUnderlying(typ) def.setUnderlying(typ)
if def != nil {
typ.obj = def.obj
}
check.interfaceType(typ, e, def) check.interfaceType(typ, e, def)
return typ return typ
@ -344,8 +519,8 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
typ := new(Map) typ := new(Map)
def.setUnderlying(typ) def.setUnderlying(typ)
typ.key = check.typ(e.Key) typ.key = check.varType(e.Key)
typ.elem = check.typ(e.Value) typ.elem = check.varType(e.Value)
// spec: "The comparison operators == and != must be fully defined // spec: "The comparison operators == and != must be fully defined
// for operands of the key type; thus the key type must not be a // 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). // it is safe to continue in any case (was issue 6667).
check.atEnd(func() { check.atEnd(func() {
if !Comparable(typ.key) { 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.dir = dir
typ.elem = check.typ(e.Value) typ.elem = check.varType(e.Value)
return typ return typ
default: default:
check.errorf(e, _NotAType, "%s is not a type", e) check.errorf(e0, _NotAType, "%s is not a type", e0)
} }
typ := Typ[Invalid] 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 // typeOrNil type-checks the type expression (or nil value) e
// and returns the typ of e, or nil. // and returns the type of e, or nil. If e is a type, it must
// If e is neither a type nor nil, typOrNil returns Typ[Invalid]. // not be an (uninstantiated) generic type.
// // If e is neither a type nor nil, typeOrNil returns Typ[Invalid].
func (check *Checker) typOrNil(e ast.Expr) Type { // TODO(gri) should we also disallow non-var types?
func (check *Checker) typeOrNil(e ast.Expr) Type {
var x operand var x operand
check.rawExpr(&x, e, nil) check.rawExpr(&x, e, nil)
switch x.mode { switch x.mode {
@ -404,6 +584,7 @@ func (check *Checker) typOrNil(e ast.Expr) Type {
case novalue: case novalue:
check.errorf(&x, _NotAType, "%s used as type", &x) check.errorf(&x, _NotAType, "%s used as type", &x)
case typexpr: case typexpr:
check.instantiatedOperand(&x)
return x.typ return x.typ
case value: case value:
if x.isNil() { if x.isNil() {
@ -416,6 +597,49 @@ func (check *Checker) typOrNil(e ast.Expr) Type {
return Typ[Invalid] 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 // arrayLength type-checks the array length expression e
// and returns the constant length >= 0, or a value < 0 // and returns the constant length >= 0, or a value < 0
// to indicate an error (and thus an unknown length). // to indicate an error (and thus an unknown length).
@ -443,7 +667,25 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
return -1 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 { if list == nil {
return return
} }
@ -451,6 +693,9 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
var named, anonymous bool var named, anonymous bool
for i, field := range list.List { for i, field := range list.List {
ftype := field.Type ftype := field.Type
if i == 0 && type0 != nil {
ftype = type0
}
if t, _ := ftype.(*ast.Ellipsis); t != nil { if t, _ := ftype.(*ast.Ellipsis); t != nil {
ftype = t.Elt ftype = t.Elt
if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 { 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 // ignore ... and continue
} }
} }
typ := check.typ(ftype) typ := check.varType(ftype)
// The parser ensures that f.Tag is nil and we don't // The parser ensures that f.Tag is nil and we don't
// care if a constructed AST contains a non-nil tag. // care if a constructed AST contains a non-nil tag.
if len(field.Names) > 0 { 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) { 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 { for _, f := range iface.Methods.List {
if len(f.Names) > 0 { 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 // (The parser ensures that there's only one method
// and we don't care if a constructed AST has more.) // and we don't care if a constructed AST has more.)
name := f.Names[0] name := f.Names[0]
@ -522,6 +770,18 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
continue // ignore 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) typ := check.typ(f.Type)
sig, _ := typ.(*Signature) sig, _ := typ.(*Signature)
if sig == nil { if sig == nil {
@ -531,6 +791,13 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
continue // ignore 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) // use named receiver type if available (for better error messages)
var recvTyp Type = ityp var recvTyp Type = ityp
if def != nil { if def != nil {
@ -542,25 +809,17 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
check.recordDef(name, m) check.recordDef(name, m)
ityp.methods = append(ityp.methods, m) ityp.methods = append(ityp.methods, m)
} else { } else {
// We have an embedded interface and f.Type is its // We have an embedded type. completeInterface will
// (possibly qualified) embedded type name. Collect // eventually verify that we have an interface.
// it if it's a valid interface. ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type))
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)
check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos()) 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 // empty interface
ityp.allMethods = markComplete ityp.allMethods = markComplete
return return
@ -665,7 +924,7 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
format = "%s is not an interface" format = "%s is not an interface"
} }
// TODO: correct error code. // TODO: correct error code.
check.errorf(atPos(pos), 0, format, typ) check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ)
} }
continue 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 (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func sortName(t Type) string { func sortName(t Type) string {
if named, _ := t.(*Named); named != nil { if named := asNamed(t); named != nil {
return named.obj.Id() return named.obj.Id()
} }
return "" return ""
@ -798,7 +1057,7 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType) {
} }
for _, f := range list.List { for _, f := range list.List {
typ = check.typ(f.Type) typ = check.varType(f.Type)
tag = check.tag(f.Tag) tag = check.tag(f.Tag)
if len(f.Names) > 0 { if len(f.Names) > 0 {
// named fields // named fields
@ -807,8 +1066,9 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType) {
} }
} else { } else {
// embedded field // embedded field
// spec: "An embedded type must be specified as a type name T or as a pointer // spec: "An embedded type must be specified as a type name T or as a
// to a non-interface type name *T, and T itself may not be a pointer type." // pointer to a non-interface type name *T, and T itself may not be a
// pointer type."
pos := f.Type.Pos() pos := f.Type.Pos()
name := embeddedFieldIdent(f.Type) name := embeddedFieldIdent(f.Type)
if name == nil { if name == nil {
@ -818,37 +1078,37 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType) {
addInvalid(name, pos) addInvalid(name, pos)
continue 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 // 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. // of a (named or alias) type, and t (= deref(typ)) must be the type of T.
switch t := t.Underlying().(type) { // We must delay this check to the end because we don't want to instantiate
case *Basic: // (via under(t)) a possibly incomplete type.
if t == Typ[Invalid] {
// error was reported before
addInvalid(name, pos)
continue
}
// unsafe.Pointer is treated like a regular pointer // for use in the closure below
if t.kind == UnsafePointer { embeddedTyp := typ
check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer") embeddedPos := f.Type
addInvalid(name, pos)
continue
}
case *Pointer: check.atEnd(func() {
check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be a pointer") t, isPtr := deref(embeddedTyp)
addInvalid(name, pos) switch t := optype(t).(type) {
continue case *Basic:
if t == Typ[Invalid] {
case *Interface: // error was reported before
if isPtr { return
check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface") }
addInvalid(name, pos) // unsafe.Pointer is treated like a regular pointer
continue 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: case *ast.SelectorExpr:
return e.Sel 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 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. // includes reports whether typ is in list.
func includes(list []Type, typ Type) bool { func includes(list []Type, typ Type) bool {
for _, e := range list { for _, e := range list {
@ -880,3 +1184,59 @@ func includes(list []Type, typ Type) bool {
} }
return false 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
}