compiler: allow multi-return variables declaration
Problem: an attempt to compile the following code leads to a runtime panic: ``` package foo var a, b = f() func Main() int { return a + b } func f() (int, int) { return 1, 2 } ``` ``` panic: runtime error: index out of range [1] with length 1 [recovered] panic: runtime error: index out of range [1] with length 1 goroutine 22 [running]: testing.tRunner.func1.2({0xa341c0, 0xc0001647f8}) /usr/local/go/src/testing/testing.go:1209 +0x24e testing.tRunner.func1() /usr/local/go/src/testing/testing.go:1212 +0x218 panic({0xa341c0, 0xc0001647f8}) /usr/local/go/src/runtime/panic.go:1038 +0x215 github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).Visit(0xc0001623c0, {0xc75520, 0xc000155d80}) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:591 +0x6559 go/ast.Walk({0xc6c4e0, 0xc0001623c0}, {0xc75520, 0xc000155d80}) /usr/local/go/src/go/ast/walk.go:50 +0x5f github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).convertGlobals.func1({0xc75520, 0xc000155d80}) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:359 +0x70 go/ast.inspector.Visit(0xc000229740, {0xc75520, 0xc000155d80}) /usr/local/go/src/go/ast/walk.go:375 +0x31 go/ast.Walk({0xc6d920, 0xc000229740}, {0xc75520, 0xc000155d80}) /usr/local/go/src/go/ast/walk.go:50 +0x5f go/ast.walkDeclList({0xc6d920, 0xc000229740}, {0xc000155e80, 0x3, 0x120}) /usr/local/go/src/go/ast/walk.go:36 +0x87 go/ast.Walk({0xc6d920, 0xc000229740}, {0xc75458, 0xc000156c80}) /usr/local/go/src/go/ast/walk.go:355 +0x15c5 go/ast.Inspect(...) /usr/local/go/src/go/ast/walk.go:387 github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).convertGlobals(0xc0001623c0, 0xc000156c80, 0xc000254280) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:354 +0x71 github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).traverseGlobals.func2(0xc000254280) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/analysis.go:86 +0x16e github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).ForEachPackage(0xc0001623c0, 0xc000191b98) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:93 +0xc6 github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).traverseGlobals(0xc0001623c0) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/analysis.go:82 +0x22c github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).compile(0xc0001623c0, 0xc000274d20, 0x1) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:2118 +0x17c github.com/nspcc-dev/neo-go/pkg/compiler.codeGen(0xc000274d20) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:2191 +0x353 github.com/nspcc-dev/neo-go/pkg/compiler.CompileWithOptions({0xa6f39a, 0xc00023cee0}, {0xc6d240, 0xc00024a460}, 0x0) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:218 +0x65 github.com/nspcc-dev/neo-go/pkg/compiler_test.vmAndCompileInterop(0x5648df, {0xa9989f, 0x7d}) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/vm_test.go:75 +0x113 github.com/nspcc-dev/neo-go/pkg/compiler_test.eval(0xc00024a440, {0xa9989f, 0x129f366e}, {0xa68880, 0xc00024a440}) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/vm_test.go:36 +0x2d github.com/nspcc-dev/neo-go/pkg/compiler_test.TestGenDeclWithMultiRet.func2(0x4079f9) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/global_test.go:36 +0x4f testing.tRunner(0xc00022e9c0, 0xbce760) /usr/local/go/src/testing/testing.go:1259 +0x102 created by testing.(*T).Run /usr/local/go/src/testing/testing.go:1306 +0x35a ``` Solution: Allow using multi-return function calls as general variable declaration value. It was supported for assignment statements, so do the same for *ast.GenDecl if it's a variable under the hood.
This commit is contained in:
parent
f8857c5ebe
commit
07ee7f7e12
2 changed files with 38 additions and 2 deletions
|
@ -575,6 +575,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
for _, spec := range n.Specs {
|
for _, spec := range n.Specs {
|
||||||
switch t := spec.(type) {
|
switch t := spec.(type) {
|
||||||
case *ast.ValueSpec:
|
case *ast.ValueSpec:
|
||||||
|
multiRet := n.Tok == token.VAR && len(t.Values) != 0 && len(t.Names) != len(t.Values)
|
||||||
for _, id := range t.Names {
|
for _, id := range t.Names {
|
||||||
if id.Name != "_" {
|
if id.Name != "_" {
|
||||||
if c.scope == nil {
|
if c.scope == nil {
|
||||||
|
@ -583,12 +584,16 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
} else {
|
} else {
|
||||||
c.scope.newLocal(id.Name)
|
c.scope.newLocal(id.Name)
|
||||||
}
|
}
|
||||||
c.registerDebugVariable(id.Name, t.Type)
|
if !multiRet {
|
||||||
|
c.registerDebugVariable(id.Name, t.Type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := range t.Names {
|
for i := range t.Names {
|
||||||
if len(t.Values) != 0 {
|
if len(t.Values) != 0 {
|
||||||
ast.Walk(c, t.Values[i])
|
if i == 0 || !multiRet {
|
||||||
|
ast.Walk(c, t.Values[i])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
c.emitDefault(c.typeOf(t.Type))
|
c.emitDefault(c.typeOf(t.Type))
|
||||||
}
|
}
|
||||||
|
|
31
pkg/compiler/vardecl_test.go
Normal file
31
pkg/compiler/vardecl_test.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package compiler_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenDeclWithMultiRet(t *testing.T) {
|
||||||
|
t.Run("global var decl", func(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
func Main() int {
|
||||||
|
var a, b = f()
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
func f() (int, int) {
|
||||||
|
return 1, 2
|
||||||
|
}`
|
||||||
|
eval(t, src, big.NewInt(3))
|
||||||
|
})
|
||||||
|
t.Run("local var decl", func(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
var a, b = f()
|
||||||
|
func Main() int {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
func f() (int, int) {
|
||||||
|
return 1, 2
|
||||||
|
}`
|
||||||
|
eval(t, src, big.NewInt(3))
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue