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:
parent
1dce9cc7c7
commit
b956928b06
|
|
@ -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.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue