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:
@@ -49,24 +49,24 @@ WindowFlags :: enum flags u32 { vsync :: 64; resizable :: 4; hidden :: 128; }
|
||||
|
||||
// --- Top-level functions ---
|
||||
|
||||
add :: (a: s32, b: s32) -> s32 { a + b; }
|
||||
add :: (a: s32, b: s32) -> s32 { a + b }
|
||||
|
||||
mul :: (a: s32, b: s32) -> s32 { a * b; }
|
||||
mul :: (a: s32, b: s32) -> s32 { a * b }
|
||||
|
||||
identity :: (x: $T) -> T { x; }
|
||||
identity :: (x: $T) -> T { x }
|
||||
|
||||
pair_add :: (a: $T, b: $U) -> s64 {
|
||||
cast(s64) a + cast(s64) b;
|
||||
cast(s64) a + cast(s64) b
|
||||
}
|
||||
|
||||
typed_sum :: (..args: []s32) -> s32 {
|
||||
result := 0;
|
||||
for args: (it) { result = result + it; }
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
apply :: (f: (s32, s32) -> s32, x: s32, y: s32) -> s32 {
|
||||
f(x, y);
|
||||
f(x, y)
|
||||
}
|
||||
|
||||
void_return :: () {
|
||||
@@ -74,19 +74,19 @@ void_return :: () {
|
||||
}
|
||||
|
||||
implicit_return :: (x: s32) -> s32 {
|
||||
x * 2;
|
||||
x * 2
|
||||
}
|
||||
|
||||
early_return :: (x: s32) -> s32 {
|
||||
if x > 10 { return 99; }
|
||||
x;
|
||||
x
|
||||
}
|
||||
|
||||
vec3 :: (x: f32, y: f32, z: f32) -> Vector(3, f32) {
|
||||
.[x, y, z];
|
||||
.[x, y, z]
|
||||
}
|
||||
|
||||
point_sum :: (p: Point) -> s32 { p.x + p.y; }
|
||||
point_sum :: (p: Point) -> s32 { p.x + p.y }
|
||||
|
||||
// #run compile-time constants
|
||||
|
||||
@@ -215,11 +215,11 @@ SimpleCounter :: struct { val: s32; }
|
||||
|
||||
impl Counter for SimpleCounter {
|
||||
inc :: (self: *SimpleCounter) { self.val += 1; }
|
||||
get :: (self: *SimpleCounter) -> s32 { self.val; }
|
||||
get :: (self: *SimpleCounter) -> s32 { self.val }
|
||||
}
|
||||
|
||||
impl Summable for Point {
|
||||
sum :: (self: *Point) -> s32 { self.x + self.y; }
|
||||
sum :: (self: *Point) -> s32 { self.x + self.y }
|
||||
}
|
||||
|
||||
// Phase 2: #inline protocol for dynamic dispatch
|
||||
@@ -236,14 +236,14 @@ Accumulator :: struct {
|
||||
|
||||
impl Adder for Accumulator {
|
||||
add :: (self: *Accumulator, n: s32) { self.total += n; }
|
||||
value :: (self: *Accumulator) -> s32 { self.total; }
|
||||
value :: (self: *Accumulator) -> s32 { self.total }
|
||||
}
|
||||
|
||||
Doubler :: struct { val: s32; }
|
||||
|
||||
impl Adder for Doubler {
|
||||
add :: (self: *Doubler, n: s32) { self.val = self.val + n + n; }
|
||||
value :: (self: *Doubler) -> s32 { self.val; }
|
||||
value :: (self: *Doubler) -> s32 { self.val }
|
||||
}
|
||||
|
||||
// Phase 4: default methods
|
||||
@@ -272,10 +272,10 @@ impl Repeater for Printer {
|
||||
Chained :: protocol {
|
||||
base :: (msg: string) -> s32;
|
||||
wrap :: (msg: string) -> s32 {
|
||||
self.base(msg) + 1;
|
||||
self.base(msg) + 1
|
||||
}
|
||||
double_wrap :: (msg: string) -> s32 {
|
||||
self.wrap(msg) + self.wrap(msg);
|
||||
self.wrap(msg) + self.wrap(msg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ ChainImpl :: struct { val: s32; }
|
||||
impl Chained for ChainImpl {
|
||||
base :: (self: *ChainImpl, msg: string) -> s32 {
|
||||
self.val += 1;
|
||||
msg.len;
|
||||
msg.len
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ Eq :: protocol {
|
||||
|
||||
impl Eq for Point {
|
||||
eq :: (self: *Point, other: Point) -> bool {
|
||||
self.x == other.x and self.y == other.y;
|
||||
self.x == other.x and self.y == other.y
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,13 +306,13 @@ Cloneable :: protocol {
|
||||
|
||||
impl Cloneable for Point {
|
||||
clone :: (self: *Point) -> Point {
|
||||
Point.{ x = self.x, y = self.y };
|
||||
Point.{ x = self.x, y = self.y }
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for s64 {
|
||||
eq :: (self: *s64, other: s64) -> bool {
|
||||
self.* == other;
|
||||
self.* == other
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ impl Eq for s64 {
|
||||
|
||||
// Phase 6: Generic constraints
|
||||
are_equal :: ($T: Type/Eq, a: T, b: T) -> bool {
|
||||
a.eq(b);
|
||||
a.eq(b)
|
||||
}
|
||||
|
||||
Hashable :: protocol {
|
||||
@@ -329,20 +329,20 @@ Hashable :: protocol {
|
||||
|
||||
impl Hashable for Point {
|
||||
hash :: (self: *Point) -> s64 {
|
||||
xx self.x * 31 + xx self.y;
|
||||
xx self.x * 31 + xx self.y
|
||||
}
|
||||
}
|
||||
|
||||
eq_and_hash :: ($T: Type/Eq/Hashable, a: T, b: T) -> bool {
|
||||
if a.hash() != b.hash() { return false; }
|
||||
a.eq(b);
|
||||
a.eq(b)
|
||||
}
|
||||
|
||||
// P6.4: inline constraint syntax ($T/Protocol)
|
||||
|
||||
// P6.4: inline constraint syntax ($T/Protocol)
|
||||
sum_of_inline :: (a: $T/Summable, b: T) -> s32 {
|
||||
a.sum() + b.sum();
|
||||
a.sum() + b.sum()
|
||||
}
|
||||
|
||||
// Phase 7: Generic struct impls
|
||||
@@ -355,7 +355,7 @@ Pair :: struct ($T: Type) {
|
||||
|
||||
impl Summable for Pair($T) {
|
||||
sum :: (self: *Pair(T)) -> s32 {
|
||||
xx self.a + xx self.b;
|
||||
xx self.a + xx self.b
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ main :: () {
|
||||
{
|
||||
use_adder :: (a: Adder, n: s32) -> s32 {
|
||||
a.add(n);
|
||||
a.value();
|
||||
a.value()
|
||||
}
|
||||
acc := Accumulator.{ total = 100 };
|
||||
result := use_adder(xx @acc, 50);
|
||||
@@ -470,7 +470,7 @@ main :: () {
|
||||
use_counter :: (c: Counter) -> s32 {
|
||||
c.inc();
|
||||
c.inc();
|
||||
c.get();
|
||||
c.get()
|
||||
}
|
||||
sc := SimpleCounter.{ val = 10 };
|
||||
result := use_counter(xx @sc);
|
||||
@@ -603,14 +603,14 @@ main :: () {
|
||||
|
||||
// P2.7: xx on inline struct literal (no intermediate variable)
|
||||
{
|
||||
use_adder :: (a: Adder) -> s32 { a.add(10); a.value(); }
|
||||
use_adder :: (a: Adder) -> s32 { a.add(10); a.value() }
|
||||
result := use_adder(xx Accumulator.{ total = 5 });
|
||||
print("P2.7: {}\n", result);
|
||||
}
|
||||
|
||||
// P3.3: xx on inline struct literal with vtable protocol
|
||||
{
|
||||
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get(); }
|
||||
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get() }
|
||||
result := use_counter(xx SimpleCounter.{ val = 100 });
|
||||
print("P3.3: {}\n", result);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user