diff --git a/examples/1050-errors-defer-block-body.sx b/examples/1050-errors-defer-block-body.sx new file mode 100644 index 0000000..f0c5716 --- /dev/null +++ b/examples/1050-errors-defer-block-body.sx @@ -0,0 +1,27 @@ +// A braced `defer { … }` body parses as a full statement block (like `onfail`), +// so it supports every statement form — a destructure decl, a `catch`-statement, +// nested var decls — not just a single bare expression. Previously `defer { … }` +// routed through the expression parser and rejected those with "expected ';'". +// +// Regression (issue 0065). + +#import "modules/std.sx"; + +E :: error { Bad } + +probe :: () -> (s32, !E) { return 21; } +failing :: () -> !E { raise error.Bad; } + +run :: () { + defer { + v, e := probe(); // destructure decl + if !e { print("defer: v={}\n", v); } // value live under the guard + failing() catch x print("defer: caught\n"); // catch-statement absorbs + } + print("body\n"); +} + +main :: () -> s32 { + run(); + return 0; +} diff --git a/examples/expected/1050-errors-defer-block-body.exit b/examples/expected/1050-errors-defer-block-body.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/examples/expected/1050-errors-defer-block-body.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/1050-errors-defer-block-body.stderr b/examples/expected/1050-errors-defer-block-body.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/expected/1050-errors-defer-block-body.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/1050-errors-defer-block-body.stdout b/examples/expected/1050-errors-defer-block-body.stdout new file mode 100644 index 0000000..0faeb53 --- /dev/null +++ b/examples/expected/1050-errors-defer-block-body.stdout @@ -0,0 +1,3 @@ +body +defer: v=21 +defer: caught diff --git a/issues/0065-block-expr-destructure-decl-parse.md b/issues/0065-block-expr-destructure-decl-parse.md index 6551582..da8fd35 100644 --- a/issues/0065-block-expr-destructure-decl-parse.md +++ b/issues/0065-block-expr-destructure-decl-parse.md @@ -1,5 +1,15 @@ # 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 diff --git a/src/parser.zig b/src/parser.zig index b261c5a..d85de7b 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -2019,15 +2019,23 @@ pub const Parser = struct { return try self.createNode(start, .{ .return_stmt = .{ .value = value } }); } - // Defer statement: defer ; + // Defer statement: defer { body } | defer ; + // A braced body parses as a full statement block (like `onfail`), so it + // supports every statement form (destructure, `catch`-statement, …); the + // bare-expression form keeps its trailing `;`. if (self.current.tag == .kw_defer) { const start = self.current.loc.start; self.advance(); const saved_defer = self.in_defer_body; self.in_defer_body = true; defer self.in_defer_body = saved_defer; - const deferred = try self.parseExpr(); - try self.expect(.semicolon); + const deferred: *Node = if (self.current.tag == .l_brace) + try self.parseBlock() + else blk: { + const e = try self.parseExpr(); + try self.expect(.semicolon); + break :blk e; + }; return try self.createNode(start, .{ .defer_stmt = .{ .expr = deferred } }); }