go.tools/go/types: Fix for broken IsAssignableTo.

Was broken by CL 12378043.

- factored out some error checking code
- adjusted error positions

R=adonovan
TBR=adonovan
CC=golang-dev
https://golang.org/cl/12401043
This commit is contained in:
Robert Griesemer 2013-08-05 13:54:22 -07:00
parent 1dce9cc7c7
commit b956928b06
7 changed files with 57 additions and 55 deletions

View File

@ -1152,8 +1152,8 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
if x.mode == invalid {
goto Error
}
var T *Interface
if T, _ = x.typ.Underlying().(*Interface); T == nil {
xtyp, _ := x.typ.Underlying().(*Interface)
if xtyp == nil {
check.invalidOp(x.pos(), "%s is not an interface", x)
goto Error
}
@ -1162,27 +1162,13 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
check.invalidAST(e.Pos(), "use of .(type) outside type switch")
goto Error
}
typ := check.typ(e.Type, nil, false)
if typ == Typ[Invalid] {
T := check.typ(e.Type, nil, false)
if T == Typ[Invalid] {
goto Error
}
if method, wrongType := MissingMethod(typ, T); method != nil {
var msg string
if wrongType {
msg = "%s cannot have dynamic type %s (wrong type for method %s)"
} else if _, ok := typ.Underlying().(*Interface); !ok {
// Concrete types must have all methods of T. Interfaces
// may not have all methods of T statically, but may have
// them dynamically; don't report an error in that case.
msg = "%s cannot have dynamic type %s (missing method %s)"
}
if msg != "" {
check.errorf(e.Type.Pos(), msg, x, typ, method.name)
}
// ok to continue
}
check.typeAssertion(x.pos(), x, xtyp, T)
x.mode = valueok
x.typ = typ
x.typ = T
case *ast.CallExpr:
check.call(x, e)
@ -1251,6 +1237,21 @@ Error:
x.expr = e
}
// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
func (check *checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) {
method, wrongType := MissingMethod(T, xtyp, false)
if method == nil {
return
}
var msg string
if wrongType {
msg = "%s cannot have dynamic type %s (wrong type for method %s)"
} else {
msg = "%s cannot have dynamic type %s (missing method %s)"
}
check.errorf(pos, msg, x, T, method.name)
}
// expr typechecks expression e and initializes x with the expression value.
// If an error occurred, x.mode is set to invalid.
//

View File

@ -220,23 +220,33 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
return list[:n]
}
// MissingMethod returns (nil, false) if typ implements T, otherwise
// it returns the first missing method required by T and whether it
// is missing or simply has the wrong type.
// MissingMethod returns (nil, false) if typ implements T, otherwise it
// returns a missing method required by T and whether it is missing or
// just has the wrong type.
//
func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
// For typ of interface type, if static is set, implements checks that all
// methods of T are present in typ (e.g., for a conversion T(x) where x is
// of type typ). Otherwise, implements only checks that methods of T which
// are also present in typ have matching types (e.g., for a type assertion
// x.(T) where x is of interface type typ).
//
func MissingMethod(typ Type, T *Interface, static bool) (method *Func, wrongType bool) {
// fast path for common case
if T.NumMethods() == 0 {
return
}
// The dynamic type of a value stored in an interface may implement T,
// but only if all the shared interface methods have matching signatures.
// Note: This is stronger than the current spec. Should the spec require this?
// TODO(gri) Consider using methods sets here. Might be more efficient.
if ityp, _ := typ.Underlying().(*Interface); ityp != nil {
for _, m := range T.methods {
_, obj := lookupMethod(ityp.methods, m.pkg, m.name)
if obj != nil && !IsIdentical(obj.Type(), m.typ) {
switch {
case obj == nil:
if static {
return m, false
}
case !IsIdentical(obj.Type(), m.typ):
return m, true
}
}

View File

@ -143,7 +143,7 @@ func (x *operand) isAssignableTo(conf *Config, T Type) bool {
// T is an interface type and x implements T
// (Do this check first as it might succeed early.)
if Ti, ok := Tu.(*Interface); ok {
if m, _ := MissingMethod(x.typ, Ti); m == nil {
if m, _ := MissingMethod(x.typ, Ti, true); m == nil {
return true
}
}

View File

@ -393,8 +393,8 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) {
if x.mode == invalid {
return
}
var T *Interface
if T, _ = x.typ.Underlying().(*Interface); T == nil {
xtyp, _ := x.typ.Underlying().(*Interface)
if xtyp == nil {
check.errorf(x.pos(), "%s is not an interface", &x)
return
}
@ -406,20 +406,11 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) {
continue // error reported before
}
// Check each type in this type switch case.
var typ Type
var T Type
for _, expr := range clause.List {
typ = check.typOrNil(expr)
if typ != nil && typ != Typ[Invalid] {
if method, wrongType := MissingMethod(typ, T); method != nil {
var msg string
if wrongType {
msg = "%s cannot have dynamic type %s (wrong type for method %s)"
} else {
msg = "%s cannot have dynamic type %s (missing method %s)"
}
check.errorf(expr.Pos(), msg, &x, typ, method.name)
// ok to continue
}
T = check.typOrNil(expr)
if T != nil && T != Typ[Invalid] {
check.typeAssertion(expr.Pos(), &x, xtyp, T)
}
}
check.openScope(clause)
@ -430,10 +421,10 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) {
// the implicit block in each clause. In clauses with a case listing
// exactly one type, the variable has that type; otherwise, the variable
// has the type of the expression in the TypeSwitchGuard."
if len(clause.List) != 1 || typ == nil {
typ = x.typ
if len(clause.List) != 1 || T == nil {
T = x.typ
}
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, typ)
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
check.declareObj(check.topScope, nil, obj)
check.recordImplicit(clause, obj)
}

View File

@ -66,16 +66,16 @@ func interface_conversions() {
_ = I1 /* ERROR "cannot convert" */ (0)
_ = I1(nil)
_ = I1(i1)
_ = I1(e)
_ = I1 /* ERROR "cannot convert" */ (e)
_ = I1(i2)
_ = I2(nil)
_ = I2(i1)
_ = I2 /* ERROR "cannot convert" */ (i1)
_ = I2(i2)
_ = I2 /* ERROR "cannot convert" */ (i3)
_ = I3(nil)
_ = I3(i1)
_ = I3 /* ERROR "cannot convert" */ (i1)
_ = I3 /* ERROR "cannot convert" */ (i2)
_ = I3(i3)

View File

@ -282,11 +282,11 @@ func type_asserts() {
var t I
_ = t /* ERROR "use of .* outside type switch" */ .(type)
_ = t.(T /* ERROR "missing method m" */ )
_ = t /* ERROR "missing method m" */ .(T)
_ = t.(*T)
_ = t.(T1 /* ERROR "missing method m" */ )
_ = t.(T2 /* ERROR "wrong type for method m" */ )
_ = t.(I2 /* ERROR "wrong type for method m" */ )
_ = t /* ERROR "missing method m" */ .(T1)
_ = t /* ERROR "wrong type for method m" */ .(T2)
_ = t /* ERROR "wrong type for method m" */ .(I2)
// e doesn't statically have an m, but may have one dynamically.
_ = e.(I2)

View File

@ -1328,7 +1328,7 @@ func conv(t_dst, t_src types.Type, x value) value {
// On success it returns "", on failure, an error message.
//
func checkInterface(i *interpreter, itype *types.Interface, x iface) string {
if meth, wrongType := types.MissingMethod(x.t, itype); meth != nil {
if meth, wrongType := types.MissingMethod(x.t, itype, true); meth != nil {
reason := "is missing"
if wrongType {
reason = "has wrong type"