From 92acc71c80c5eecf9211c56d3e25d97ea8f26c9b Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 11 Jan 2023 15:49:10 +0300 Subject: [PATCH 1/2] compiler: create new locals for range loops when needed, fix #2855 --- pkg/compiler/codegen.go | 12 ++++++++++-- pkg/compiler/for_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index b9faf3c5d..fb5c75ef6 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1313,7 +1313,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { } else { emit.Opcodes(c.prog.BinWriter, opcode.DUP) } - c.emitStoreVar("", n.Key.(*ast.Ident).Name) + keyIdent := n.Key.(*ast.Ident) + if n.Tok == token.DEFINE { + c.scope.newLocal(keyIdent.Name) + } + c.emitStoreVar("", keyIdent.Name) } if needValue { if !isMap || !keyLoaded { @@ -1327,7 +1331,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { opcode.SWAP, // key should be on top opcode.PICKITEM) } - c.emitStoreVar("", n.Value.(*ast.Ident).Name) + valIdent := n.Value.(*ast.Ident) + if n.Tok == token.DEFINE { + c.scope.newLocal(valIdent.Name) + } + c.emitStoreVar("", valIdent.Name) } ast.Walk(c, n.Body) diff --git a/pkg/compiler/for_test.go b/pkg/compiler/for_test.go index a07cad913..c7bd36cc1 100644 --- a/pkg/compiler/for_test.go +++ b/pkg/compiler/for_test.go @@ -714,6 +714,32 @@ var forLoopTestCases = []testCase{ `, big.NewInt(6), }, + { + "shadow range key", + `func F%d() int { + i := 10 + ints := []int{1, 2, 3, 4, 5} + for i := range ints { + _ = i + } + return i + } + `, + big.NewInt(10), + }, + { + "shadow range value", + `func F%d() int { + i := 10 + ints := []int{1, 2, 3, 4, 5} + for _, i := range ints { + _ = i + } + return i + } + `, + big.NewInt(10), + }, } func TestForLoop(t *testing.T) { From 8305389852d93b097956b8445dec350112f62374 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 11 Jan 2023 16:21:40 +0300 Subject: [PATCH 2/2] compiler: don't panic, return error for strange range loops See #2870. --- pkg/compiler/codegen.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index fb5c75ef6..50edd84c9 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1301,25 +1301,45 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { emit.Opcodes(c.prog.BinWriter, opcode.OVER, opcode.OVER) emit.Jmp(c.prog.BinWriter, opcode.JMPLEL, end) - var keyLoaded bool - needValue := n.Value != nil && n.Value.(*ast.Ident).Name != "_" - if n.Key != nil && n.Key.(*ast.Ident).Name != "_" { + var ( + haveKey bool + haveVal bool + keyIdent *ast.Ident + keyLoaded bool + valIdent *ast.Ident + ) + if n.Key != nil { + keyIdent, haveKey = n.Key.(*ast.Ident) + if !haveKey { + c.prog.Err = errors.New("only simple identifiers can be used for range loop keys (see #2870)") + return nil + } + haveKey = (keyIdent.Name != "_") + } + if n.Value != nil { + valIdent, haveVal = n.Value.(*ast.Ident) + if !haveVal { + c.prog.Err = errors.New("only simple identifiers can be used for range loop values (see #2870)") + return nil + } + haveVal = (valIdent.Name != "_") + } + if haveKey { if isMap { c.rangeLoadKey() - if needValue { + if haveVal { emit.Opcodes(c.prog.BinWriter, opcode.DUP) keyLoaded = true } } else { emit.Opcodes(c.prog.BinWriter, opcode.DUP) } - keyIdent := n.Key.(*ast.Ident) if n.Tok == token.DEFINE { c.scope.newLocal(keyIdent.Name) } c.emitStoreVar("", keyIdent.Name) } - if needValue { + if haveVal { if !isMap || !keyLoaded { c.rangeLoadKey() } @@ -1331,7 +1351,6 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { opcode.SWAP, // key should be on top opcode.PICKITEM) } - valIdent := n.Value.(*ast.Ident) if n.Tok == token.DEFINE { c.scope.newLocal(valIdent.Name) }