fix: lambda inferred return type from a block body's early returns (issue 0187)
A `:=`-bound closure with no explicit `-> T` and a BLOCK body inferred its
return type via inferExprType(lam.body), which yields the last statement's
type. A block whose value comes only from early `return`s ends in a return
statement (void/noreturn), so the closure was built with a void return while
the body returned i64 — the call site then fed `i64 undef` and LLVM
verification failed. (A block whose tail referenced a block-local hit the
sibling failure: inferExprType returned .unresolved → an LLVM panic.)
Infer the return type exactly as a named fn does (resolveReturnType in
lower.zig): an arrow body `(params) => expr` uses the expression type; a block
body `(params) { stmts }` takes the first explicit `return <val>` type via
findReturnValueType, else void (the block tail is a discarded statement unless
an explicit `-> R` makes it the value). Regression test:
examples/closures/0313-closure-inferred-return-early.sx.
This commit is contained in:
@@ -1,3 +1,30 @@
|
||||
> **RESOLVED.** Root cause: the lambda return-type inference in `lowerLambda`
|
||||
> (`src/ir/lower/closure.zig`) always used `inferExprType(lam.body)`, which for a
|
||||
> block body returns the *last statement's* value type — and a block whose value
|
||||
> comes only from early `return`s ends in a `return` statement (typed
|
||||
> void/noreturn), so the closure was built with a void return while the body
|
||||
> returned `i64`. Fix: distinguish the two body forms exactly as a named fn does
|
||||
> (`resolveReturnType` in `src/ir/lower.zig`) —
|
||||
> - **arrow** `(params) => expr` → `inferExprType(expr)` (the expression IS the value);
|
||||
> - **block** `(params) { stmts }` → first explicit `return <val>` type via
|
||||
> `findReturnValueType`, else **void** (the block's tail is a discarded
|
||||
> statement, not an implicit return — only an explicit `-> R` makes the tail
|
||||
> the value).
|
||||
>
|
||||
> This also subsumes the block-tail-references-a-local case (a `closure(() { x
|
||||
> := 5; x * 2 })` with no `-> R` is now correctly **void**, not an `.unresolved`
|
||||
> type reaching LLVM and panicking). Regression test:
|
||||
> `examples/closures/0313-closure-inferred-return-early.sx`.
|
||||
>
|
||||
> Syntax note: the original repro above used the malformed `() => { ... }`
|
||||
> (arrow + block) form, which the parser currently accepts but the spec does
|
||||
> not define — a block body is the `closure((params) -> R? { ... })` form, and
|
||||
> the `=>` lambda takes an EXPRESSION (specs.md §Lambda / §Closures). The bug is
|
||||
> real under the valid form too: `closure(() { if c { return 11; } return 22; })`
|
||||
> with an inferred return failed identically before the fix. The regression test
|
||||
> uses the valid `closure(...)` syntax. (The parser accepting `() => { block }`
|
||||
> at all is a separate leniency gap, not tracked here.)
|
||||
|
||||
# 0187 — lambda with INFERRED return type + block body with early `return`s mis-infers its return type (LLVM verifier failure)
|
||||
|
||||
## Symptom
|
||||
|
||||
Reference in New Issue
Block a user