Files
sx/issues/0187-lambda-inferred-return-block-early-return.md
agra 820cd62fa1 docs: file issues 0185-0188
File four issue write-ups discovered alongside the 0179 work:
- 0185: binary-op operand auto-unwrap silently miscompiles a NULL ?T
- 0186: closure VALUE call does not coerce arg to ?T parameter
- 0187: lambda with inferred return type + block body with early returns
  mis-infers its return type
- 0188: closure-VALUE calls skip argument validation (arity + tuple spread)
2026-06-25 13:57:56 +03:00

56 lines
2.6 KiB
Markdown

# 0187 — lambda with INFERRED return type + block body with early `return`s mis-infers its return type (LLVM verifier failure)
## Symptom
A `:=`-bound lambda (closure literal) that has NO explicit `-> T` return type
and whose body is a BLOCK containing `return` statements infers the WRONG
return type (apparently `void`). Calling it and using the value fails LLVM
verification (`Call parameter type does not match function signature! ... i64
undef` / `Function arguments must have first-class types!`). Adding an explicit
`-> T` makes it work. No optionals or flow narrowing are involved — found while
verifying issue 0186.
## Reproduction
```sx
#import "modules/std.sx";
main :: () {
// inferred return type, block body with early returns — NO optionals
f := () => { if 1 > 0 { return 11; } return 22; };
print("f: {}\n", f()); // LLVM verification failed (return type inferred void/undef)
}
```
Workaround / contrast (works): annotate the return type —
`f := () -> i64 => { if 1 > 0 { return 11; } return 22; };`
## Root cause (hypothesis)
The lambda return-type inference in `lowerLambda` (`src/ir/lower/closure.zig`,
the `ret_ty` computation around line 164: `const inferred =
self.inferExprType(lam.body);`) does not infer the type from the body's
`return` statements when the body is a block. For a block whose value is
produced only via early `return`s (not a trailing tail expression),
`inferExprType` likely yields `.void`, so the lambda is built with a void
return while the body actually returns `i64` — the mismatch surfaces at the
call site / LLVM verifier.
## Investigation prompt
In `src/ir/lower/closure.zig`, the lambda return-type inference path
(`inferExprType(lam.body)` ~line 164) must, for a block body, infer the return
type from the body's `return` statement operands (matching how
`lowerValueBody` / the function-decl return inference handles bodies with early
returns), not just the block's tail-expression value. Reuse the existing
return-type inference the top-level fn path uses (a top-level
`f :: () { if c { return 11; } return 22; }` with inferred return works — see
why, and apply the same to lambdas). Verify:
1. The repro prints `f: 11`.
2. `examples/optionals/0919`/`0921` and `examples/closures/0312` still pass
(0312 deliberately uses explicit `-> i64` to dodge this bug — once fixed, an
inferred-return variant should also work).
3. Add a regression `examples/closures/03xx-lambda-inferred-return-early.sx`.
Unrelated to the optional-unwrap family (0179/0185) and the closure-arg
coercion fix (0186); purely lambda return-type inference.