Files
sx/examples/0131-types-init-blocks.sx
agra bdd0e96d78 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).
2026-06-02 09:23:50 +03:00

201 lines
4.9 KiB
Plaintext

#import "modules/std.sx";
#import "modules/math/math.sx";
#import "modules/compiler.sx";
#import "modules/test.sx";
pkg :: #import "modules/testpkg";
Point :: struct { x, y: s32; }
add :: (a: s32, b: s32) -> s32 { a + b }
Counter :: protocol {
inc :: ();
get :: () -> s32;
}
Summable :: protocol {
sum :: () -> s32;
}
SimpleCounter :: struct { val: s32; }
impl Counter for SimpleCounter {
inc :: (self: *SimpleCounter) { self.val += 1; }
get :: (self: *SimpleCounter) -> s32 { self.val }
}
impl Summable for Point {
sum :: (self: *Point) -> s32 { self.x + self.y }
}
// Phase 2: #inline protocol for dynamic dispatch
// Phase 7: Generic struct impls
Pair :: struct ($T: Type) {
a: T;
b: T;
}
impl Summable for Pair($T) {
sum :: (self: *Pair(T)) -> s32 {
xx self.a + xx self.b
}
}
// P6.5: Struct type param constraints
// Init block test struct
Builder :: struct {
total: s32;
count: s32;
add :: (self: *Builder, val: s32) {
self.total += val;
self.count += 1;
}
}
// Global variable for address-of test
main :: () {
// --- Init Blocks (IB) ---
print("=== Init Blocks ===\n");
// IB1: basic init block with struct methods
{
b := Builder.{ total = 0, count = 0 } {
self.add(10);
self.add(20);
self.add(30);
};
print("IB1: {} {}\n", b.total, b.count);
}
// IB2: nested init blocks (self shadows correctly)
{
b1 := Builder.{ total = 0, count = 0 } {
self.add(100);
b2 := Builder.{ total = 0, count = 0 } {
self.add(42);
};
self.add(b2.total);
};
print("IB2: {} {}\n", b1.total, b1.count);
}
// IB3: empty init block
{
b := Builder.{ total = 5, count = 1 } {};
print("IB3: {} {}\n", b.total, b.count);
}
// IB4: conditional inside init block
{
add_extra := true;
b := Builder.{ total = 0, count = 0 } {
self.add(10);
if add_extra {
self.add(90);
}
};
print("IB4: {}\n", b.total);
}
// IB5: init block + auto type erasure combined
{
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get() }
result := use_counter(SimpleCounter.{ val = 0 } {
self.val = 50;
});
print("IB5: {}\n", result);
}
// ============================================================
// SECTION: Struct static method shorthand (.method(args) syntax)
// ============================================================
print("--- struct static method shorthand ---\n");
// SM1: Basic shorthand — .create(args) resolves to Dims.create(args)
{
Dims :: struct {
w: f32;
h: f32;
create :: (w: f32, h: f32) -> Dims {
Dims.{ w = w, h = h }
}
square :: (size: f32) -> Dims {
Dims.{ w = size, h = size }
}
}
use_dims :: (d: Dims) { print("SM1: {} {}\n", d.w, d.h); }
use_dims(.create(16.0, 8.0));
use_dims(.square(5.0));
}
// SM2: Shorthand in variable declaration with explicit type
{
Pair :: struct {
a: s64;
b: s64;
make :: (a: s64, b: s64) -> Pair {
Pair.{ a = a, b = b }
}
}
p : Pair = .make(10, 20);
print("SM2: {} {}\n", p.a, p.b);
}
// ============================================================
// OPTIONAL IF-ELSE COERCION
// ============================================================
{
print("--- optional if-else coercion ---\n");
OptF :: struct { width: ?f32; }
x :f32: 10.0;
// null in then branch
f1 := OptF.{ width = if true then null else x };
print("opt-if1: {}\n", f1.width ?? 99.0);
// value in then branch, null in else
f2 := OptF.{ width = if true then x else null };
print("opt-if2: {}\n", f2.width ?? 99.0);
// both branches are values
f3 := OptF.{ width = if false then 5.0 else x };
print("opt-if3: {}\n", f3.width ?? 99.0);
// standalone optional variable
val: ?f32 = if true then null else 42.0;
print("opt-if4: {}\n", val ?? 0.0);
val2: ?f32 = if false then null else 42.0;
print("opt-if5: {}\n", val2 ?? 0.0);
}
// --- usize / isize ---
{
a : usize = 42;
b : isize = 0 - 7;
print("usize: {}\n", a);
print("isize: {}\n", b);
// arithmetic
c : usize = a + 8;
print("usize+8: {}\n", c);
// coercion from s32
x : s32 = 10;
y : usize = xx x;
print("s32->usize: {}\n", y);
// coercion to s64
z : s64 = xx a;
print("usize->s64: {}\n", z);
}
}