-
Notifications
You must be signed in to change notification settings - Fork 35
cb.NewClosureWith support optional auto-lambda category #642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 ( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exported constants have no doc comments. These are part of a public API consumed by the XGo compiler and will render on pkg.go.dev as a bare const block. Nearby code in this repo does document constants inline (e.g. Worth stating for each value what it means and which restriction it implies — especially since |
||
| 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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verified panic: use
for c := AutoLambdaNormal
if len(cate) > 0 {
c = cate[0]
} |
||
| c = cate[0] | ||
| } | ||
| return p.pkg.newClosure(sig, c) | ||
| } | ||
|
|
||
| // ConvertToClosure converts an expression into a closure. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
cb.NewClosureWith(sig, gogen.AutoLambdaLoop).BodyStart(pkg)
l := cb.NewLabel(token.NoPos, token.NoPos, "G")
cb.Goto(l)
cb.Label(l).None().EndStmt()
cb.End(nil)
// => can't use return/continue/break/goto in auto lambdaLabels are per-
For the record, I did check the labeled- |
||
| cb.handleCodeError(getSrcPos(src), getSrcEnd(src), cantUseFlowsInAutoLambda) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After this error, generation continues and emits uncompilable Go.
package main
func main() {
func() {
break
}
}which does not compile. The structurally identical for-range check uses Accumulating is arguably the better choice — it lets the compiler collect multiple diagnostics — but then the offending body should be neutralized (drop the flow statements, or emit an empty body) so gogen never emits invalid source. Otherwise use |
||
| } | ||
| 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 { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discarding
flowshere may be a real gap, not just a mechanical_.Unlike a real closure, an inline closure's body is spliced into the enclosing block, so control flow inside it belongs to the enclosing block's flow set.
ReturnErr(outer: true)emits a genuinereturnand setsflowFlagReturnon this context (codebuild.go:433) — dropped here. If such an inline closure sits inside an auto lambda or afor range udt.XGo_Enum(callback), the escapingreturnwon't trip either check.Note you can't blindly propagate everything:
Returninside an inline closure lowers toGoto(endingLabel)with the label emitted in the same inlined block (codebuild.go:480), so thatflowFlagGotois genuinely self-contained. Something likecb.current.flows |= flows &^ flowFlagGoto— or local-label tracking as above — is probably right.If the discard is deliberate, a short comment instead of a bare
_would help; as written it reads like fallout from the signature change.