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.
This commit is contained in:
Marc Vertes
2020-12-15 16:14:05 +01:00
committed by GitHub
parent 662838fd80
commit 9e1da978b0
4 changed files with 67 additions and 10 deletions

32
_test/interface47.go Normal file
View File

@@ -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

View File

@@ -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)})
}

View File

@@ -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

View File

@@ -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)
}
}