A braced `defer` body routed through `parseExpr` + a mandatory trailing
`;`, so it parsed the `{ … }` as a block-EXPRESSION whose statement loop
doesn't handle a destructure decl or a `catch`-statement — `defer { v, e
:= f(); … }` and `defer { x() catch e … }` failed with "expected ';'",
and even `defer { stmt; }` needed a spurious trailing semicolon.
Now the `kw_defer` arm parses a braced body with `parseBlock` (the same
path `onfail` uses), so every statement form works; the bare-expression
form (`defer expr;`) is unchanged. `in_defer_body` is still set before
parsing, so the cleanup-body control-flow bans (return/break/continue/
try/raise) and the E1.7 failable-absorption check still fire.
Resolves the `defer` manifestation of issue 0065 (the general
value-block-in-binding-position destructure remains open). Regression:
examples/1050-errors-defer-block-body.sx.
Gates: zig build, zig build test, run_examples.sh -> 341 passed, 0 failed.
3.5 KiB
0065 — block-expression body does not parse a destructure decl (v, e := f();)
PARTIALLY RESOLVED (defer manifestation, this session). A braced
defer { … }body now parses viaparseBlock(src/parser.zig, thekw_deferarm) instead ofparseExpr, mirroringonfail. Sodefer { v, e := f(); … },defer { x() catch e … }, and plaindefer { stmt; }all parse and run. Regression:examples/1050-errors-defer-block-body.sx. Still open: the general value-producing block in binding position (y := { v, e := f(); v };) — a distinct parser path — does not parse a destructure decl; see the second reproduction below.
Symptom
A destructure declaration (v, e := f();) inside a block used in
expression position fails to parse with expected ';'. Two surfaced
forms:
defer { v, e := f(); ... }— adeferbody is parsed viaparseExpr(so its{ ... }is a block-EXPRESSION), and the block-expression statement loop doesn't recognize thename, name :=destructure form.y := { v, e := f(); v };— a value-producing block bound to a name.
Observed: error: expected ';' pointing at the statement after the
destructure (the parser bails at the := and resyncs). Expected: the
destructure parses exactly as it does in a normal statement block (an
if body, a plain { } statement block, or an onfail { } body — all of
which use parseBlock and handle it fine).
This is the same family as the pre-existing "value-producing block body
in binding position doesn't parse" note in current/CHECKPOINT-ERR.md
(E2.4b log). onfail { } is unaffected because it parses its body with
parseBlock (src/parser.zig ~2063); defer is affected because it uses
parseExpr (~2029).
Reproduction
#import "modules/std.sx";
E :: error { Bad }
val :: () -> (s32, !E) { return 5; }
f :: () -> !E {
defer {
v, e := val(); // ← error: expected ';'
print("v={}\n", v);
}
return;
}
main :: () -> s32 { return 0; }
Also reproduces with no defer, as a plain value block:
y := {
v, e := val(); // ← error: expected ';'
v
};
Investigation prompt
The block-expression statement loop (the parser path reached from
parseExpr when it hits { — see src/parser.zig, the block-as-value
parsing around the parsePrimary/parseBlockExpr path, distinct from
parseBlock at ~1931) parses each inner statement but does not run the
destructure-decl detection that parseStmt does. Find where
parseStmt/parseBlock recognizes the ident (, ident)+ := lookahead
and make the block-expression statement loop use the same statement
parser (ideally route block-expression bodies through parseStmt so
every statement form — destructure, var/const decl, etc. — is handled
uniformly).
For defer specifically: the simplest aligned fix is to parse a
braced defer body with parseBlock (like onfail does) while keeping
the bare-expression form (defer expr;) on parseExpr. That removes the
defer-body manifestation even if the general block-expression path is
handled separately.
Verification: run the repro above — expect it to compile and run
(exit 0), with the destructure-bound value usable under an if !e { … }
guard (ERR E1.8). Add a regression example under examples/ once fixed.
Status
OPEN. Orthogonal to ERR E1.7/E1.8 — the spec'd cleanup-body absorbers are
catch / or <value> (both parse fine in a defer body), so this does
not block the error-handling work. Filed while implementing E1.7.