Repository: traefik/yaegi (Go)

Issue:
Assignation of a func as a struct field
The following program `sample.go` triggers a panic:

```go
package main

import "fmt"

type Fromage struct {
	Name string
	Call func(string) string
}

func main() {
	a := Fromage{}
	a.Name = "test"

	a.Call = func(s string) string {
		return s
	}

	fmt.Println(a.Call(a.Name))
}

// Output:
// test
```

Expected result:
```console
$ go run ./sample.go
test
```

Got:
```console
$ dyngo ./sample.go
panic: reflect.Set: value of type *interp.Node is not assignable to type func(string) string [recovered]
	panic: reflect.Set: value of type *interp.Node is not assignable to type func(string) string

goroutine 1 [running]:
github.com/containous/dyngo/interp.runCfg.func1(0xc0001944b0)
	/home/ldez/sources/go/src/github.com/containous/dyngo/interp/run.go:103 +0x134
panic(0xb9d580, 0xc000180a80)
	/home/ldez/.gvm/gos/go1.11.5/src/runtime/panic.go:513 +0x1b9
reflect.Value.assignTo(0xc1d700, 0xc0002570e0, 0x16, 0xcac792, 0xb, 0xbb83a0, 0x0, 0x120, 0xc96100, 0xc000217d01)
	/home/ldez/.gvm/gos/go1.11.5/src/reflect/value.go:2269 +0x44c
reflect.Value.Set(0xbb83a0, 0xc00017ee50, 0x193, 0xc1d700, 0xc0002570e0, 0x16)
	/home/ldez/.gvm/gos/go1.11.5/src/reflect/value.go:1403 +0xa7
github.com/containous/dyngo/interp.assign.func2(0xc0001944b0, 0xc00017ed60)
	/home/ldez/sources/go/src/github.com/containous/dyngo/interp/run.go:208 +0xb6
github.com/containous/dyngo/interp.runCfg(0xc0002545a0, 0xc0001944b0)
	/home/ldez/sources/go/src/github.com/containous/dyngo/interp/run.go:108 +0x62
github.com/containous/dyngo/interp.(*Interpreter).run(0xc000200070, 0xc0001f9c20, 0xc000194460)
	/home/ldez/sources/go/src/github.com/containous/dyngo/interp/run.go:90 +0xa6
github.com/containous/dyngo/interp.(*Interpreter).Eval(0xc000200070, 0xc000204000, 0xf0, 0xca131f, 0x4, 0xc000200070, 0x0, 0x115a5b8)
	/home/ldez/sources/go/src/github.com/containous/dyngo/interp/interp.go:236 +0x4c3
main.main()
	/home/ldez/sources/go/src/github.com/containous/dyngo/dyngo.go:50 +0x334
exit status 2

```



Discussion:

Patch:
diff --git a/interp/cfg.go b/interp/cfg.go
index 41eaa483c..0bf9f0610 100644
--- a/interp/cfg.go
+++ b/interp/cfg.go
@@ -951,8 +951,14 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
 					n.typ = n.typ.fieldSeq(ti)
 					n.gen = getPtrIndexSeq
 				default:
-					n.typ = n.typ.fieldSeq(ti)
 					n.gen = getIndexSeq
+					n.typ = n.typ.fieldSeq(ti)
+					if n.typ.cat == FuncT {
+						// function in a struct field is always wrapped in reflect.Value
+						rtype := n.typ.TypeOf()
+						n.typ = &Type{cat: ValueT, rtype: rtype}
+						n.fsize = rtype.NumOut()
+					}
 				}
 			} else {
 				err = n.cfgError("undefined selector: %s", n.child[1].ident)
diff --git a/interp/run.go b/interp/run.go
index 28a389693..b2a53eadd 100644
--- a/interp/run.go
+++ b/interp/run.go
@@ -192,7 +192,6 @@ func assignX(n *Node) {
 
 // assign implements single value assignment
 func assign(n *Node) {
-	value := genValue(n)
 	next := getExec(n.tnext)
 
 	if n.child[0].typ.cat == InterfaceT {
@@ -203,7 +202,14 @@ func assign(n *Node) {
 			return next
 		}
 	} else {
-		value1 := genValue(n.lastChild())
+		value := genValue(n)
+		dest, src := n.child[0], n.lastChild()
+		var value1 func(*Frame) reflect.Value
+		if dest.typ.cat == ValueT && src.typ.cat == FuncT {
+			value1 = genNodeWrapper(src)
+		} else {
+			value1 = genValue(src)
+		}
 		n.exec = func(f *Frame) Builtin {
 			value(f).Set(value1(f))
 			return next


Test patch:
diff --git a/interp/interp_eval_test.go b/interp/interp_eval_test.go
index f3e6b56d4..bddf63a52 100644
--- a/interp/interp_eval_test.go
+++ b/interp/interp_eval_test.go
@@ -1,6 +1,7 @@
 package interp_test
 
 import (
+	"log"
 	"reflect"
 	"testing"
 
@@ -98,6 +99,55 @@ func TestEvalNil2(t *testing.T) {
 	}
 }
 
+func TestEvalStruct0(t *testing.T) {
+	i := interp.New(interp.Opt{})
+	evalCheck(t, i, `
+type Fromage struct {
+	Name string
+	Call func(string) string
+}
+
+func f() string {
+	a := Fromage{}
+	a.Name = "test"
+	a.Call = func(s string) string { return s }
+
+	return a.Call(a.Name)
+}
+`)
+
+	v := evalCheck(t, i, `f()`)
+	if v.Interface().(string) != "test" {
+		t.Fatalf("expected test, got %v", v)
+	}
+}
+
+func TestEvalStruct1(t *testing.T) {
+	t.Skip("not yet implemented")
+	log.SetFlags(log.Lshortfile)
+	i := interp.New(interp.Opt{})
+	evalCheck(t, i, `
+type Fromage struct {
+	Name string
+	Call func(string) string
+}
+
+func f() string {
+	a := Fromage{
+		"test",
+		func(s string) string { return s },
+	}
+
+	return a.Call(a.Name)
+}
+`)
+
+	v := evalCheck(t, i, `f()`)
+	if v.Interface().(string) != "test" {
+		t.Fatalf("expected test, got %v", v)
+	}
+}
+
 func TestEvalComposite0(t *testing.T) {
 	i := interp.New(interp.Opt{})
 	evalCheck(t, i, `