diff --git a/go/types/expr.go b/go/types/expr.go index 41e35e9a..a414d32f 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -1564,7 +1564,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } // x.(type) expressions are handled explicitly in type switches if e.Type == nil { - check.errorf(e.Pos(), "use of .(type) outside type switch") + check.invalidAST(e.Pos(), "use of .(type) outside type switch") goto Error } typ := check.typ(e.Type, false) @@ -1575,10 +1575,15 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle var msg string if wrongType { msg = "%s cannot have dynamic type %s (wrong type for method %s)" - } else { + } 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)" } - check.errorf(e.Type.Pos(), msg, x, typ, method.name) + if msg != "" { + check.errorf(e.Type.Pos(), msg, x, typ, method.name) + } // ok to continue } x.mode = valueok diff --git a/go/types/lookup.go b/go/types/lookup.go index 47e43dcb..170b886a 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -188,17 +188,21 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { return } + // An interface type implements T if it has at least the methods of T. 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) { + if obj == nil { + return m, false + } + if !IsIdentical(obj.Type(), m.typ) { return m, true } } return } - // a concrete type implements T if it implements all methods of T. + // A concrete type implements T if it implements all methods of T. for _, m := range T.methods { obj, _, indirect := LookupFieldOrMethod(typ, m.pkg, m.name) if obj == nil { @@ -222,6 +226,7 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { return m, true } } + return } diff --git a/go/types/testdata/conversions.src b/go/types/testdata/conversions.src index 291fa781..82af6314 100644 --- a/go/types/testdata/conversions.src +++ b/go/types/testdata/conversions.src @@ -33,4 +33,36 @@ func string_conversions() { // var ( _ = int8(0) -) \ No newline at end of file +) + +func interface_conversions() { + type E interface{} + + type I1 interface{ + m1() + } + + type I2 interface{ + m1() + m2() + } + + var e E + var i1 I1 + var i2 I2 + _ = E(0) + _ = E(nil) + _ = E(e) + _ = E(i1) + _ = E(i2) + + _ = I1 /* ERROR "cannot convert" */ (0) + _ = I1(nil) + _ = I1(i1) + _ = I1 /* ERROR "cannot convert" */ (e) + _ = I1(i2) + + _ = I2 /* ERROR "cannot convert" */ (i1) + + // TODO(gri) add more tests, improve error message +} \ No newline at end of file diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src index 0a9abd7f..a0f088db 100644 --- a/go/types/testdata/expr3.src +++ b/go/types/testdata/expr3.src @@ -281,6 +281,9 @@ func type_asserts() { _ = t.(T1 /* ERROR "missing method m" */ ) _ = t.(T2 /* ERROR "wrong type for method m" */ ) _ = t.(I2 /* ERROR "wrong type for method m" */ ) + + // e doesn't statically have an m, but may have one dynamically. + _ = e.(I2) } func f0() {}