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:
@@ -161,8 +161,8 @@ pub fn lowerLambda(self: *Lowering, lam: *const ast.Lambda) Ref {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Arrow lambda without explicit return type — infer from body expression
|
||||
// Temporarily bind params in scope so inferExprType can resolve param types
|
||||
// Lambda without explicit return type — infer from the body.
|
||||
// Temporarily bind params in scope so inference can resolve param types.
|
||||
var temp_scope = Scope.init(self.alloc, self.scope);
|
||||
const saved = self.scope;
|
||||
self.scope = &temp_scope;
|
||||
@@ -170,7 +170,23 @@ pub fn lowerLambda(self: *Lowering, lam: *const ast.Lambda) Ref {
|
||||
const pty = params.items[user_param_base + i].ty;
|
||||
temp_scope.put(p.name, .{ .ref = @enumFromInt(0), .ty = pty, .is_alloca = false });
|
||||
}
|
||||
const inferred = self.inferExprType(lam.body);
|
||||
// Two body forms (parser.zig parseLambda), inferred exactly as a named
|
||||
// fn (resolveReturnType in lower.zig):
|
||||
// (params) => expr — arrow: the body expression IS the value.
|
||||
// (params) { stmts } — block: an explicit `return <val>` sets the
|
||||
// type; with none the return is VOID (the
|
||||
// block's tail is a discarded statement, not an
|
||||
// implicit return — only an explicit `-> R`,
|
||||
// handled above, makes the tail the value).
|
||||
// The old code always used inferExprType(lam.body); for a block body
|
||||
// that mis-inferred — void/noreturn when the value came only from early
|
||||
// `return`s (issue 0187), or `.unresolved` when the tail referenced a
|
||||
// block-local the temp scope never bound (a variant the same fix
|
||||
// subsumes) → LLVM panic.
|
||||
const inferred = if (lam.body.data == .block)
|
||||
self.findReturnValueType(lam.body) orelse .void
|
||||
else
|
||||
self.inferExprType(lam.body);
|
||||
self.scope = saved;
|
||||
temp_scope.deinit();
|
||||
break :blk inferred;
|
||||
|
||||
Reference in New Issue
Block a user