diff --git a/codebuild.go b/codebuild.go index 4fe7d37..ede89f8 100644 --- a/codebuild.go +++ b/codebuild.go @@ -287,13 +287,12 @@ func insertParams(scope *types.Scope, params *types.Tuple) { } } -func (p *CodeBuilder) endFuncBody(old funcBodyCtx) []target.Stmt { +func (p *CodeBuilder) endFuncBody(old funcBodyCtx) ([]target.Stmt, int) { p.current.checkLabels(p) p.current.fn = old.fn p.current.labels = old.labels p.current.panicCalls = old.panicCalls - stmts, _ := p.endBlockStmt(&old.codeBlockCtx) - return stmts + return p.endBlockStmt(&old.codeBlockCtx) } func (p *CodeBuilder) startBlockStmt(current codeBlock, src []ast.Node, comment string, old *codeBlockCtx) *CodeBuilder { @@ -587,7 +586,8 @@ func (p *Func) inlineClosureEnd(cb *CodeBuilder) { cb.Label(ending) } sig := p.Type().(*types.Signature) - cb.emitStmt(&target.BlockStmt{List: cb.endFuncBody(p.old)}) + fnBody, _ := cb.endFuncBody(p.old) + cb.emitStmt(&target.BlockStmt{List: fnBody}) cb.stk.PopN(p.getInlineCallArity()) results := sig.Results() for i, n := 0, results.Len(); i < n; i++ { // return results & clean env @@ -642,15 +642,28 @@ func (p *CodeBuilder) emitVar(pkg *Package, closure *Func, param *types.Var, wit p.paramInsts[key] = p.current.scope.Lookup(name).(*types.Var) } -// NewClosure func +// NewClosure creates a new closure. func (p *CodeBuilder) NewClosure(params, results *types.Tuple, variadic bool) *Func { sig := types.NewSignatureType(nil, nil, nil, params, results, variadic) - return p.NewClosureWith(sig) + return p.NewClosureWith(sig, AutoLambdaNormal) } -// NewClosureWith func -func (p *CodeBuilder) NewClosureWith(sig *types.Signature) *Func { - return p.pkg.newClosure(sig) +// AutoLambdaCategory represents the category of an auto-lambda. +type AutoLambdaCategory int + +const ( + AutoLambdaNormal AutoLambdaCategory = iota + AutoLambdaCond + AutoLambdaLoop +) + +// NewClosureWith creates a new closure with an optional auto-lambda category. +func (p *CodeBuilder) NewClosureWith(sig *types.Signature, cate ...AutoLambdaCategory) *Func { + var c AutoLambdaCategory + if cate != nil { + c = cate[0] + } + return p.pkg.newClosure(sig, c) } // ConvertToClosure converts an expression into a closure. diff --git a/error_msg_test.go b/error_msg_test.go index e8189f0..fc3c26c 100644 --- a/error_msg_test.go +++ b/error_msg_test.go @@ -133,6 +133,17 @@ func TestErrSwitch(t *testing.T) { }) } +func TestErrAutoLambda(t *testing.T) { + codeErrorTest(t, `./foo.gop:5:1: can't use return/continue/break/goto in auto lambda`, func(pkg *gogen.Package) { + cb := pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg) + sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) + cb.NewClosureWith(sig, gogen.AutoLambdaLoop).BodyStart(pkg). + Return(0). + End(source("foo", 5, 1)) + cb.End() + }) +} + func TestErrTypeRedefined(t *testing.T) { codeErrorTest(t, "./foo.gop:2:5: foo redeclared in this block\n\tprevious declaration at ./foo.gop:1:5", func(pkg *gogen.Package) { typ := pkg.NewType("foo", source("foo", 1, 5)) diff --git a/func.go b/func.go index 16c2db9..0e6548a 100644 --- a/func.go +++ b/func.go @@ -41,6 +41,7 @@ func (p *Package) NewParam(pos token.Pos, name string, typ types.Type, optional type Func struct { *types.Func decl *funcDecl + cate AutoLambdaCategory old funcBodyCtx arity1 int // 0 for normal, (arity+1) for inlineClosure } @@ -89,6 +90,10 @@ func (p *Func) BodyStart(pkg *Package, src ...ast.Node) *CodeBuilder { return pkg.cb.startFuncBody(p, src, &p.old) } +const ( + cantUseFlowsInAutoLambda = "can't use return/continue/break/goto in auto lambda" +) + // End is for internal use. func (p *Func) End(cb *CodeBuilder, src ast.Node) { if p.isInline() { @@ -97,7 +102,11 @@ func (p *Func) End(cb *CodeBuilder, src ast.Node) { } pkg := cb.pkg checker := termChecker{cb.current.panicCalls} - body := &target.BlockStmt{List: cb.endFuncBody(p.old)} + fnBody, flows := cb.endFuncBody(p.old) + if flows != 0 && p.cate != AutoLambdaNormal { + cb.handleCodeError(getSrcPos(src), getSrcEnd(src), cantUseFlowsInAutoLambda) + } + body := &target.BlockStmt{List: fnBody} t := p.Type().(*types.Signature) // Check for missing return at the closing brace position. @@ -252,9 +261,9 @@ func (p *Package) NewFuncWith( return fn, nil } -func (p *Package) newClosure(sig *types.Signature) *Func { +func (p *Package) newClosure(sig *types.Signature, cate AutoLambdaCategory) *Func { fn := types.NewFunc(token.NoPos, p.Types, "", sig) - return &Func{Func: fn} + return &Func{Func: fn, cate: cate} } func (p *Package) newInlineClosure(sig *types.Signature, arity int) *Func { diff --git a/util_gengo.go b/util_gengo.go index f45a794..00fb48f 100644 --- a/util_gengo.go +++ b/util_gengo.go @@ -1179,7 +1179,7 @@ func emitForRangeStmt(cb *CodeBuilder, p *forRangeStmt, stmts []ast.Stmt, flows }) */ if flows != 0 { - cb.panicCodeError(p.stmt.For, p.stmt.For, cantUseFlows) + cb.panicCodeError(p.stmt.For, p.stmt.For, cantUseFlowsInForRange) } n = -n def := p.stmt.Tok == token.DEFINE @@ -1214,7 +1214,7 @@ func emitForRangeStmt(cb *CodeBuilder, p *forRangeStmt, stmts []ast.Stmt, flows } const ( - cantUseFlows = "can't use return/continue/break/goto in for range of udt.XGo_Enum(callback)" + cantUseFlowsInForRange = "can't use return/continue/break/goto in for range of udt.XGo_Enum(callback)" ) var (