diff --git a/_test/assign17.go b/_test/assign17.go new file mode 100644 index 000000000..ebedf00ac --- /dev/null +++ b/_test/assign17.go @@ -0,0 +1,21 @@ +package main + +func main() { + s := make([]map[string]string, 0) + m := make(map[string]string) + m["m1"] = "m1" + m["m2"] = "m2" + s = append(s, m) + tmpStr := "start" + println(tmpStr) + for _, v := range s { + tmpStr, ok := v["m1"] + println(tmpStr, ok) + } + println(tmpStr) +} + +// Output: +// start +// m1 true +// start diff --git a/_test/assign18.go b/_test/assign18.go new file mode 100644 index 000000000..f0e0f7348 --- /dev/null +++ b/_test/assign18.go @@ -0,0 +1,21 @@ +package main + +func main() { + s := make([]map[string]string, 0) + m := make(map[string]string) + m["m1"] = "m1" + m["m2"] = "m2" + s = append(s, m) + tmpStr := "start" + println(tmpStr) + for _, v := range s { + tmpStr, _ := v["m1"] + println(tmpStr) + } + println(tmpStr) +} + +// Output: +// start +// m1 +// start diff --git a/interp/cfg.go b/interp/cfg.go index bd579662e..73721ef1f 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -221,6 +221,14 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string sc.sym[label] = sym c.sym = sym } + // If block is the body of a function, get declared variables in current scope. + // This is done in order to add the func signature symbols into sc.sym, + // as we will need them in post-processing. + if n.anc != nil && n.anc.kind == funcDecl { + for k, v := range sc.anc.sym { + sc.sym[k] = v + } + } case breakStmt, continueStmt, gotoStmt: if len(n.child) == 0 { @@ -2288,11 +2296,36 @@ func compDefineX(sc *scope, n *node) error { return n.cfgErrorf("unsupported assign expression") } + // Handle redeclarations: find out new symbols vs existing ones. + symIsNew := map[string]bool{} + hasNewSymbol := false + for i := range types { + id := n.child[i].ident + if id == "_" || id == "" { + continue + } + if _, found := symIsNew[id]; found { + return n.cfgErrorf("%s repeated on left side of :=", id) + } + // A new symbol doesn't exist in current scope. Upper scopes are not + // taken into accout here, as a new symbol can shadow an existing one. + if _, found := sc.sym[id]; found { + symIsNew[id] = false + } else { + symIsNew[id] = true + hasNewSymbol = true + } + } + for i, t := range types { var index int id := n.child[i].ident - if sym, level, ok := sc.lookup(id); ok && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() { - // Reuse symbol in case of a variable redeclaration with the same type. + // A variable can be redeclared if at least one other not blank variable is created. + // The redeclared variable must be of same type (it is reassigned, not created). + // Careful to not reuse a variable which has been shadowed (it must not be a newSym). + sym, level, ok := sc.lookup(id) + canRedeclare := hasNewSymbol && len(symIsNew) > 1 && !symIsNew[id] && ok + if canRedeclare && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() { index = sym.index } else { index = sc.add(t) diff --git a/interp/interp_eval_test.go b/interp/interp_eval_test.go index 602cd133f..2b2ec58fd 100644 --- a/interp/interp_eval_test.go +++ b/interp/interp_eval_test.go @@ -133,6 +133,8 @@ func TestEvalAssign(t *testing.T) { {src: "j := true || _", err: "1:33: cannot use _ as value"}, {src: "j := true && _", err: "1:33: cannot use _ as value"}, {src: "j := interface{}(int(1)); j.(_)", err: "1:54: cannot use _ as value"}, + {src: "ff := func() (a, b, c int) {return 1, 2, 3}; x, y, x := ff()", err: "1:73: x repeated on left side of :="}, + {src: "xx := 1; xx, _ := 2, 3", err: "1:37: no new variables on left side of :="}, }) }