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)
2.6 KiB
0187 — lambda with INFERRED return type + block body with early returns 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
#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 returns (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:
- The repro prints
f: 11. examples/optionals/0919/0921andexamples/closures/0312still pass (0312 deliberately uses explicit-> i64to dodge this bug — once fixed, an inferred-return variant should also work).- 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.