diff --git a/_test/method36.go b/_test/method36.go new file mode 100644 index 000000000..ce36264dc --- /dev/null +++ b/_test/method36.go @@ -0,0 +1,26 @@ +package main + +type I interface{ Hello() } + +type T struct{ Name string } + +func (t *T) Hello() { println("Hello", t.Name) } + +type FT func(i I) + +type ST struct{ Handler FT } + +func newF() FT { + return func(i I) { + i.Hello() + } +} + +func main() { + st := &ST{} + st.Handler = newF() + st.Handler(&T{"test"}) +} + +// Output: +// Hello test diff --git a/interp/cfg.go b/interp/cfg.go index 67a79caae..33db22d29 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -90,7 +90,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { return false } if !isInterface(dest.typ) { - // Interface types are not propagated, and will be resolved at post-order. + // Interface type are not propagated, and will be resolved at post-order. n.typ = dest.typ } case binaryExpr, unaryExpr, parenExpr: @@ -578,6 +578,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { // Setting a map entry requires an additional step, do not optimize. // As we only write, skip the default useless getIndexMap dest action. dest.gen = nop + case isFuncField(dest): + // Setting a struct field of function type requires an extra step. Do not optimize. case isCall(src) && dest.typ.cat != interfaceT && !isRecursiveField(dest) && n.kind != defineStmt: // Call action may perform the assignment directly. n.gen = nop @@ -2395,6 +2397,10 @@ func isMethod(n *node) bool { return len(n.child[0].child) > 0 // receiver defined } +func isFuncField(n *node) bool { + return isField(n) && isFunc(n.typ) +} + func isMapEntry(n *node) bool { return n.action == aGetIndex && isMap(n.child[0].typ) } diff --git a/interp/run.go b/interp/run.go index bca4ceeb0..0c006ba1a 100644 --- a/interp/run.go +++ b/interp/run.go @@ -1334,16 +1334,18 @@ func callBin(n *node) { tnext := getExec(n.tnext) fnext := getExec(n.fnext) child := n.child[1:] - value := genValue(n.child[0]) + c0 := n.child[0] + value := genValue(c0) var values []func(*frame) reflect.Value - funcType := n.child[0].typ.rtype + funcType := c0.typ.rtype + wt := wrappedType(c0) variadic := -1 if funcType.IsVariadic() { variadic = funcType.NumIn() - 1 } // A method signature obtained from reflect.Type includes receiver as 1st arg, except for interface types. rcvrOffset := 0 - if recv := n.child[0].recv; recv != nil && !isInterface(recv.node.typ) { + if recv := c0.recv; recv != nil && !isInterface(recv.node.typ) { if variadic > 0 || funcType.NumIn() > len(child) { rcvrOffset = 1 } @@ -1392,6 +1394,11 @@ func callBin(n *node) { } } + if wt != nil && wt.arg[i].cat == interfaceT { + values = append(values, genValueInterface(c)) + break + } + switch c.typ.cat { case funcT: values = append(values, genFunctionWrapper(c)) diff --git a/interp/type.go b/interp/type.go index ef01332e5..0861df8af 100644 --- a/interp/type.go +++ b/interp/type.go @@ -1631,6 +1631,13 @@ func defRecvType(n *node) *itype { return nil } +func wrappedType(n *node) *itype { + if n.typ.cat != valueT { + return nil + } + return n.typ.val +} + func isShiftNode(n *node) bool { switch n.action { case aShl, aShr, aShlAssign, aShrAssign: diff --git a/interp/value.go b/interp/value.go index 38fef3d63..b83db7f50 100644 --- a/interp/value.go +++ b/interp/value.go @@ -45,21 +45,35 @@ func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value n.child[0].child == nil || n.child[0].child[0] == nil { return defaultGen } - if n.child[0].child[1] == nil || n.child[0].child[1].ident == "" { + c0 := n.child[0] + if c0.child[1] == nil || c0.child[1].ident == "" { return defaultGen } - value0 := genValue(n.child[0].child[0]) + value0 := genValue(c0.child[0]) return func(f *frame) reflect.Value { - val, ok := value0(f).Interface().(valueInterface) - if !ok { + v := value0(f) + var nod *node + + for v.IsValid() { + // Traverse interface indirections to find out concrete type. + vi, ok := v.Interface().(valueInterface) + if !ok { + break + } + v = vi.value + nod = vi.node + } + + if nod == nil { return defaultGen(f) } - typ := val.node.typ + + typ := nod.typ if typ.node != nil || typ.cat != valueT { return defaultGen(f) } - meth, _ := typ.rtype.MethodByName(n.child[0].child[1].ident) + meth, _ := typ.rtype.MethodByName(c0.child[1].ident) return meth.Func } }