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:
parent
9546596d77
commit
0e286579c5
@ -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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user