interp: improve handling of empty interface values (#1393)

At variable, function parameter, slice, map or field element assign,
if the destination type is an empty interface, the value was never
wrapped into a valueInterface (to preserve type mutability in case
of re-assign). Now we wrap it in a valueInterface if the source
type has a non empty set of methods, to allow a future use as a non
empty interface.

There are still corner cases, but it extends notably the support
of interfaces within the interpreter.

Fixes #1355.
This commit is contained in:
Marc Vertes
2022-05-19 17:53:56 +02:00
committed by GitHub
parent 821e9ee006
commit d183f4205e
4 changed files with 38 additions and 11 deletions

22
_test/issue-1355.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import "github.com/traefik/yaegi/_test/p2"
func f(i interface{}) {
_, ok := i.(p2.I)
println("ok:", ok)
}
func main() {
var v *p2.T
var i interface{}
i = v
_, ok := i.(p2.I)
println("ok:", ok)
f(v)
}
// Output:
// ok: true
// ok: true

9
_test/p2/p2.go Normal file
View File

@@ -0,0 +1,9 @@
package p2
type I interface {
isI()
}
type T struct{}
func (t *T) isI() {}

View File

@@ -1228,13 +1228,13 @@ func call(n *node) {
convertLiteralValue(c, argType)
}
switch {
case isEmptyInterface(arg):
case hasVariadicArgs:
values = append(values, genValue(c))
case isInterfaceSrc(arg) && !hasVariadicArgs:
case isInterfaceSrc(arg) && (!isEmptyInterface(arg) || len(c.typ.method) > 0):
values = append(values, genValueInterface(c))
case isInterfaceBin(arg):
values = append(values, genInterfaceWrapper(c, arg.rtype))
case isFuncSrc(arg) && !hasVariadicArgs:
case isFuncSrc(arg):
values = append(values, genValueNode(c))
default:
values = append(values, genValue(c))
@@ -2690,7 +2690,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
values[fieldIndex] = genValueAsFunctionWrapper(val)
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val) && !isEmptyInterface(val.typ.val):
values[fieldIndex] = genValueInterfaceArray(val)
case isInterfaceSrc(ft) && !isEmptyInterface(ft):
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
values[fieldIndex] = genValueInterface(val)
case isInterface(ft):
values[fieldIndex] = genInterfaceWrapper(val, rft)
@@ -3131,9 +3131,7 @@ func _append(n *node) {
values := make([]func(*frame) reflect.Value, l)
for i, arg := range args {
switch elem := n.typ.elem(); {
case isEmptyInterface(elem):
values[i] = genValue(arg)
case isInterfaceSrc(elem):
case isInterfaceSrc(elem) && (!isEmptyInterface(elem) || len(arg.typ.method) > 0):
values[i] = genValueInterface(arg)
case isInterfaceBin(elem):
values[i] = genInterfaceWrapper(arg, elem.rtype)
@@ -3155,9 +3153,7 @@ func _append(n *node) {
default:
var value0 func(*frame) reflect.Value
switch elem := n.typ.elem(); {
case isEmptyInterface(elem):
value0 = genValue(n.child[2])
case isInterfaceSrc(elem):
case isInterfaceSrc(elem) && (!isEmptyInterface(elem) || len(n.child[2].typ.method) > 0):
value0 = genValueInterface(n.child[2])
case isInterfaceBin(elem):
value0 = genInterfaceWrapper(n.child[2], elem.rtype)

View File

@@ -219,7 +219,7 @@ func genValue(n *node) func(*frame) reflect.Value {
func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
convertLiteralValue(n, typ.TypeOf())
switch {
case isInterfaceSrc(typ) && !isEmptyInterface(typ):
case isInterfaceSrc(typ) && (!isEmptyInterface(typ) || len(n.typ.method) > 0):
return genValueInterface(n)
case isFuncSrc(typ) && (n.typ.cat == valueT || n.typ.cat == nilT):
return genValueNode(n)