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.
92 lines
3.5 KiB
Markdown
92 lines
3.5 KiB
Markdown
# 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 via `parseBlock` (src/parser.zig, the
|
|
> `kw_defer` arm) instead of `parseExpr`, mirroring `onfail`. So
|
|
> `defer { v, e := f(); … }`, `defer { x() catch e … }`, and plain
|
|
> `defer { 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(); ... }` — a `defer` body is parsed via `parseExpr`
|
|
(so its `{ ... }` is a block-EXPRESSION), and the block-expression
|
|
statement loop doesn't recognize the `name, 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
|
|
|
|
```sx
|
|
#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:
|
|
|
|
```sx
|
|
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.
|