feat(lang): block value requires no trailing ; (Rust-style)
A block's value is now its last statement ONLY when that statement is a trailing expression with no `;`. A trailing `;` discards the value, leaving the block void. This makes value-vs-statement explicit and lets the compiler reject "this block was supposed to produce a value". Compiler: - Parser records `Block.produces_value` (last stmt is a no-`;` trailing expression) + `Block.discarded_semi` (the `;` that discarded a value), via `expectSemicolonAfter`. A trailing expression before `}` may now omit its `;` (previously a parse error). Match-arm and else-arm bodies are built value-producing regardless of the arm `;` (arms are exempt — the `;` is an arm terminator). - Lowering: `lowerBlockValue` / the block-expr path / `inferExprType` respect `produces_value`. A value-position block that discards its value is a hard error (`lowerValueBody` for function bodies; the value-context `.block` path for if/else branches, `catch` bodies, value bindings, match arms). Pure-failable `-> !` bodies (value rides the error channel) and a value-if whose branches are void are handled without false errors. - `defer`/`onfail` cleanup bodies lower as statements (void), so a trailing `;` there is fine. Migration (behavior-preserving — output unchanged): - stdlib + ~210 examples: dropped the trailing `;` on value-position last expressions. `format` now ends with an explicit `#insert "return result;"` (it relied on `#insert`-as-block-value, which `;` discards). - Two `main :: () -> s32` examples that relied on the old silent default-return got an explicit trailing `0`. - Rejection snapshots 0412 / 1013 regenerated (their quoted source lines lost a `;`); the diagnostics themselves are unchanged. Docs/tests: specs.md "Block values" section; examples 0040 (rules) + 0041 (rejection); 3 parser unit tests. Filed issue 0066 (pre-existing match-arm negated-literal phi-width quirk, surfaced not caused here). Gates: zig build, zig build test, run_examples.sh -> 343 passed, cross_compile.sh -> 7 passed (also refreshed its stale example names).
This commit is contained in:
@@ -19,12 +19,12 @@ Vec2 :: struct {
|
||||
x: f32;
|
||||
y: f32;
|
||||
|
||||
create :: (x: f32, y: f32) -> Vec2 { Vec2.{ x = x, y = y }; }
|
||||
zero :: () -> Vec2 { Vec2.{ x = 0.0, y = 0.0 }; }
|
||||
unit_x :: () -> Vec2 { Vec2.{ x = 1.0, y = 0.0 }; }
|
||||
add :: (a: Vec2, b: Vec2) -> Vec2 { Vec2.{ x = a.x + b.x, y = a.y + b.y }; }
|
||||
scale :: (v: Vec2, s: f32) -> Vec2 { Vec2.{ x = v.x * s, y = v.y * s }; }
|
||||
len :: (v: Vec2) -> s32 { xx (v.x + v.y); }
|
||||
create :: (x: f32, y: f32) -> Vec2 { Vec2.{ x = x, y = y } }
|
||||
zero :: () -> Vec2 { Vec2.{ x = 0.0, y = 0.0 } }
|
||||
unit_x :: () -> Vec2 { Vec2.{ x = 1.0, y = 0.0 } }
|
||||
add :: (a: Vec2, b: Vec2) -> Vec2 { Vec2.{ x = a.x + b.x, y = a.y + b.y } }
|
||||
scale :: (v: Vec2, s: f32) -> Vec2 { Vec2.{ x = v.x * s, y = v.y * s } }
|
||||
len :: (v: Vec2) -> s32 { xx (v.x + v.y) }
|
||||
}
|
||||
|
||||
EdgeInsets :: struct {
|
||||
@@ -33,9 +33,9 @@ EdgeInsets :: struct {
|
||||
bottom: f32;
|
||||
left: f32;
|
||||
|
||||
all :: (v: f32) -> EdgeInsets { EdgeInsets.{ top = v, right = v, bottom = v, left = v }; }
|
||||
symmetric :: (h: f32, v: f32) -> EdgeInsets { EdgeInsets.{ top = v, right = h, bottom = v, left = h }; }
|
||||
horizontal :: (h: f32) -> EdgeInsets { EdgeInsets.{ top = 0.0, right = h, bottom = 0.0, left = h }; }
|
||||
all :: (v: f32) -> EdgeInsets { EdgeInsets.{ top = v, right = v, bottom = v, left = v } }
|
||||
symmetric :: (h: f32, v: f32) -> EdgeInsets { EdgeInsets.{ top = v, right = h, bottom = v, left = h } }
|
||||
horizontal :: (h: f32) -> EdgeInsets { EdgeInsets.{ top = 0.0, right = h, bottom = 0.0, left = h } }
|
||||
}
|
||||
|
||||
Trio :: struct {
|
||||
@@ -43,8 +43,8 @@ Trio :: struct {
|
||||
b: s32;
|
||||
c: s32;
|
||||
|
||||
make :: (a: s32, b: s32, c: s32) -> Trio { Trio.{ a = a, b = b, c = c }; }
|
||||
sum :: (t: Trio) -> s32 { t.a + t.b + t.c; }
|
||||
make :: (a: s32, b: s32, c: s32) -> Trio { Trio.{ a = a, b = b, c = c } }
|
||||
sum :: (t: Trio) -> s32 { t.a + t.b + t.c }
|
||||
}
|
||||
|
||||
Result :: enum {
|
||||
@@ -99,7 +99,7 @@ main :: () {
|
||||
|
||||
// T6: Return .variant(payload) from function
|
||||
{
|
||||
make_shape :: (r: f32) -> Shape { .circle(r); }
|
||||
make_shape :: (r: f32) -> Shape { .circle(r) }
|
||||
sh := make_shape(4.2);
|
||||
print("T6: {}\n", sh.circle);
|
||||
}
|
||||
@@ -126,7 +126,7 @@ main :: () {
|
||||
case .circle: 10;
|
||||
case .rect: 20;
|
||||
case .none: 30;
|
||||
};
|
||||
}
|
||||
}
|
||||
print("T8a: {}\n", describe(.circle(7.0)));
|
||||
print("T8b: {}\n", describe(.rect(.{ 3.0, 4.0 })));
|
||||
@@ -137,8 +137,8 @@ main :: () {
|
||||
{
|
||||
r : Result = .ok(42);
|
||||
ms := if r == {
|
||||
case .ok: (v) { v; }
|
||||
case .err: (e) { -1; }
|
||||
case .ok: (v) { v }
|
||||
case .err: (e) { -1 }
|
||||
};
|
||||
print("T9: {}\n", ms);
|
||||
}
|
||||
@@ -150,7 +150,7 @@ main :: () {
|
||||
case 0: .none;
|
||||
case 1: .circle(1.0);
|
||||
else: .rect(.{ 9.0, 9.0 });
|
||||
};
|
||||
}
|
||||
}
|
||||
print("T10a: {}\n", if select(0) == { case .none: 1; else: 0; });
|
||||
print("T10b: {}\n", select(1).circle);
|
||||
@@ -176,7 +176,7 @@ main :: () {
|
||||
|
||||
// S3: Return .method(args) from function with return type
|
||||
{
|
||||
make_vec :: () -> Vec2 { .create(7.0, 8.0); }
|
||||
make_vec :: () -> Vec2 { .create(7.0, 8.0) }
|
||||
v := make_vec();
|
||||
print("S3: {} {}\n", v.x, v.y);
|
||||
}
|
||||
@@ -253,7 +253,7 @@ main :: () {
|
||||
// E3: Bare .variant (no parens) as function arg
|
||||
{
|
||||
check_none :: (sh: Shape) -> s32 {
|
||||
if sh == { case .none: 1; else: 0; };
|
||||
if sh == { case .none: 1; else: 0; }
|
||||
}
|
||||
print("E3: {}\n", check_none(.none));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user