Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion codebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,9 @@ func (p *CodeBuilder) Return(n int, src ...ast.Node) *CodeBuilder {
log.Println("Return", n)
}
fn := p.current.fn
if fn.cate != AutoLambdaNormal {
p.panicCodeErrorf(getPos(src), getEnd(src), cantUseFlowsInAutoLambda)
}

// For non-inline functions, if stack has fewer elements than expected, it
// means compilation of return arguments failed. We still mark return to
Expand Down Expand Up @@ -2287,7 +2290,7 @@ func (p *CodeBuilder) Fallthrough() *CodeBuilder {
panic("please use fallthrough in case statement")
}

// For func
// For loop statement: For (..|None) Then [.. Post] .. End

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] For doc comment ordering reads reversed

The new comment // For loop statement: For (..|None) Then [.. Post] .. End groups [.. Post] as the optional element with the bare body .. after it, which reads as "optional post-section, then body" — the reverse of the actual builder order. TestFor and the stmt.go grammar block show: For → init/cond (or None) → Thenbody → optional (Post → post-stmt) → End. Suggest e.g. For (..|None) Then .. [Post ..] End.

func (p *CodeBuilder) For(src ...ast.Node) *CodeBuilder {
if debugInstr {
log.Println("For")
Expand Down
14 changes: 11 additions & 3 deletions error_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,20 @@ 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) {
codeErrorTest(t, `./foo.gop:5:2: 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))
Return(0, source("foo", 5, 2)).
End()
cb.End()
})
codeErrorTest(t, `./foo.gop:6: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).
Break(nil).
End(source("foo", 6, 1))
cb.End()
})
}
Expand Down
12 changes: 9 additions & 3 deletions func.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,24 @@ func (p *Func) End(cb *CodeBuilder, src ast.Node) {
p.inlineClosureEnd(cb)
return
}

pkg := cb.pkg
checker := termChecker{cb.current.panicCalls}
fnBody, flows := cb.endFuncBody(p.old)
if flows != 0 && p.cate != AutoLambdaNormal {
cb.handleCodeError(getSrcPos(src), getSrcEnd(src), cantUseFlowsInAutoLambda)
cate := p.cate
if cate != AutoLambdaNormal {
if flows != 0 {
cb.handleCodeError(getSrcPos(src), getSrcEnd(src), cantUseFlowsInAutoLambda)
} else {
fnBody = return0IfNeeded(fnBody)
}
}
body := &target.BlockStmt{List: fnBody}
t := p.Type().(*types.Signature)

// Check for missing return at the closing brace position.
// For FuncDecl/FuncLit/BlockStmt, End() returns Rbrace+1, so End()-1 is the Rbrace position.
if t.Results().Len() > 0 && !checker.isTerminating(body, "") {
if cate == AutoLambdaNormal && t.Results().Len() > 0 && !checker.isTerminating(body, "") {
pos, end := token.NoPos, token.NoPos
if src != nil {
end = src.End()
Expand Down
39 changes: 39 additions & 0 deletions package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3787,6 +3787,45 @@ func main() {
`)
}

func TestAutoLambda1(t *testing.T) {
pkg := newMainPackage()
results := types.NewTuple(types.NewVar(0, pkg.Types, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, nil, results, false)
pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg).
NewClosureWith(sig, gogen.AutoLambdaLoop).BodyStart(pkg).
End().Call(0).EndStmt().
End()
domTest(t, pkg, `package main

func main() {
func() int {
return 0
}()
}
`)
}

func TestAutoLambda2(t *testing.T) {
pkg := newMainPackage()
results := types.NewTuple(types.NewVar(0, pkg.Types, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, nil, results, false)
pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg).
NewClosureWith(sig, gogen.AutoLambdaLoop).BodyStart(pkg).
For().None().Then().End().
End().Call(0).EndStmt().
End()
domTest(t, pkg, `package main

func main() {
func() int {
for {
}
return 0
}()
}
`)
}

func TestClosure(t *testing.T) {
pkg := newMainPackage()
fmt := pkg.Import("fmt")
Expand Down
15 changes: 15 additions & 0 deletions util_gengo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,21 @@ func buildTypeForCallExpr(pkg *Package, T types.Type) ast.Expr {
}
}

func return0IfNeeded(stmts []ast.Stmt) []ast.Stmt {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] return0IfNeeded hardcodes return 0, invalid for non-int result types

return0IfNeeded unconditionally appends return 0 (a token.INT BasicLit "0") when the body doesn't already end in a ReturnStmt, ignoring the closure signature entirely. This only produces valid Go when the auto-lambda has a single numeric result:

  • Non-numeric result (string, bool, struct, pointer) → func() string { return 0 } is invalid.
  • Zero results → return 0 is a too many return values error.
  • Multiple results → not enough return values.

The tests (TestAutoLambda1/2) only cover a single int result, so this gap is untested. If callers guarantee auto-lambdas always have a single integer result, please document/assert that invariant; otherwise derive the zero-value expression from the signature's result type instead of hardcoding "0".

var hasReturn bool
if len(stmts) > 0 {
_, hasReturn = stmts[len(stmts)-1].(*ast.ReturnStmt)
}
if !hasReturn {
stmts = append(stmts, &ast.ReturnStmt{
Results: []ast.Expr{
&ast.BasicLit{Kind: token.INT, Value: "0"},
},
})
}
return stmts
}

// ----------------------------------------------------------------------------

func newIncDecStmt(x ast.Expr, tok token.Token) *ast.IncDecStmt {
Expand Down
4 changes: 4 additions & 0 deletions util_genjs.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ func buildTypeForCallExpr(pkg *Package, T types.Type) js.Expr {
panic("todo buildTypeForCallExpr")
}

func return0IfNeeded(stmts []js.Stmt) []js.Stmt {
panic("todo return0IfNeeded")
}

// ----------------------------------------------------------------------------

func newIncDecStmt(x js.Expr, tok token.Token) js.Stmt {
Expand Down