From a91581fa16d911fe5d2e50e2486291dd97921479 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 12 Nov 2021 19:30:50 +0300 Subject: [PATCH] compiler: correctly handle defer in functions without return values Fix #2253. --- pkg/compiler/codegen.go | 12 ++++++++---- pkg/compiler/defer_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 7a5922856..a2bbc54be 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -499,6 +499,7 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types. // we should clean alt.stack manually. // This can be the case with void and named-return functions. if !isInit && !isDeploy && !lastStmtIsReturn(decl.Body) { + c.processDefers() c.saveSequencePoint(decl.Body) emit.Opcodes(c.prog.BinWriter, opcode.RET) } @@ -1329,10 +1330,13 @@ func (c *codegen) processDefers() { c.emitStoreByIndex(varLocal, finalIndex) ast.Walk(c, stmt.expr) if i == 0 { - // After panic, default values must be returns, except for named returns, - // which we don't support here for now. - for i := len(c.scope.decl.Type.Results.List) - 1; i >= 0; i-- { - c.emitDefault(c.typeOf(c.scope.decl.Type.Results.List[i].Type)) + results := c.scope.decl.Type.Results + if results.NumFields() != 0 { + // After panic, default values must be returns, except for named returns, + // which we don't support here for now. + for i := len(results.List) - 1; i >= 0; i-- { + c.emitDefault(c.typeOf(results.List[i].Type)) + } } } emit.Jmp(c.prog.BinWriter, opcode.ENDTRYL, after) diff --git a/pkg/compiler/defer_test.go b/pkg/compiler/defer_test.go index 2cabbed13..5d6784da1 100644 --- a/pkg/compiler/defer_test.go +++ b/pkg/compiler/defer_test.go @@ -3,6 +3,8 @@ package compiler_test import ( "math/big" "testing" + + "github.com/stretchr/testify/require" ) func TestDefer(t *testing.T) { @@ -79,6 +81,33 @@ func TestDefer(t *testing.T) { }` eval(t, src, big.NewInt(13)) }) + t.Run("NoReturnReturn", func(t *testing.T) { + src := `package main + var i int + func Main() { + defer func() { + i++ + }() + return + }` + vm := vmAndCompile(t, src) + err := vm.Run() + require.NoError(t, err) + require.Equal(t, 0, vm.Estack().Len(), "stack contains unexpected items") + }) + t.Run("NoReturnNoReturn", func(t *testing.T) { + src := `package main + var i int + func Main() { + defer func() { + i++ + }() + }` + vm := vmAndCompile(t, src) + err := vm.Run() + require.NoError(t, err) + require.Equal(t, 0, vm.Estack().Len(), "stack contains unexpected items") + }) } func TestRecover(t *testing.T) {