interp: do not wrap empty interface
The empty interface (interface{}), and its variants (such as []interface{} and map[string]interface{}), are commonly used in Go to (json) Unmarshal arbitrary data. Within Yaegi, all interface types are wrapped in a valueInterface struct in order to retain all the information needed for a consistent internal state (as reflect is not enough to achieve that). However, this wrapping ends up being problematic when it comes to the type assertions related to the aforementioned Unmarshaling.
Therefore, this PR is an attempt to consider the empty interface (and its variants) as an exception within Yaegi, that should never be wrapped within a valueInterface, and to treat it similarly to the other basic Go types. The assumption is that the wrapping should not be needed, as there is no information about implemented methods to maintain.
Fixes #984
Fixes #829
Fixes #1015
This commit is contained in:
@@ -221,21 +221,25 @@ func genValueRangeArray(n *node) func(*frame) reflect.Value {
|
||||
return value(f).Elem()
|
||||
}
|
||||
case n.typ.val != nil && n.typ.val.cat == interfaceT:
|
||||
return func(f *frame) reflect.Value {
|
||||
val := value(f)
|
||||
v := []valueInterface{}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
switch av := val.Index(i).Interface().(type) {
|
||||
case []valueInterface:
|
||||
v = append(v, av...)
|
||||
case valueInterface:
|
||||
v = append(v, av)
|
||||
default:
|
||||
panic(n.cfgErrorf("invalid type %v", val.Index(i).Type()))
|
||||
if len(n.typ.val.field) > 0 {
|
||||
return func(f *frame) reflect.Value {
|
||||
val := value(f)
|
||||
v := []valueInterface{}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
switch av := val.Index(i).Interface().(type) {
|
||||
case []valueInterface:
|
||||
v = append(v, av...)
|
||||
case valueInterface:
|
||||
v = append(v, av)
|
||||
default:
|
||||
panic(n.cfgErrorf("invalid type %v", val.Index(i).Type()))
|
||||
}
|
||||
}
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
// empty interface, do not wrap.
|
||||
fallthrough
|
||||
default:
|
||||
return func(f *frame) reflect.Value {
|
||||
// This is necessary to prevent changes in the returned
|
||||
@@ -265,6 +269,7 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
nod := n
|
||||
|
||||
for v.IsValid() {
|
||||
// traverse interface indirections to find out concrete type
|
||||
vi, ok := v.Interface().(valueInterface)
|
||||
@@ -274,6 +279,12 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
v = vi.value
|
||||
nod = vi.node
|
||||
}
|
||||
|
||||
// empty interface, do not wrap.
|
||||
if nod.typ.cat == interfaceT && len(nod.typ.field) == 0 {
|
||||
return v
|
||||
}
|
||||
|
||||
return reflect.ValueOf(valueInterface{nod, v})
|
||||
}
|
||||
}
|
||||
@@ -284,12 +295,26 @@ func zeroInterfaceValue() reflect.Value {
|
||||
return reflect.ValueOf(valueInterface{n, v})
|
||||
}
|
||||
|
||||
func wantEmptyInterface(n *node) bool {
|
||||
return n.typ.cat == interfaceT && len(n.typ.field) == 0 ||
|
||||
n.anc.action == aAssign && n.anc.typ.cat == interfaceT && len(n.anc.typ.field) == 0 ||
|
||||
n.anc.kind == returnStmt && n.anc.val.(*node).typ.ret[0].cat == interfaceT && len(n.anc.val.(*node).typ.ret[0].field) == 0
|
||||
}
|
||||
|
||||
func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
switch {
|
||||
case n.anc.action == aAssign && n.anc.typ.cat == interfaceT:
|
||||
if len(n.anc.typ.field) == 0 {
|
||||
// empty interface, do not wrap
|
||||
return value
|
||||
}
|
||||
fallthrough
|
||||
case n.anc.kind == returnStmt && n.anc.val.(*node).typ.ret[0].cat == interfaceT:
|
||||
if len(n.anc.val.(*node).typ.ret[0].field) == 0 {
|
||||
// empty interface, do not wrap
|
||||
return value
|
||||
}
|
||||
// The result of the builtin has to be returned as an interface type.
|
||||
// Wrap it in a valueInterface and return the dereferenced value.
|
||||
return func(f *frame) reflect.Value {
|
||||
|
||||
Reference in New Issue
Block a user