Repository: traefik/yaegi (Go)

Issue:
struct with many fields panic
The following program `sample.go` triggers a panic:

```go
package main

import "fmt"

type T struct {
    a, b, c, d, e, f, g, h, i, j, k, l, m, n string
    o map[string]int
    p []string
}

func main() {
    a := T{
        o: map[string]int{"truc": 1, "machin": 2},
        p: []string{"hello", "world"},
    }
    fmt.Println(a)
}
```

Expected result:
```console
$ go run ./sample.go
{              map[truc:1 machin:2] [hello world]}
```

Got:
```console
$ dyngo ./sample.go
panic: runtime error: index out of range

goroutine 1 [running]:
github.com/containous/dyngo/interp.frameTypes.func1(0xc000233440, 0x1)
	/Users/marc/go/src/github.com/containous/dyngo/interp/cfg.go:1352 +0x2a7
github.com/containous/dyngo/interp.(*Node).Walk(0xc000233440, 0xc0001edc40, 0x0)
	/Users/marc/go/src/github.com/containous/dyngo/interp/interp.go:108 +0xb5
github.com/containous/dyngo/interp.(*Node).Walk(0xc000233200, 0xc0001edc40, 0x0)
	/Users/marc/go/src/github.com/containous/dyngo/interp/interp.go:112 +0x66
github.com/containous/dyngo/interp.(*Node).Walk(0xc000232fc0, 0xc0001edc40, 0x0)
	/Users/marc/go/src/github.com/containous/dyngo/interp/interp.go:112 +0x66
github.com/containous/dyngo/interp.(*Node).Walk(0xc000232ea0, 0xc0001edc40, 0x0)
	/Users/marc/go/src/github.com/containous/dyngo/interp/interp.go:112 +0x66
github.com/containous/dyngo/interp.(*Node).Walk(0xc000232900, 0xc0001edc40, 0x0)
	/Users/marc/go/src/github.com/containous/dyngo/interp/interp.go:112 +0x66
github.com/containous/dyngo/interp.frameTypes(0xc000232900, 0x8, 0x0, 0xc00015a0f8, 0xc0001edce0, 0x11ce446, 0xc0001cec60)
	/Users/marc/go/src/github.com/containous/dyngo/interp/cfg.go:1342 +0xd7
github.com/containous/dyngo/interp.genRun.func1(0xc000232900, 0xc0001edda8)
	/Users/marc/go/src/github.com/containous/dyngo/interp/cfg.go:1121 +0x1b8
github.com/containous/dyngo/interp.(*Node).Walk(0xc000232900, 0xc0001edda8, 0x0)
	/Users/marc/go/src/github.com/containous/dyngo/interp/interp.go:108 +0xb5
github.com/containous/dyngo/interp.(*Node).Walk(0xc0001ce360, 0xc0001edda8, 0x0)
	/Users/marc/go/src/github.com/containous/dyngo/interp/interp.go:112 +0x66
github.com/containous/dyngo/interp.genRun(0xc0001ce360, 0xc0001a12f0, 0xc00015e060)
	/Users/marc/go/src/github.com/containous/dyngo/interp/cfg.go:1110 +0x65
github.com/containous/dyngo/interp.(*Interpreter).Eval(0xc0001d6070, 0xc0001da000, 0x153, 0x189b0ad, 0x4, 0xc0001d6070, 0x0, 0x1795a00)
	/Users/marc/go/src/github.com/containous/dyngo/interp/interp.go:233 +0x397
main.main()
	/Users/marc/go/src/github.com/containous/dyngo/dyngo.go:50 +0x334
```



Discussion:

Patch:
diff --git a/interp/cfg.go b/interp/cfg.go
index 6264eb323..41eaa483c 100644
--- a/interp/cfg.go
+++ b/interp/cfg.go
@@ -556,17 +556,10 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
 			case MapT:
 				n.gen = mapLit
 			case StructT:
-				n.action, n.gen = CompositeLit, compositeLit
-				child := n.child
-				if child[0].isType(scope) {
-					child = n.child[1:]
-				}
-				// Handle object assign from sparse key / values
-				if len(child) > 0 && child[0].kind == KeyValueExpr {
+				if n.lastChild().kind == KeyValueExpr {
 					n.gen = compositeSparse
-					for _, c := range child {
-						c.findex = n.typ.fieldIndex(c.child[0].ident)
-					}
+				} else {
+					n.gen = compositeLit
 				}
 			}
 
@@ -1111,10 +1104,10 @@ func (n *Node) cfgError(format string, a ...interface{}) CfgError {
 	return CfgError(fmt.Errorf("%s: "+format, a...))
 }
 
-func genRun(n *Node) error {
+func genRun(node *Node) error {
 	var err CfgError
 
-	n.Walk(func(n *Node) bool {
+	node.Walk(func(n *Node) bool {
 		if err != nil {
 			return false
 		}
diff --git a/interp/run.go b/interp/run.go
index db724f3de..28a389693 100644
--- a/interp/run.go
+++ b/interp/run.go
@@ -1017,8 +1017,9 @@ func compositeSparse(n *Node) {
 	values := make(map[int]func(*Frame) reflect.Value)
 	a, _ := n.typ.zero()
 	for _, c := range child {
-		convertLiteralValue(c.child[1], n.typ.field[c.findex].typ.TypeOf())
-		values[c.findex] = genValue(c.child[1])
+		field := n.typ.fieldIndex(c.child[0].ident)
+		convertLiteralValue(c.child[1], n.typ.field[field].typ.TypeOf())
+		values[field] = genValue(c.child[1])
 	}
 
 	n.exec = func(f *Frame) Builtin {


Test patch:
diff --git a/interp/interp_eval_test.go b/interp/interp_eval_test.go
index d05ed7360..f3e6b56d4 100644
--- a/interp/interp_eval_test.go
+++ b/interp/interp_eval_test.go
@@ -98,6 +98,26 @@ func TestEvalNil2(t *testing.T) {
 	}
 }
 
+func TestEvalComposite0(t *testing.T) {
+	i := interp.New(interp.Opt{})
+	evalCheck(t, i, `
+type T struct {
+	a, b, c, d, e, f, g, h, i, j, k, l, m, n string
+	o map[string]int
+	p []string
+}
+
+var a = T{
+	o: map[string]int{"truc": 1, "machin": 2},
+	p: []string{"hello", "world"},
+}
+`)
+	v := evalCheck(t, i, `a.p[1]`)
+	if v.Interface().(string) != "world" {
+		t.Fatalf("expected world, got %v", v)
+	}
+}
+
 func evalCheck(t *testing.T, i *interp.Interpreter, src string) reflect.Value {
 	res, err := i.Eval(src)
 	if err != nil {