From 9e1da978b03df3690bb337e28671cdf7c1177eb3 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Tue, 15 Dec 2020 16:14:05 +0100 Subject: [PATCH] interp: fix handling interface types in wrapped functions The interpreter interface type was replaced by a reflect.Value in objects passed or return to function wrappers, losing the ability to retrieve methods. The valueInterface is now preserved, and correctly accessed if wrapped multiple times. Fixes #977. --- _test/interface47.go | 32 ++++++++++++++++++++++++++++++++ interp/cfg.go | 9 ++++++++- interp/run.go | 23 +++++++++++++++-------- interp/value.go | 13 ++++++++++++- 4 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 _test/interface47.go diff --git a/_test/interface47.go b/_test/interface47.go new file mode 100644 index 00000000..0fd8dc56 --- /dev/null +++ b/_test/interface47.go @@ -0,0 +1,32 @@ +package main + +type Doer interface { + Do() error +} + +type T struct { + Name string +} + +func (t *T) Do() error { println("in do"); return nil } + +func f() (Doer, error) { return &T{"truc"}, nil } + +type Ev struct { + doer func() (Doer, error) +} + +func (e *Ev) do() { + d, _ := e.doer() + d.Do() +} + +func main() { + e := &Ev{f} + println(e != nil) + e.do() +} + +// Output: +// true +// in do diff --git a/interp/cfg.go b/interp/cfg.go index 57aa23a9..e9139b45 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -1878,8 +1878,15 @@ func compDefineX(sc *scope, n *node) error { if err != nil { return err } + for funtype.cat == valueT && funtype.val != nil { + // Retrieve original interpreter type from a wrapped function. + // Struct fields of function types are always wrapped in valueT to ensure + // their possible use in runtime. In that case, the val field retains the + // original interpreter type, which is used now. + funtype = funtype.val + } if funtype.cat == valueT { - // Handle functions imported from runtime + // Handle functions imported from runtime. for i := 0; i < funtype.rtype.NumOut(); i++ { types = append(types, &itype{cat: valueT, rtype: funtype.rtype.Out(i)}) } diff --git a/interp/run.go b/interp/run.go index 010a6ef7..becd704f 100644 --- a/interp/run.go +++ b/interp/run.go @@ -386,6 +386,7 @@ func typeAssert(n *node, withResult, withOk bool) { } return next } + v = valueInterfaceValue(v) ok = canAssertTypes(v.Type(), rtype) if !ok { if !withOk { @@ -887,11 +888,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value { if v, ok := r.Interface().(*node); ok { result[i] = genFunctionWrapper(v)(f) } - if def.typ.ret[i].cat == interfaceT { - x := result[i].Interface().(valueInterface).value - result[i] = reflect.New(reflect.TypeOf((*interface{})(nil)).Elem()).Elem() - result[i].Set(x) - } } return result }) @@ -1271,13 +1267,13 @@ func callBin(n *node) { numOut := c.child[0].typ.rtype.NumOut() for j := 0; j < numOut; j++ { ind := c.findex + j - values = append(values, func(f *frame) reflect.Value { return f.data[ind] }) + values = append(values, func(f *frame) reflect.Value { return valueInterfaceValue(f.data[ind]) }) } case isRegularCall(c): // Handle nested function calls: pass returned values as arguments for j := range c.child[0].typ.ret { ind := c.findex + j - values = append(values, func(f *frame) reflect.Value { return f.data[ind] }) + values = append(values, func(f *frame) reflect.Value { return valueInterfaceValue(f.data[ind]) }) } default: if c.kind == basicLit || c.rval.IsValid() { @@ -1371,7 +1367,11 @@ func callBin(n *node) { for i := range rvalues { c := n.anc.child[i] if c.ident != "_" { - rvalues[i] = genValue(c) + if c.typ.cat == interfaceT { + rvalues[i] = genValueInterfaceValue(c) + } else { + rvalues[i] = genValue(c) + } } } n.exec = func(f *frame) bltn { @@ -1714,6 +1714,13 @@ func getMethodByName(n *node) { n.exec = func(f *frame) bltn { val := value0(f).Interface().(valueInterface) + for { + v, ok := val.value.Interface().(valueInterface) + if !ok { + break + } + val = v + } typ := val.node.typ if typ.node == nil && typ.cat == valueT { // happens with a var of empty interface type, that has value of concrete type diff --git a/interp/value.go b/interp/value.go index 022c375e..660065bc 100644 --- a/interp/value.go +++ b/interp/value.go @@ -297,6 +297,17 @@ func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value { return value } +func valueInterfaceValue(v reflect.Value) reflect.Value { + for { + vv, ok := v.Interface().(valueInterface) + if !ok { + break + } + v = vv.value + } + return v +} + func genValueInterfaceValue(n *node) func(*frame) reflect.Value { value := genValue(n) @@ -307,7 +318,7 @@ func genValueInterfaceValue(n *node) func(*frame) reflect.Value { v.Set(zeroInterfaceValue()) v = value(f) } - return v.Interface().(valueInterface).value + return valueInterfaceValue(v) } }