interp: fix closure in a struct field
Functions in a struct fields are always wrapped (as potentially used by the runtime), so generate a function wrapper also for closure when assigned to a struct field. When such a function is called from the interpreter, ensure that interface arguments are also wrapped so method and receiver resolution can be performed. Fixes partially #1043.
This commit is contained in:
26
_test/method36.go
Normal file
26
_test/method36.go
Normal file
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user