diff --git a/_test/interface14.go b/_test/interface14.go new file mode 100644 index 00000000..e6bbacbe --- /dev/null +++ b/_test/interface14.go @@ -0,0 +1,17 @@ +package main + +type T struct{} + +func (t *T) Error() string { return "T: error" } + +var invalidT = &T{} + +func main() { + var err error + if err != invalidT { + println("ok") + } +} + +// Output: +// ok diff --git a/_test/interface15.go b/_test/interface15.go new file mode 100644 index 00000000..59e21e07 --- /dev/null +++ b/_test/interface15.go @@ -0,0 +1,28 @@ +package main + +type Fooer interface { + Foo() string +} + +type Barer interface { + //fmt.Stringer + Fooer + Bar() +} + +type T struct{} + +func (t *T) Foo() string { return "T: foo" } +func (*T) Bar() { println("in bar") } + +var t = &T{} + +func main() { + var f Barer + if f != t { + println("ok") + } +} + +// Output: +// ok diff --git a/_test/interface16.go b/_test/interface16.go new file mode 100644 index 00000000..9e94c8bf --- /dev/null +++ b/_test/interface16.go @@ -0,0 +1,25 @@ +package main + +import "fmt" + +type Barer interface { + fmt.Stringer + Bar() +} + +type T struct{} + +func (*T) String() string { return "T: nothing" } +func (*T) Bar() { println("in bar") } + +var t = &T{} + +func main() { + var f Barer + if f != t { + println("ok") + } +} + +// Output: +// ok diff --git a/_test/interface17.go b/_test/interface17.go new file mode 100644 index 00000000..3049920a --- /dev/null +++ b/_test/interface17.go @@ -0,0 +1,17 @@ +package main + +type T struct{} + +func (t T) Error() string { return "T: error" } + +var invalidT = T{} + +func main() { + var err error + if err != invalidT { + println("ok") + } +} + +// Output: +// ok diff --git a/interp/cfg.go b/interp/cfg.go index ccaafe63..3a77742c 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -547,7 +547,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) { t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf() // Shift operator type is inherited from first parameter only // All other binary operators require both parameter types to be the same - if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && c0.typ.id() != c1.typ.id() { + if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && !c0.typ.equal(c1.typ) { err = n.cfgErrorf("mismatched types %s and %s", c0.typ.id(), c1.typ.id()) break } diff --git a/interp/run.go b/interp/run.go index c24fd275..843d2c4b 100644 --- a/interp/run.go +++ b/interp/run.go @@ -260,7 +260,7 @@ func assign(n *node) { switch { case dest.typ.cat == interfaceT: svalue[i] = genValueInterface(src) - case dest.typ.cat == valueT && dest.typ.rtype.Kind() == reflect.Interface: + case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface: svalue[i] = genInterfaceWrapper(src, dest.typ.rtype) case dest.typ.cat == valueT && src.typ.cat == funcT: svalue[i] = genFunctionWrapper(src) diff --git a/interp/type.go b/interp/type.go index fa273c54..ec38a380 100644 --- a/interp/type.go +++ b/interp/type.go @@ -636,6 +636,51 @@ func (t *itype) finalize() (*itype, error) { return t, err } +// equal returns true if the given type is identical to the receiver one +func (t *itype) equal(o *itype) bool { + if isInterface(t) || isInterface(o) { + // Check for identical methods sets + return reflect.DeepEqual(t.methods(), o.methods()) + } + return t.id() == o.id() +} + +// methods returns a map of method type strings, indexed by method names +func (t *itype) methods() map[string]string { + res := make(map[string]string) + switch t.cat { + case interfaceT: + // Get methods from recursive analysis of interface fields + for _, f := range t.field { + if f.typ.cat == funcT { + res[f.name] = f.typ.TypeOf().String() + } else { + for k, v := range f.typ.methods() { + res[k] = v + } + } + } + case valueT, errorT: + // Get method from corresponding reflect.Type + for i := t.rtype.NumMethod() - 1; i >= 0; i-- { + m := t.rtype.Method(i) + res[m.Name] = m.Type.String() + } + case ptrT: + // Consider only methods where receiver is a pointer to type t + for _, m := range t.val.method { + if m.child[0].child[0].lastChild().typ.cat == ptrT { + res[m.ident] = m.typ.TypeOf().String() + } + } + default: + for _, m := range t.method { + res[m.ident] = m.typ.TypeOf().String() + } + } + return res +} + // id returns a unique type identificator string func (t *itype) id() string { // TODO: if res is nil, build identity from String()