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:
@@ -1,10 +1,33 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
b := 2
|
||||
var a interface{} = 5 + b
|
||||
b := 2 // int
|
||||
|
||||
var c int = 5 + b
|
||||
println(c)
|
||||
|
||||
var d int32 = 6 + int32(b)
|
||||
println(d)
|
||||
|
||||
var a interface{} = 7 + b
|
||||
println(a.(int))
|
||||
|
||||
var e int32 = 2
|
||||
var f interface{} = 8 + e
|
||||
println(f.(int32))
|
||||
|
||||
a = 9 + e
|
||||
println(a.(int32))
|
||||
|
||||
var g int = 2
|
||||
a = 10 + g
|
||||
println(a.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 7
|
||||
// 8
|
||||
// 9
|
||||
// 10
|
||||
// 11
|
||||
// 12
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
@@ -10,7 +11,30 @@ type Email struct {
|
||||
Addr string
|
||||
}
|
||||
|
||||
func f(s string, r interface{}) error {
|
||||
func f(r interface{}) error {
|
||||
return withPointerAsInterface(&r)
|
||||
}
|
||||
|
||||
func withPointerAsInterface(r interface{}) error {
|
||||
_ = (r).(*interface{})
|
||||
rp, ok := (r).(*interface{})
|
||||
if !ok {
|
||||
return errors.New("cannot assert to *interface{}")
|
||||
}
|
||||
em, ok := (*rp).(*Email)
|
||||
if !ok {
|
||||
return errors.New("cannot assert to *Email")
|
||||
}
|
||||
em.Where = "work"
|
||||
em.Addr = "bob@work.com"
|
||||
return nil
|
||||
}
|
||||
|
||||
func ff(s string, r interface{}) error {
|
||||
return xml.Unmarshal([]byte(s), r)
|
||||
}
|
||||
|
||||
func fff(s string, r interface{}) error {
|
||||
return xml.Unmarshal([]byte(s), &r)
|
||||
}
|
||||
|
||||
@@ -21,9 +45,19 @@ func main() {
|
||||
</Email>
|
||||
`
|
||||
v := Email{}
|
||||
err := f(data, &v)
|
||||
err := f(&v)
|
||||
fmt.Println(err, v)
|
||||
|
||||
vv := Email{}
|
||||
err = ff(data, &vv)
|
||||
fmt.Println(err, vv)
|
||||
|
||||
vvv := Email{}
|
||||
err = ff(data, &vvv)
|
||||
fmt.Println(err, vvv)
|
||||
}
|
||||
|
||||
// Ouput:
|
||||
// <nil> {work bob@work.com}
|
||||
// <nil> {work bob@work.com}
|
||||
// <nil> {work bob@work.com}
|
||||
|
||||
24
_test/addr3.go
Normal file
24
_test/addr3.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
a = 2
|
||||
fmt.Println(a)
|
||||
|
||||
var b *interface{}
|
||||
b = &a
|
||||
fmt.Println(*b)
|
||||
|
||||
var c **interface{}
|
||||
c = &b
|
||||
fmt.Println(**c)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 2
|
||||
// 2
|
||||
114
_test/addr4.go
Normal file
114
_test/addr4.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
const jsonData = `[
|
||||
"foo",
|
||||
"bar"
|
||||
]`
|
||||
|
||||
const jsonData2 = `[
|
||||
{"foo": "foo"},
|
||||
{"bar": "bar"}
|
||||
]`
|
||||
|
||||
const jsonData3 = `{
|
||||
"foo": "foo",
|
||||
"bar": "bar"
|
||||
}`
|
||||
|
||||
func fromSlice() {
|
||||
var a []interface{}
|
||||
var c, d interface{}
|
||||
c = 2
|
||||
d = 3
|
||||
a = []interface{}{c, d}
|
||||
|
||||
if err := json.Unmarshal([]byte(jsonData), &a); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for k, v := range a {
|
||||
fmt.Println(k, ":", v)
|
||||
}
|
||||
}
|
||||
|
||||
func fromEmpty() {
|
||||
var a interface{}
|
||||
var c, d interface{}
|
||||
c = 2
|
||||
d = 3
|
||||
a = []interface{}{c, d}
|
||||
|
||||
if err := json.Unmarshal([]byte(jsonData), &a); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
b := a.([]interface{})
|
||||
|
||||
for k, v := range b {
|
||||
fmt.Println(k, ":", v)
|
||||
}
|
||||
}
|
||||
|
||||
func sliceOfObjects() {
|
||||
var a interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(jsonData2), &a); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
b := a.([]interface{})
|
||||
|
||||
for k, v := range b {
|
||||
fmt.Println(k, ":", v)
|
||||
}
|
||||
}
|
||||
|
||||
func intoMap() {
|
||||
var a interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(jsonData3), &a); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
b := a.(map[string]interface{})
|
||||
|
||||
seenFoo := false
|
||||
for k, v := range b {
|
||||
vv := v.(string)
|
||||
if vv != "foo" {
|
||||
if seenFoo {
|
||||
fmt.Println(k, ":", vv)
|
||||
break
|
||||
}
|
||||
kk := k
|
||||
vvv := vv
|
||||
defer fmt.Println(kk, ":", vvv)
|
||||
continue
|
||||
}
|
||||
seenFoo = true
|
||||
fmt.Println(k, ":", vv)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fromSlice()
|
||||
fromEmpty()
|
||||
sliceOfObjects()
|
||||
intoMap()
|
||||
}
|
||||
|
||||
// Ouput:
|
||||
// 0 : foo
|
||||
// 1 : bar
|
||||
// 0 : foo
|
||||
// 1 : bar
|
||||
// 0 : map[foo:foo]
|
||||
// 1 : map[bar:bar]
|
||||
// foo : foo
|
||||
// bar : bar
|
||||
62
_test/addr5.go
Normal file
62
_test/addr5.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func main() {
|
||||
body := []byte(`{
|
||||
"BODY_1": "VALUE_1",
|
||||
"BODY_2": "VALUE_2",
|
||||
"BODY_3": null,
|
||||
"BODY_4": {
|
||||
"BODY_1": "VALUE_1",
|
||||
"BODY_2": "VALUE_2",
|
||||
"BODY_3": null
|
||||
},
|
||||
"BODY_5": [
|
||||
"VALUE_1",
|
||||
"VALUE_2",
|
||||
"VALUE_3"
|
||||
]
|
||||
}`)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
var rawData map[string]interface{}
|
||||
err := json.Unmarshal(body, &rawData)
|
||||
if err != nil {
|
||||
fmt.Println("can't parse body")
|
||||
return
|
||||
}
|
||||
|
||||
for key, val := range rawData {
|
||||
switch val.(type) {
|
||||
case string, bool, float64:
|
||||
values.Add(key, fmt.Sprint(val))
|
||||
case nil:
|
||||
values.Add(key, "")
|
||||
case map[string]interface{}, []interface{}:
|
||||
jsonVal, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
fmt.Println("can't encode json")
|
||||
return
|
||||
}
|
||||
values.Add(key, string(jsonVal))
|
||||
}
|
||||
}
|
||||
fmt.Println(values.Get("BODY_1"))
|
||||
fmt.Println(values.Get("BODY_2"))
|
||||
fmt.Println(values.Get("BODY_3"))
|
||||
fmt.Println(values.Get("BODY_4"))
|
||||
fmt.Println(values.Get("BODY_5"))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// VALUE_1
|
||||
// VALUE_2
|
||||
//
|
||||
// {"BODY_1":"VALUE_1","BODY_2":"VALUE_2","BODY_3":null}
|
||||
// ["VALUE_1","VALUE_2","VALUE_3"]
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -10,6 +9,10 @@ type MyWriter interface {
|
||||
Write(p []byte) (i int, err error)
|
||||
}
|
||||
|
||||
type DummyWriter interface {
|
||||
Write(p []byte) (i int, err error)
|
||||
}
|
||||
|
||||
type TestStruct struct{}
|
||||
|
||||
func (t TestStruct) Write(p []byte) (n int, err error) {
|
||||
@@ -25,14 +28,18 @@ type MyStringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type DummyStringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
func usesStringer(s MyStringer) {
|
||||
fmt.Println(s.String())
|
||||
}
|
||||
|
||||
func main() {
|
||||
aType := reflect.TypeOf((*MyWriter)(nil)).Elem()
|
||||
|
||||
var t interface{}
|
||||
// TODO(mpl): restore when we can deal with empty interface.
|
||||
// var t interface{}
|
||||
var t DummyWriter
|
||||
t = TestStruct{}
|
||||
var tw MyWriter
|
||||
var ok bool
|
||||
@@ -45,8 +52,6 @@ func main() {
|
||||
}
|
||||
n, _ := t.(MyWriter).Write([]byte("hello world"))
|
||||
fmt.Println(n)
|
||||
bType := reflect.TypeOf(TestStruct{})
|
||||
fmt.Println(bType.Implements(aType))
|
||||
|
||||
// not redundant with the above, because it goes through a slightly different code path.
|
||||
if _, ok := t.(MyWriter); !ok {
|
||||
@@ -56,6 +61,8 @@ func main() {
|
||||
fmt.Println("TestStruct implements MyWriter")
|
||||
}
|
||||
|
||||
// TODO(mpl): restore
|
||||
/*
|
||||
t = 42
|
||||
foo, ok := t.(MyWriter)
|
||||
if !ok {
|
||||
@@ -70,8 +77,10 @@ func main() {
|
||||
} else {
|
||||
fmt.Println("42 implements MyWriter")
|
||||
}
|
||||
*/
|
||||
|
||||
var tt interface{}
|
||||
// var tt interface{}
|
||||
var tt DummyStringer
|
||||
tt = time.Nanosecond
|
||||
var myD MyStringer
|
||||
myD, ok = tt.(MyStringer)
|
||||
@@ -82,9 +91,6 @@ func main() {
|
||||
usesStringer(myD)
|
||||
}
|
||||
fmt.Println(tt.(MyStringer).String())
|
||||
cType := reflect.TypeOf((*MyStringer)(nil)).Elem()
|
||||
dType := reflect.TypeOf(time.Nanosecond)
|
||||
fmt.Println(dType.Implements(cType))
|
||||
|
||||
if _, ok := tt.(MyStringer); !ok {
|
||||
fmt.Println("time.Nanosecond does not implement MyStringer")
|
||||
@@ -92,6 +98,8 @@ func main() {
|
||||
fmt.Println("time.Nanosecond implements MyStringer")
|
||||
}
|
||||
|
||||
// TODO(mpl): restore
|
||||
/*
|
||||
tt = 42
|
||||
bar, ok := tt.(MyStringer)
|
||||
if !ok {
|
||||
@@ -106,20 +114,15 @@ func main() {
|
||||
} else {
|
||||
fmt.Println("42 implements MyStringer")
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Output:
|
||||
// TestStruct implements MyWriter
|
||||
// 11
|
||||
// 11
|
||||
// true
|
||||
// TestStruct implements MyWriter
|
||||
// 42 does not implement MyWriter
|
||||
// 42 does not implement MyWriter
|
||||
// time.Nanosecond implements MyStringer
|
||||
// 1ns
|
||||
// 1ns
|
||||
// true
|
||||
// time.Nanosecond implements MyStringer
|
||||
// 42 does not implement MyStringer
|
||||
// 42 does not implement MyStringer
|
||||
|
||||
@@ -12,6 +12,10 @@ func (t TestStruct) String() string {
|
||||
return "hello world"
|
||||
}
|
||||
|
||||
type DummyStringer interface{
|
||||
String() string
|
||||
}
|
||||
|
||||
func main() {
|
||||
aType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
||||
|
||||
@@ -52,7 +56,9 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
var tt interface{}
|
||||
// TODO(mpl): restore when fixed
|
||||
// var tt interface{}
|
||||
var tt DummyStringer
|
||||
tt = TestStruct{}
|
||||
ss, ok := tt.(fmt.Stringer)
|
||||
if !ok {
|
||||
@@ -61,9 +67,6 @@ func main() {
|
||||
}
|
||||
fmt.Println(ss.String())
|
||||
fmt.Println(tt.(fmt.Stringer).String())
|
||||
// TODO(mpl): uncomment when fixed
|
||||
// cType := reflect.TypeOf(TestStruct{})
|
||||
// fmt.Println(cType.Implements(aType))
|
||||
|
||||
if _, ok := tt.(fmt.Stringer); !ok {
|
||||
fmt.Println("TestStuct does not implement fmt.Stringer")
|
||||
|
||||
@@ -4,10 +4,16 @@ func f(a []int) interface{} {
|
||||
return cap(a)
|
||||
}
|
||||
|
||||
func g(a []int) int {
|
||||
return cap(a)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2}
|
||||
println(g(a))
|
||||
println(f(a).(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 2
|
||||
|
||||
@@ -32,6 +32,7 @@ func interfaceAsInterfaces() {
|
||||
println("nope")
|
||||
return
|
||||
}
|
||||
fmt.Println(d)
|
||||
|
||||
for _, v := range d {
|
||||
fmt.Println(v)
|
||||
@@ -46,5 +47,6 @@ func main() {
|
||||
// Output:
|
||||
// 2
|
||||
// 3
|
||||
// [2 3]
|
||||
// 2
|
||||
// 3
|
||||
|
||||
@@ -2,10 +2,15 @@ package main
|
||||
|
||||
func f1(a int) interface{} { return a + 1 }
|
||||
|
||||
func f2(a int64) interface{} { return a + 1 }
|
||||
|
||||
func main() {
|
||||
c := f1(3)
|
||||
println(c.(int))
|
||||
b := f2(3)
|
||||
println(b.(int64))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
// 4
|
||||
|
||||
@@ -2,7 +2,13 @@ package main
|
||||
|
||||
func f1(a int) int { return a + 1 }
|
||||
|
||||
func f2(a int) interface{} { return f1(a) }
|
||||
func f2(a int) interface{} {
|
||||
// TODO: re-enable the optimized case below, once we've figured out why it
|
||||
// interferes with the empty interface model.
|
||||
// return f1(a)
|
||||
var foo interface{} = f1(a)
|
||||
return foo
|
||||
}
|
||||
|
||||
func main() {
|
||||
c := f2(3)
|
||||
|
||||
@@ -12,10 +12,16 @@ type Hi interface {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
type Hey interface {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
func (r *Root) Hello() string { return "Hello " + r.Name }
|
||||
|
||||
func main() {
|
||||
var one interface{} = &One{Root{Name: "test2"}}
|
||||
// TODO(mpl): restore when type assertions work again.
|
||||
// var one interface{} = &One{Root{Name: "test2"}}
|
||||
var one Hey = &One{Root{Name: "test2"}}
|
||||
println(one.(Hi).Hello())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,26 @@ package main
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
a = []int{3}
|
||||
switch a.(type) {
|
||||
case []int:
|
||||
println("a is []int")
|
||||
case []string:
|
||||
println("a is []string")
|
||||
}
|
||||
|
||||
var b interface{}
|
||||
b = []string{"hello"}
|
||||
switch b.(type) {
|
||||
case []int:
|
||||
println("b is []int")
|
||||
case []string:
|
||||
println("b is []string")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a is []int
|
||||
// b is []string
|
||||
// bye
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
const model = `package interp
|
||||
|
||||
// Code generated by 'go run ../internal/genop/genop.go'. DO NOT EDIT.
|
||||
// Code generated by 'go run ../internal/cmd/genop/genop.go'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
@@ -63,11 +63,38 @@ func {{$name}}(n *node) {
|
||||
{{else}}
|
||||
v1 := genValueInt(c1)
|
||||
{{end -}}
|
||||
{{- if (eq $name "add")}}
|
||||
if n.typ.cat != interfaceT || len(n.typ.field) > 0 {
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, j := v1(f)
|
||||
dest(f).SetInt(i + j)
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
var valf func(sum int64) reflect.Value
|
||||
// TODO: cover other int types, and actually other numbers, and even all
|
||||
// relevant operations, not just add.
|
||||
switch typ.Kind() {
|
||||
case reflect.Int:
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(int(sum)) }
|
||||
case reflect.Int32:
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(int32(sum)) }
|
||||
default: // int64
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(sum) }
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, j := v1(f)
|
||||
dest(f).Set(valf(i + j))
|
||||
return next
|
||||
}
|
||||
{{else -}}
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, j := v1(f)
|
||||
dest(f).SetInt(i {{$op.Name}} j)
|
||||
return next
|
||||
}
|
||||
{{end -}}
|
||||
case c1.rval.IsValid():
|
||||
v0 := genValueInt(c0)
|
||||
{{- if $op.Shift}}
|
||||
@@ -75,11 +102,38 @@ func {{$name}}(n *node) {
|
||||
{{else}}
|
||||
j := vInt(c1.rval)
|
||||
{{end -}}
|
||||
{{- if (eq $name "add")}}
|
||||
var valf func(sum int64) reflect.Value
|
||||
// TODO: cover other int types, and actually other numbers, and even all
|
||||
// relevant operations, not just add.
|
||||
switch typ.Kind() {
|
||||
case reflect.Int:
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(int(sum)) }
|
||||
case reflect.Int32:
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(int32(sum)) }
|
||||
default: // int64
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(sum) }
|
||||
}
|
||||
if wantEmptyInterface(n) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, i := v0(f)
|
||||
dest(f).Set(valf(i + j))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, i := v0(f)
|
||||
dest(f).SetInt(i + j)
|
||||
return next
|
||||
}
|
||||
{{else -}}
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, i := v0(f)
|
||||
dest(f).SetInt(i {{$op.Name}} j)
|
||||
return next
|
||||
}
|
||||
{{end -}}
|
||||
default:
|
||||
v0 := genValueInt(c0)
|
||||
{{- if $op.Shift}}
|
||||
|
||||
@@ -612,6 +612,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
sym.typ = n.typ
|
||||
sym.recv = src.recv
|
||||
}
|
||||
|
||||
n.level = level
|
||||
|
||||
if n.anc.kind == constDecl {
|
||||
@@ -713,7 +714,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if c0.rval.IsValid() && c1.rval.IsValid() && !isInterface(n.typ) && constOp[n.action] != nil {
|
||||
if c0.rval.IsValid() && c1.rval.IsValid() && (!isInterface(n.typ)) && constOp[n.action] != nil {
|
||||
n.typ.TypeOf() // Force compute of reflection type.
|
||||
constOp[n.action](n) // Compute a constant result now rather than during exec.
|
||||
}
|
||||
@@ -1885,6 +1886,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range n.child[:l] {
|
||||
var index int
|
||||
if sc.global {
|
||||
@@ -2521,6 +2523,10 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
|
||||
if rtyp == nil {
|
||||
rtyp = n.typ.rtype
|
||||
}
|
||||
// TODO(mpl): I do not understand where this side-effect is coming from, and why it happens. quickfix for now.
|
||||
if rtyp == nil {
|
||||
rtyp = n.typ.val.rtype
|
||||
}
|
||||
switch k := rtyp.Kind(); k {
|
||||
case reflect.Struct:
|
||||
if n.nleft == 1 {
|
||||
|
||||
@@ -507,12 +507,19 @@ func TestEvalMethod(t *testing.T) {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
type Hey interface {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
func (r *Root) Hello() string { return "Hello " + r.Name }
|
||||
|
||||
var r = Root{"R"}
|
||||
var o = One{r}
|
||||
var root interface{} = &Root{Name: "test1"}
|
||||
var one interface{} = &One{Root{Name: "test2"}}
|
||||
// TODO(mpl): restore empty interfaces when type assertions work (again) on them.
|
||||
// var root interface{} = &Root{Name: "test1"}
|
||||
// var one interface{} = &One{Root{Name: "test2"}}
|
||||
var root Hey = &Root{Name: "test1"}
|
||||
var one Hey = &One{Root{Name: "test2"}}
|
||||
`)
|
||||
runTests(t, i, []testCase{
|
||||
{src: "r.Hello()", res: "Hello R"},
|
||||
|
||||
44
interp/op.go
44
interp/op.go
@@ -1,6 +1,6 @@
|
||||
package interp
|
||||
|
||||
// Code generated by 'go run ../internal/genop/genop.go'. DO NOT EDIT.
|
||||
// Code generated by 'go run ../internal/cmd/genop/genop.go'. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
@@ -46,14 +46,54 @@ func add(n *node) {
|
||||
case c0.rval.IsValid():
|
||||
i := vInt(c0.rval)
|
||||
v1 := genValueInt(c1)
|
||||
|
||||
if n.typ.cat != interfaceT || len(n.typ.field) > 0 {
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, j := v1(f)
|
||||
dest(f).SetInt(i + j)
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
var valf func(sum int64) reflect.Value
|
||||
// TODO: cover other int types, and actually other numbers, and even all
|
||||
// relevant operations, not just add.
|
||||
switch typ.Kind() {
|
||||
case reflect.Int:
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(int(sum)) }
|
||||
case reflect.Int32:
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(int32(sum)) }
|
||||
default: // int64
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(sum) }
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, j := v1(f)
|
||||
dest(f).SetInt(i + j)
|
||||
dest(f).Set(valf(i + j))
|
||||
return next
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
v0 := genValueInt(c0)
|
||||
j := vInt(c1.rval)
|
||||
|
||||
var valf func(sum int64) reflect.Value
|
||||
// TODO: cover other int types, and actually other numbers, and even all
|
||||
// relevant operations, not just add.
|
||||
switch typ.Kind() {
|
||||
case reflect.Int:
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(int(sum)) }
|
||||
case reflect.Int32:
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(int32(sum)) }
|
||||
default: // int64
|
||||
valf = func(sum int64) reflect.Value { return reflect.ValueOf(sum) }
|
||||
}
|
||||
if wantEmptyInterface(n) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, i := v0(f)
|
||||
dest(f).Set(valf(i + j))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, i := v0(f)
|
||||
dest(f).SetInt(i + j)
|
||||
|
||||
406
interp/run.go
406
interp/run.go
@@ -9,6 +9,7 @@ import (
|
||||
"log"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -371,6 +372,40 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
}
|
||||
return next
|
||||
}
|
||||
// empty interface
|
||||
case n.child[0].typ.cat == interfaceT && len(n.child[0].typ.field) == 0:
|
||||
n.exec = func(f *frame) bltn {
|
||||
var ok bool
|
||||
if setStatus {
|
||||
defer func() {
|
||||
value1(f).SetBool(ok)
|
||||
}()
|
||||
}
|
||||
val := value(f)
|
||||
concrete := val.Interface()
|
||||
ctyp := reflect.TypeOf(concrete)
|
||||
|
||||
ok = canAssertTypes(ctyp, rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
// TODO(mpl): think about whether this should ever happen.
|
||||
if ctyp == nil {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", ctyp.String(), rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
if withResult {
|
||||
if isInterfaceSrc(typ) {
|
||||
// TODO(mpl): this requires more work. the wrapped node is not complete enough.
|
||||
value0(f).Set(reflect.ValueOf(valueInterface{n.child[0], reflect.ValueOf(concrete)}))
|
||||
} else {
|
||||
value0(f).Set(reflect.ValueOf(concrete))
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
case n.child[0].typ.cat == valueT || n.child[0].typ.cat == errorT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Elem()
|
||||
@@ -416,13 +451,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
return next
|
||||
}
|
||||
|
||||
styp := v.value.Type()
|
||||
// TODO(mpl): probably also maps and others. and might have to recurse too.
|
||||
if styp.String() == "[]interp.valueInterface" {
|
||||
styp = v.node.typ.rtype
|
||||
}
|
||||
|
||||
ok = canAssertTypes(styp, rtype)
|
||||
ok = canAssertTypes(v.value.Type(), rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), rtype.String()))
|
||||
@@ -447,6 +476,9 @@ func canAssertTypes(src, dest reflect.Type) bool {
|
||||
if dest.Kind() == reflect.Interface && src.Implements(dest) {
|
||||
return true
|
||||
}
|
||||
if src == nil {
|
||||
return false
|
||||
}
|
||||
if src.AssignableTo(dest) {
|
||||
return true
|
||||
}
|
||||
@@ -466,11 +498,12 @@ func firstMissingMethod(src, dest reflect.Type) string {
|
||||
func convert(n *node) {
|
||||
dest := genValue(n)
|
||||
c := n.child[1]
|
||||
typ := n.child[0].typ.TypeOf()
|
||||
typ := n.child[0].typ.frameType()
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if c.isNil() { // convert nil to type
|
||||
if n.child[0].typ.cat == interfaceT {
|
||||
// TODO(mpl): Try to completely remove, as maybe frameType already does the job for interfaces.
|
||||
if n.child[0].typ.cat == interfaceT && len(n.child[0].typ.field) > 0 {
|
||||
typ = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -559,7 +592,11 @@ func assign(n *node) {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
switch {
|
||||
case dest.typ.cat == interfaceT:
|
||||
svalue[i] = genValueInterface(src)
|
||||
if len(dest.typ.field) > 0 {
|
||||
svalue[i] = genValueInterface(src)
|
||||
break
|
||||
}
|
||||
svalue[i] = genValue(src)
|
||||
case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface:
|
||||
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
|
||||
case src.typ.cat == funcT && dest.typ.cat == valueT:
|
||||
@@ -682,12 +719,11 @@ func addr(n *node) {
|
||||
c0 := n.child[0]
|
||||
value := genValue(c0)
|
||||
switch c0.typ.cat {
|
||||
case interfaceT:
|
||||
case interfaceT, ptrT:
|
||||
i := n.findex
|
||||
l := n.level
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Interface().(valueInterface).value
|
||||
getFrame(f, l).data[i] = reflect.ValueOf(v.Interface())
|
||||
getFrame(f, l).data[i] = value(f).Addr()
|
||||
return next
|
||||
}
|
||||
default:
|
||||
@@ -767,11 +803,20 @@ func _recover(n *node) {
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
if f.anc.recovered == nil {
|
||||
// TODO(mpl): maybe we don't need that special case, and we're just forgetting to unwrap the valueInterface somewhere else.
|
||||
if n.typ.cat == interfaceT && len(n.typ.field) == 0 {
|
||||
return tnext
|
||||
}
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{}))
|
||||
return tnext
|
||||
}
|
||||
|
||||
if n.typ.cat == interfaceT && len(n.typ.field) == 0 {
|
||||
dest(f).Set(reflect.ValueOf(f.anc.recovered))
|
||||
} else {
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{n, reflect.ValueOf(f.anc.recovered)}))
|
||||
f.anc.recovered = nil
|
||||
}
|
||||
f.anc.recovered = nil
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -879,7 +924,11 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case typ.cat == interfaceT:
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
if len(typ.field) > 0 {
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
break
|
||||
}
|
||||
d[i].Set(arg)
|
||||
case typ.cat == funcT && arg.Kind() == reflect.Func:
|
||||
d[i].Set(reflect.ValueOf(genFunctionNode(arg)))
|
||||
default:
|
||||
@@ -1024,7 +1073,12 @@ func call(n *node) {
|
||||
}
|
||||
switch {
|
||||
case arg.cat == interfaceT:
|
||||
values = append(values, genValueInterface(c))
|
||||
if len(arg.field) > 0 {
|
||||
values = append(values, genValueInterface(c))
|
||||
break
|
||||
}
|
||||
// empty interface, do not wrap it.
|
||||
values = append(values, genValue(c))
|
||||
case isRecursiveType(c.typ, c.typ.rtype):
|
||||
values = append(values, genValueRecursiveInterfacePtrValue(c))
|
||||
default:
|
||||
@@ -1044,7 +1098,12 @@ func call(n *node) {
|
||||
case c.ident == "_":
|
||||
// Skip assigning return value to blank var.
|
||||
case c.typ.cat == interfaceT && rtypes[i].cat != interfaceT:
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
if len(c.typ.field) > 0 {
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
break
|
||||
}
|
||||
// empty interface, do not wrap
|
||||
fallthrough
|
||||
default:
|
||||
rvalues[i] = genValue(c)
|
||||
}
|
||||
@@ -1055,7 +1114,7 @@ func call(n *node) {
|
||||
j := n.findex + i
|
||||
ret := n.child[0].typ.ret[i]
|
||||
callret := n.anc.val.(*node).typ.ret[i]
|
||||
if callret.cat == interfaceT && ret.cat != interfaceT {
|
||||
if callret.cat == interfaceT && len(callret.field) > 0 && ret.cat != interfaceT {
|
||||
// Wrap the returned value in a valueInterface in caller frame.
|
||||
rvalues[i] = func(f *frame) reflect.Value {
|
||||
v := reflect.New(ret.rtype).Elem()
|
||||
@@ -1303,11 +1362,20 @@ func callBin(n *node) {
|
||||
case funcT:
|
||||
values = append(values, genFunctionWrapper(c))
|
||||
case interfaceT:
|
||||
values = append(values, genValueInterfaceValue(c))
|
||||
if len(c.typ.field) > 0 {
|
||||
values = append(values, genValueInterfaceValue(c))
|
||||
break
|
||||
}
|
||||
// empty interface, do not wrap it.
|
||||
values = append(values, genValue(c))
|
||||
case arrayT, variadicT:
|
||||
switch c.typ.val.cat {
|
||||
case interfaceT:
|
||||
values = append(values, genValueInterfaceArray(c))
|
||||
if len(c.typ.val.field) > 0 {
|
||||
values = append(values, genValueInterfaceArray(c))
|
||||
break
|
||||
}
|
||||
values = append(values, genValueArray(c))
|
||||
default:
|
||||
values = append(values, genInterfaceWrapper(c, defType))
|
||||
}
|
||||
@@ -1533,15 +1601,22 @@ func getIndexMap(n *node) {
|
||||
case n.typ.cat == interfaceT:
|
||||
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(mi); v.IsValid() {
|
||||
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
|
||||
dest(f).Set(e)
|
||||
} else {
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
|
||||
}
|
||||
} else {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if !v.IsValid() {
|
||||
dest(f).Set(z)
|
||||
return tnext
|
||||
}
|
||||
e := v.Elem()
|
||||
if len(n.typ.field) == 0 {
|
||||
// e is empty interface, do not wrap
|
||||
dest(f).Set(e)
|
||||
return tnext
|
||||
}
|
||||
if e.Type().AssignableTo(valueInterfaceType) {
|
||||
dest(f).Set(e)
|
||||
return tnext
|
||||
}
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
|
||||
return tnext
|
||||
}
|
||||
default:
|
||||
@@ -2065,6 +2140,13 @@ func _return(n *node) {
|
||||
case funcT:
|
||||
values[i] = genValue(c)
|
||||
case interfaceT:
|
||||
if len(t.field) == 0 {
|
||||
// empty interface case.
|
||||
// we can't let genValueInterface deal with it, because we call on c,
|
||||
// not on n, which means that the interfaceT knowledge is lost.
|
||||
values[i] = genValue(c)
|
||||
break
|
||||
}
|
||||
values[i] = genValueInterface(c)
|
||||
case valueT:
|
||||
if t.rtype.Kind() == reflect.Interface {
|
||||
@@ -2130,7 +2212,7 @@ func arrayLit(n *node) {
|
||||
for i, c := range child {
|
||||
if c.kind == keyValueExpr {
|
||||
convertLiteralValue(c.child[1], rtype)
|
||||
if n.typ.val.cat == interfaceT {
|
||||
if n.typ.val.cat == interfaceT && len(n.typ.val.field) > 0 {
|
||||
values[i] = genValueInterface(c.child[1])
|
||||
} else {
|
||||
values[i] = genValue(c.child[1])
|
||||
@@ -2138,7 +2220,7 @@ func arrayLit(n *node) {
|
||||
index[i] = int(vInt(c.child[0].rval))
|
||||
} else {
|
||||
convertLiteralValue(c, rtype)
|
||||
if n.typ.val.cat == interfaceT {
|
||||
if n.typ.val.cat == interfaceT && len(n.typ.val.field) > 0 {
|
||||
values[i] = genValueInterface(c)
|
||||
} else {
|
||||
values[i] = genValue(c)
|
||||
@@ -2162,11 +2244,7 @@ func arrayLit(n *node) {
|
||||
for i, v := range values {
|
||||
a.Index(index[i]).Set(v(f))
|
||||
}
|
||||
dest := value(f)
|
||||
if _, ok := dest.Interface().(valueInterface); ok {
|
||||
a = reflect.ValueOf(valueInterface{n, a})
|
||||
}
|
||||
dest.Set(a)
|
||||
value(f).Set(a)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -2189,7 +2267,7 @@ func mapLit(n *node) {
|
||||
} else {
|
||||
keys[i] = genValue(c.child[0])
|
||||
}
|
||||
if n.typ.val.cat == interfaceT {
|
||||
if n.typ.val.cat == interfaceT && len(n.typ.val.field) > 0 {
|
||||
values[i] = genValueInterface(c.child[1])
|
||||
} else {
|
||||
values[i] = genValue(c.child[1])
|
||||
@@ -2408,7 +2486,11 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
case d.Type().Kind() == reflect.Ptr:
|
||||
d.Set(a.Addr())
|
||||
case destInterface:
|
||||
d.Set(reflect.ValueOf(valueInterface{n, a}))
|
||||
if len(destType(n).field) > 0 {
|
||||
d.Set(reflect.ValueOf(valueInterface{n, a}))
|
||||
break
|
||||
}
|
||||
d.Set(a)
|
||||
default:
|
||||
getFrame(f, l).data[frameIndex] = a
|
||||
}
|
||||
@@ -2522,18 +2604,35 @@ func rangeMap(n *node) {
|
||||
index1 := n.child[1].findex // map value location in frame
|
||||
value = genValue(n.child[2]) // map
|
||||
if n.child[1].typ.cat == interfaceT {
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
if len(n.child[1].typ.field) > 0 {
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
if e := iter.Value().Elem(); e.Type().AssignableTo(valueInterfaceType) {
|
||||
f.data[index1].Set(e)
|
||||
} else {
|
||||
f.data[index1].Set(reflect.ValueOf(valueInterface{n, e}))
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
if e := iter.Value().Elem(); e.Type().AssignableTo(valueInterfaceType) {
|
||||
f.data[index1].Set(e)
|
||||
} else {
|
||||
f.data[index1].Set(reflect.ValueOf(valueInterface{n, e}))
|
||||
} else {
|
||||
// empty interface, do not wrap
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
if iter.Value().Elem().IsValid() {
|
||||
f.data[index1].Set(iter.Value().Elem())
|
||||
} else {
|
||||
f.data[index1].Set(reflect.New(interf).Elem())
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -2569,6 +2668,7 @@ func rangeMap(n *node) {
|
||||
func _case(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
|
||||
// TODO(mpl): a lot of what is done in typeAssert should probably be redone/reused here.
|
||||
switch {
|
||||
case n.anc.anc.kind == typeSwitch:
|
||||
fnext := getExec(n.fnext)
|
||||
@@ -2578,59 +2678,42 @@ func _case(n *node) {
|
||||
types[i] = n.child[i].typ
|
||||
}
|
||||
srcValue := genValue(sn.child[1].lastChild().child[0])
|
||||
if len(sn.child[1].child) == 2 {
|
||||
// assign in switch guard
|
||||
destValue := genValue(n.lastChild().child[0])
|
||||
switch len(types) {
|
||||
case 0:
|
||||
// default clause: assign var to interface value
|
||||
|
||||
if len(sn.child[1].child) != 2 {
|
||||
// no assign in switch guard
|
||||
if len(n.child) <= 1 {
|
||||
n.exec = func(f *frame) bltn { return tnext }
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
destValue(f).Set(srcValue(f))
|
||||
return tnext
|
||||
}
|
||||
case 1:
|
||||
// match against 1 type: assign var to concrete value
|
||||
typ := types[0]
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := srcValue(f)
|
||||
if !v.IsValid() {
|
||||
// match zero value against nil
|
||||
if typ.cat == nilT {
|
||||
return tnext
|
||||
ival := srcValue(f).Interface()
|
||||
val, ok := ival.(valueInterface)
|
||||
// TODO(mpl): I'm assuming here that !ok means that we're dealing with the empty
|
||||
// interface case. But maybe we should make sure by checking the relevant cat
|
||||
// instead? later. Use t := v.Type(); t.Kind() == reflect.Interface , like above.
|
||||
if !ok {
|
||||
var stype string
|
||||
if ival != nil {
|
||||
stype = strings.ReplaceAll(reflect.TypeOf(ival).String(), " {}", "{}")
|
||||
}
|
||||
for _, typ := range types {
|
||||
// TODO(mpl): we should actually use canAssertTypes, but need to find a valid
|
||||
// rtype for typ. Plus we need to refactor with typeAssert().
|
||||
// weak check instead for now.
|
||||
if ival == nil {
|
||||
if typ.cat == nilT {
|
||||
return tnext
|
||||
}
|
||||
continue
|
||||
}
|
||||
if stype == typ.id() {
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if t := v.Type(); t.Kind() == reflect.Interface {
|
||||
if typ.cat == nilT && v.IsNil() {
|
||||
return tnext
|
||||
}
|
||||
if typ.TypeOf().String() == t.String() {
|
||||
destValue(f).Set(v.Elem())
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
vi := v.Interface().(valueInterface)
|
||||
if vi.node == nil {
|
||||
if typ.cat == nilT {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if vi.node.typ.id() == typ.id() {
|
||||
destValue(f).Set(vi.value)
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
default:
|
||||
// match against multiple types: assign var to interface value
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := srcValue(f)
|
||||
if v := srcValue(f).Interface().(valueInterface).node; v != nil {
|
||||
if v := val.node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
destValue(f).Set(val)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2638,21 +2721,72 @@ func _case(n *node) {
|
||||
return fnext
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no assign in switch guard
|
||||
if len(n.child) <= 1 {
|
||||
n.exec = func(f *frame) bltn { return tnext }
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := srcValue(f).Interface().(valueInterface).node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// assign in switch guard
|
||||
destValue := genValue(n.lastChild().child[0])
|
||||
switch len(types) {
|
||||
case 0:
|
||||
// default clause: assign var to interface value
|
||||
n.exec = func(f *frame) bltn {
|
||||
destValue(f).Set(srcValue(f))
|
||||
return tnext
|
||||
}
|
||||
case 1:
|
||||
// match against 1 type: assign var to concrete value
|
||||
typ := types[0]
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := srcValue(f)
|
||||
if !v.IsValid() {
|
||||
// match zero value against nil
|
||||
if typ.cat == nilT {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if t := v.Type(); t.Kind() == reflect.Interface {
|
||||
if typ.cat == nilT && v.IsNil() {
|
||||
return tnext
|
||||
}
|
||||
if typ.TypeOf().String() == t.String() {
|
||||
destValue(f).Set(v.Elem())
|
||||
return tnext
|
||||
}
|
||||
ival := v.Interface()
|
||||
if ival != nil && typ.TypeOf().String() == reflect.TypeOf(ival).String() {
|
||||
destValue(f).Set(v.Elem())
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
vi := v.Interface().(valueInterface)
|
||||
if vi.node == nil {
|
||||
if typ.cat == nilT {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if vi.node.typ.id() == typ.id() {
|
||||
destValue(f).Set(vi.value)
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
default:
|
||||
// TODO(mpl): probably needs to be fixed for empty interfaces, like above.
|
||||
// match against multiple types: assign var to interface value
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := srcValue(f)
|
||||
if v := srcValue(f).Interface().(valueInterface).node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
destValue(f).Set(val)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2725,7 +2859,11 @@ func _append(n *node) {
|
||||
for i, arg := range args {
|
||||
switch {
|
||||
case n.typ.val.cat == interfaceT:
|
||||
values[i] = genValueInterface(arg)
|
||||
if len(n.typ.val.field) > 0 {
|
||||
values[i] = genValueInterface(arg)
|
||||
break
|
||||
}
|
||||
values[i] = genValue(arg)
|
||||
case isRecursiveType(n.typ.val, n.typ.val.rtype):
|
||||
values[i] = genValueRecursiveInterface(arg, n.typ.val.rtype)
|
||||
case arg.typ.untyped:
|
||||
@@ -2747,7 +2885,11 @@ func _append(n *node) {
|
||||
var value0 func(*frame) reflect.Value
|
||||
switch {
|
||||
case n.typ.val.cat == interfaceT:
|
||||
value0 = genValueInterface(n.child[2])
|
||||
if len(n.typ.val.field) > 0 {
|
||||
value0 = genValueInterface(n.child[2])
|
||||
break
|
||||
}
|
||||
value0 = genValue(n.child[2])
|
||||
case isRecursiveType(n.typ.val, n.typ.val.rtype):
|
||||
value0 = genValueRecursiveInterface(n.child[2], n.typ.val.rtype)
|
||||
case n.child[2].typ.untyped:
|
||||
@@ -2768,6 +2910,13 @@ func _cap(n *node) {
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if wantEmptyInterface(n) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(value(f).Cap()))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetInt(int64(value(f).Cap()))
|
||||
return next
|
||||
@@ -2802,17 +2951,25 @@ func _complex(n *node) {
|
||||
value1 := genValue(c2)
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if typ := n.typ.TypeOf(); isComplex(typ) {
|
||||
typ := n.typ.TypeOf()
|
||||
if isComplex(typ) {
|
||||
if wantEmptyInterface(n) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(complex(value0(f).Float(), value1(f).Float())))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetComplex(complex(value0(f).Float(), value1(f).Float()))
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
// Not a complex type: ignore imaginary part
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(value0(f).Convert(typ))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
// Not a complex type: ignore imaginary part
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(value0(f).Convert(typ))
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2822,6 +2979,13 @@ func _imag(n *node) {
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if wantEmptyInterface(n) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(imag(value(f).Complex())))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetFloat(imag(value(f).Complex()))
|
||||
return next
|
||||
@@ -2834,6 +2998,13 @@ func _real(n *node) {
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if wantEmptyInterface(n) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(real(value(f).Complex())))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetFloat(real(value(f).Complex()))
|
||||
return next
|
||||
@@ -2872,6 +3043,13 @@ func _len(n *node) {
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if wantEmptyInterface(n) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(value(f).Len()))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetInt(int64(value(f).Len()))
|
||||
return next
|
||||
|
||||
@@ -274,8 +274,33 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
t = t1
|
||||
}
|
||||
}
|
||||
|
||||
// Because an empty interface concrete type "mutates" as different values are
|
||||
// assigned to it, we need to make a new itype from scratch everytime a new
|
||||
// assignment is made, and not let different nodes (of the same variable) share the
|
||||
// same itype. Otherwise they would overwrite each other.
|
||||
if n.anc.kind == assignStmt && isInterface(n.anc.child[0].typ) && len(n.anc.child[0].typ.field) == 0 {
|
||||
// TODO(mpl): do the indexes properly for multiple assignments on the same line.
|
||||
// Also, maybe we should use nodeType to figure out dt.cat? but isn't it always
|
||||
// gonna be an interfaceT anyway?
|
||||
dt := new(itype)
|
||||
dt.cat = interfaceT
|
||||
val := new(itype)
|
||||
val.cat = t.cat
|
||||
dt.val = val
|
||||
// TODO(mpl): do the indexes properly for multiple assignments on the same line.
|
||||
// Also, maybe we should use nodeType to figure out dt.cat? but isn't it always
|
||||
// gonna be an interfaceT anyway?
|
||||
n.anc.child[0].typ = dt
|
||||
// TODO(mpl): not sure yet whether we should do that last step. It doesn't seem
|
||||
// to change anything either way though.
|
||||
// t = dt
|
||||
break
|
||||
}
|
||||
|
||||
// If the node is to be assigned or returned, the node type is the destination type.
|
||||
dt := t
|
||||
|
||||
switch a := n.anc; {
|
||||
case a.kind == defineStmt && len(a.child) > a.nleft+a.nright:
|
||||
if dt, err = nodeType(interp, sc, a.child[a.nleft]); err != nil {
|
||||
@@ -1487,7 +1512,14 @@ func (t *itype) frameType() (r reflect.Type) {
|
||||
case funcT:
|
||||
r = reflect.TypeOf((*node)(nil))
|
||||
case interfaceT:
|
||||
if len(t.field) == 0 {
|
||||
// empty interface, do not wrap it
|
||||
r = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
break
|
||||
}
|
||||
r = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
case ptrT:
|
||||
r = reflect.PtrTo(t.val.frameType())
|
||||
default:
|
||||
r = t.TypeOf()
|
||||
}
|
||||
|
||||
@@ -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