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:
22
_test/issue-1355.go
Normal file
22
_test/issue-1355.go
Normal 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
9
_test/p2/p2.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package p2
|
||||
|
||||
type I interface {
|
||||
isI()
|
||||
}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (t *T) isI() {}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user