Support bare return - #29
Merged
Merged
Conversation
Fixes the remaining half of #23. `return` with no value was a parse error, because `parse_stmt` unconditionally parsed an operand and `Expr::Return` held a non-optional `ExprID`: do_work(done: bool) { if done { return } value = value + 1 } do_work.lyte:6: Expected expression The diagnostic pointed at the line *after* the return, and in a larger file the desynced parser cascaded into a run of bogus `Expected declaration` errors, which is what made the failure hard to read. Make the operand optional: `Expr::Return(Option<ExprID>)`. The parser treats `return` as bare when nothing is left in the statement — `Endl`, `Rbrace`, or end of input — and the checker types a bare return as void, so it unifies with the enclosing function's return type through the machinery added in the previous commit. A bare return in a value-returning function is now a clear error rather than a parse failure: ❌ f.lyte:2:5: return type must match function return type: void vs i32 return ^ Backends emit their existing void-return path for the bare case: `return_(&[])` in Cranelift, `build_return(None)` in LLVM, `ReturnVoid` in the stack VM, and a zero placeholder in r0 for the register VM, whose void epilogue ignores it. Two things fixed along the way: - `return g()` where `g` is itself void previously fed a placeholder value to a void signature in the Cranelift and stack backends. Both now emit the void return. (LLVM already special-cased this.) - In the stack backend, `Expr::Return` in void context fell through to the generic "translate then drop" path, emitting a Drop after the return terminator. Return never falls through, so there is nothing to drop. Verified on all four backends — Cranelift JIT, stack VM, register VM, and LLVM — including bare returns inside loops, inside if/else, in lambdas, and with unreachable code following. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BX9w4k8qecsH9LMgBP8hXc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the remaining half of #23.
Stacked on #28 — based on
fix-return-type-check, since a barereturnneeds the return-type constraint that PR adds in order to be checked against the enclosing function. Please merge #28 first; the base will retarget tomainautomatically.The problem
returnwith no value was a parse error.parse_stmtunconditionally parsed an operand, andExpr::Returnheld a non-optionalExprID, so there was no way to represent it:The error pointed at the line after the return, and in a larger file the desynced parser cascaded into a run of bogus
Expected declarationerrors — which is what made this hard to diagnose from the reporter's side.The fix
Expr::Return(Option<ExprID>). The parser treatsreturnas bare when nothing is left in the statement —Endl,Rbrace, or end of input — and the checker types a bare return as void, so it unifies with the enclosing function's return type through the machinery from #28.The issue asked for either support or a clearer diagnostic. This gives both: bare
returnworks as an early exit, and using one in a value-returning function is now a clear type error rather than a parse failure.Each backend emits its existing void-return path for the bare case:
return_(&[])in Cranelift,build_return(None)in LLVM,ReturnVoidin the stack VM, and a zero placeholder in r0 for the register VM (whose void epilogue ignores it).Two bugs fixed along the way
return g()wheregis itself void fed a placeholder value to a void signature in the Cranelift and stack backends. Both now emit the void return. LLVM already special-cased this.Expr::Returnin void context fell through to the generic "translate then drop" path, emitting aDropafter the return terminator. Return never falls through, so there is nothing to drop.Testing
New golden tests:
checker/bare_return(the issue's example, plus unreachable code after a bare return),checker/bare_return_in_value_fn(the error case),loops/bare_return_in_loop(returns from the function, not just the loop). Plus apretty_printunit test for the bare form.cargo test --workspacepasses: 352 lib tests and 313 golden tests across all four backend suites.I also built with
--features llvmand ran the bare-return cases through the LLVM backend by hand, since it isn't part of the default test run. All four backends — Cranelift JIT, stack VM, register VM, LLVM — produce identical output for bare returns inside loops, inside if/else, in lambdas, and with unreachable code following.No new
cargo fmtdrift orclippywarnings relative to the base (both verified by diffing against a stashed working tree; the repo has pre-existing drift in other files).🤖 Generated with Claude Code
https://claude.ai/code/session_01BX9w4k8qecsH9LMgBP8hXc