fix: handle use of functions in struct fields

This commit is contained in:
Traefiker Bot
2020-03-05 13:40:05 +01:00
committed by GitHub
parent cfb73445a2
commit 2edd18a0c0
6 changed files with 126 additions and 17 deletions

39
_test/struct32.go Normal file
View File

@@ -0,0 +1,39 @@
package main
type T0 struct {
name string
}
type lookupFunc func(s string) T0
type T1 struct {
name string
info lookupFunc
}
func (t T0) F1() bool { println("in F1"); return true }
type T2 struct {
t1 T1
}
func (t2 *T2) f() {
info := t2.t1.info("foo")
println(info.F1())
}
var t0 = T0{"t0"}
func main() {
t := &T2{T1{
"bar", func(s string) T0 { return t0 },
}}
println("hello")
println(t.t1.info("foo").F1())
}
// Output:
// hello
// in F1
// true

34
_test/struct33.go Normal file
View File

@@ -0,0 +1,34 @@
package main
type T0 struct {
name string
}
type lookupFunc func(s string) T0
type T1 struct {
name string
info lookupFunc
}
func (t T0) F1() bool { println("in F1"); return true }
var t0 = T0{"t0"}
func look(s string) T0 { println("in look"); return t0 }
var table = []*T1{{
name: "bar",
info: look,
},
}
func main() {
info := table[0].info
println(info("foo").F1())
}
// Output:
// in look
// in F1
// true

View File

@@ -777,10 +777,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
case isBinCall(n):
n.gen = callBin
if typ := n.child[0].typ.rtype; typ.NumOut() > 0 {
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
n.findex = sc.add(n.typ)
for i := 1; i < typ.NumOut(); i++ {
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
if funcType := n.child[0].typ.val; funcType != nil {
// Use the original unwrapped function type, to allow future field and
// methods resolutions, otherwise impossible on the opaque bin type.
n.typ = funcType.ret[0]
n.findex = sc.add(n.typ)
for i := 1; i < len(funcType.ret); i++ {
sc.add(funcType.ret[i])
}
} else {
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
n.findex = sc.add(n.typ)
for i := 1; i < typ.NumOut(); i++ {
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
}
}
}
default:
@@ -1225,7 +1235,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if n.typ.cat == funcT {
// function in a struct field is always wrapped in reflect.Value
rtype := n.typ.TypeOf()
n.typ = &itype{cat: valueT, rtype: rtype}
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
}
default:
n.gen = getIndexSeq
@@ -1233,7 +1243,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if n.typ.cat == funcT {
// function in a struct field is always wrapped in reflect.Value
rtype := n.typ.TypeOf()
n.typ = &itype{cat: valueT, rtype: rtype}
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
}
}
} else if s, lind, ok := n.typ.lookupBinField(n.child[1].ident); ok {
@@ -1741,6 +1751,10 @@ func isKey(n *node) bool {
(n.anc.kind == fieldExpr && len(n.anc.child) > 1 && n.anc.child[0] == n)
}
func isField(n *node) bool {
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
}
// isNewDefine returns true if node refers to a new definition
func isNewDefine(n *node, sc *scope) bool {
if n.ident == "_" {

View File

@@ -525,7 +525,11 @@ func eval(t *testing.T, i *interp.Interpreter, src string) reflect.Value {
t.Helper()
res, err := i.Eval(src)
if err != nil {
t.Fatal(err)
t.Logf("Error: %v", err)
if e, ok := err.(interp.Panic); ok {
t.Logf(string(e.Stack))
}
t.FailNow()
}
return res
}
@@ -541,7 +545,11 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
}
if err != nil {
t.Fatalf("got an error %v", err)
t.Logf("got an error: %v", err)
if e, ok := err.(interp.Panic); ok {
t.Logf(string(e.Stack))
}
t.FailNow()
}
if fmt.Sprintf("%v", res) != expectedRes {

View File

@@ -264,7 +264,9 @@ func assign(n *node) {
svalue[i] = genValueInterface(src)
case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface:
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
case dest.typ.cat == valueT && src.typ.cat == funcT:
case src.typ.cat == funcT && dest.typ.cat == valueT:
svalue[i] = genFunctionWrapper(src)
case src.typ.cat == funcT && isField(dest):
svalue[i] = genFunctionWrapper(src)
case dest.typ.cat == funcT && src.typ.cat == valueT:
svalue[i] = genValueNode(src)
@@ -708,19 +710,24 @@ func call(n *node) {
}
n.exec = func(f *frame) bltn {
def := value(f).Interface().(*node)
var def *node
var ok bool
bf := value(f)
if def, ok = bf.Interface().(*node); ok {
bf = def.rval
}
// Call bin func if defined
if def.rval.IsValid() {
if bf.IsValid() {
in := make([]reflect.Value, len(values))
for i, v := range values {
in[i] = v(f)
}
if goroutine {
go def.rval.Call(in)
go bf.Call(in)
return tnext
}
out := def.rval.Call(in)
out := bf.Call(in)
for i, v := range rvalues {
if v != nil {
v(f).Set(out[i])

View File

@@ -619,6 +619,8 @@ func (t *itype) finalize() (*itype, error) {
sym, _, found := t.scope.lookup(t.name)
if found && !sym.typ.incomplete {
sym.typ.method = append(sym.typ.method, t.method...)
t.method = sym.typ.method
t.incomplete = false
return sym.typ, nil
}
m := t.method
@@ -1017,12 +1019,17 @@ func isInterface(t *itype) bool {
func isStruct(t *itype) bool {
// Test first for a struct category, because a recursive interpreter struct may be
// represented by an interface{} at reflect level.
if t.cat == structT {
switch t.cat {
case structT:
return true
case aliasT, ptrT:
return isStruct(t.val)
case valueT:
k := t.rtype.Kind()
return k == reflect.Struct || (k == reflect.Ptr && t.rtype.Elem().Kind() == reflect.Struct)
default:
return false
}
rt := t.TypeOf()
k := rt.Kind()
return k == reflect.Struct || (k == reflect.Ptr && rt.Elem().Kind() == reflect.Struct)
}
func isBool(t *itype) bool { return t.TypeOf().Kind() == reflect.Bool }