Compare commits

...

10 Commits

Author SHA1 Message Date
agra
9214eefba1 chore(bench): build servers into .sx-tmp; fix stale example path
Some checks failed
Build / build-linux (push) Has been cancelled
Build / build-windows (push) Has been cancelled
The bench built sx-server/zig-server into bench/ (committed as stray
artifacts) and pointed at the pre-migration examples/32-http-server.sx.
Now builds into the gitignored .sx-tmp/ and uses the current
examples/1602-platform-http-server.sx, so a run leaves the tree clean.
2026-06-02 09:48:06 +03:00
agra
2c518f2dc4 chore: remove zig-server bench binary; don't gitignore artifacts
Drop the accidentally-tracked-in-working-tree zig-server bench binary
(built by bench/run.sh). Reverted the wasm_check* gitignore entry from
bce53b7 — these are removed, not ignored.
2026-06-02 09:43:05 +03:00
agra
bce53b720b chore: drop accidentally-committed wasm_check build artifacts
The wasm_check{,2}.{html,js,wasm} files are emscripten smoke-test output
committed by accident in f9ecf9d; nothing in the build/tests/docs
references them. Removed and gitignored the pattern so future WASM
output stays out of the tree.
2026-06-02 09:42:08 +03:00
agra
80abaf1e7d issues/0066: RESOLVED — match-value arms lowered against result type
A value-position match's arms are now lowered with `target_type` set to
the merge's `result_type`, so positive and negated integer literals pick
the same width. Fixes the `PHI node operands are not the same type as the
result` failure for `if n == { case 0: 100; else: -1; }`-style returns.

Regression: examples/0043-basic-match-value-mixed-width.sx.
Gates: zig build, zig build test, run_examples.sh -> 345 passed.
2026-06-02 09:35:41 +03:00
agra
92dba9078c issues/0065: RESOLVED — value-block destructure parses
The block-value rework routes value-position `{ … }` through the same
statement parser as every other block, so a destructure decl (and any
statement form) inside a value-bound block now parses, with the trailing
expression as the block's value. The `defer { … }` half was fixed
earlier (634cf9b). Regression: examples/0042-basic-block-value-destructure.sx.

Gates: zig build test, run_examples.sh -> 344 passed.
2026-06-02 09:31:18 +03:00
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
agra
634cf9bc7f fix(parser): parse braced defer { … } body as a statement block (issue 0065)
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.
2026-06-01 23:29:07 +03:00
agra
c3bc6acd42 ERR/E1.7: reject bare failable calls in defer/onfail cleanup bodies
A `defer`/`onfail` body runs while the block is already exiting, so a
failable call there has nowhere to propagate its error. The parser
already bans `try`/`raise`/`return`/`break`/`continue` in cleanup bodies
(f9dd965); this adds the remaining sema rule — a bare (un-absorbed)
failable call must be absorbed locally with `catch` or `or <value>`.

Implemented in the shared error-flow pass (`checkCleanupBody` /
`checkCleanupNode` / `cleanupReject` in ir/lower.zig): when the walk hits
a `defer`/`onfail`, it scans the body transitively (through blocks, `if`,
loops, match arms, `catch` handlers; stopping at nested closures) and
flags any still-failable expression. `catch` / `or value` strip the
error channel, so `exprIsFailable` is false for them — only an unhandled
failable trips the check. This completes ERR PLAN E0–E5 plus the two
deferred E1 follow-ups (E1.7 + E1.8).

New regressions: 1048 (catch/or-value absorbed forms compile + run) and
1049 (bare failable in defer and onfail rejected, exit 1).

Filed issue 0065: a braced `defer { … }` / value-block body routes
through `parseExpr` (not `parseBlock` like `onfail`), so it can't parse a
destructure or `catch`-statement inside. Orthogonal to E1.7 — the spec'd
cleanup absorbers (`catch` / `or value`) parse fine in a `defer` body.

Gates: zig build, zig build test, run_examples.sh -> 340 passed, 0 failed.
2026-06-01 23:24:15 +03:00
agra
296c809d85 ERR/E1.8: path-sensitive value-slot liveness check
A `v, err := failable()` destructure now binds the value slot(s) "live
only where `err` is proven absent". Reading `v` where the compiler cannot
prove `err == null` is a compile error.

New diagnostic-only Pass 1e (`checkErrorFlow` in ir/lower.zig): a
structured, path-sensitive walk over each main-file function body. A
proven-null set is threaded across branches and joined by intersection
at each `if`'s merge. Proof shapes recognized:

  - `if !err { … v … }`           (proven inside the guard)
  - `if err { return/raise } … v` (proven on the fall-through)
  - `if err { … } else { … v … }` (proven in the else branch)
  - `!err and <reads v>`          (short-circuit refinement)

Error-set tag compares (`if err == error.X`) prove nothing about
absence — they narrow the tag only. Nested lambdas are analyzed as their
own boundaries. Library modules are trusted (skipped).

Migrated the canon value-failable examples (1011/1012/1018/1044) to read
their value slots under `if !err` guards — output unchanged. New
regressions: 1046 (every proof shape compiles + runs, exit 210) and 1047
(unproven reads rejected, exit 1).

Gates: zig build, zig build test, run_examples.sh -> 338 passed, 0 failed.
2026-06-01 23:14:24 +03:00
agra
5491acbcbb ERR/E5.4: add composition section to the errors smoke example
Extends 1036-errors-failable-smoke with an end-to-end Composition section
covering the E5.1 forms: a failable closure literal through a Closure(...)
param (try-propagated, caught), a non-failable closure literal widened
into a failable bare slot (∅-widening adapter), and generic ($T)
value-carrying failable composition. Completes E5.4 — the per-feature
examples (1039-1045) remain the focused units; this is the integrated
smoke.
2026-06-01 22:47:58 +03:00
306 changed files with 1909 additions and 4409 deletions

View File

@@ -5,6 +5,9 @@
set -e
cd "$(dirname "$0")/.."
# Build outputs go in the gitignored scratch dir, not the tree.
mkdir -p .sx-tmp
REQUESTS=${1:-10000}
CONCURRENCY=${2:-50}
WARMUP=500
@@ -31,11 +34,11 @@ echo ""
# --- Build ---
echo -e "${BOLD}Building sx server...${RESET}"
time_sx_build=$( { time ./zig-out/bin/sx build examples/32-http-server.sx -o bench/sx-server 2>&1; } 2>&1 | tail -1 )
time_sx_build=$( { time ./zig-out/bin/sx build examples/1602-platform-http-server.sx -o .sx-tmp/sx-server 2>&1; } 2>&1 | tail -1 )
echo " $time_sx_build"
echo -e "${BOLD}Building zig server...${RESET}"
time_zig_build=$( { time zig build-exe bench/http-server.zig -O ReleaseFast -femit-bin=bench/zig-server 2>&1; } 2>&1 | tail -1 )
time_zig_build=$( { time zig build-exe bench/http-server.zig -O ReleaseFast -femit-bin=.sx-tmp/zig-server 2>&1; } 2>&1 | tail -1 )
echo " $time_zig_build"
echo ""
@@ -68,7 +71,7 @@ run_bench() {
}
# --- sx server ---
bench/sx-server > /dev/null 2>&1 &
.sx-tmp/sx-server > /dev/null 2>&1 &
SX_PID=$!
wait_for_port $SX_PORT
@@ -79,7 +82,7 @@ SX_PID=""
sleep 0.5
# --- zig server ---
bench/zig-server > /dev/null 2>&1 &
.sx-tmp/zig-server > /dev/null 2>&1 &
ZIG_PID=$!
wait_for_port $ZIG_PORT

View File

@@ -1,5 +1,5 @@
#import "modules/std.sx";
main :: () -> s32 {
if false then 40 else 42;
if false then 40 else 42
}

View File

@@ -10,6 +10,7 @@ main :: () -> s32 {
print("scoped x: {}\n", x); //expect 6
}
print("main x: {}\n", x); //expect 42
0
}
// ** stdout **

View File

@@ -1,7 +1,7 @@
std :: #import "modules/std.sx";
vec3 :: (x:f32, y:f32, z:f32) -> std.Vector(3, f32) {
.[x,y,z];
.[x,y,z]
}
main :: () {

View File

@@ -7,7 +7,7 @@ sumOf10 :: () -> s32 {
s+=i;
i+=1;
}
s;
s
}
someSum :: #run sumOf10();

View File

@@ -14,7 +14,7 @@ quick_sort :: (items: []$T) {
}
i += 1;
items[i], items[hi] = items[hi], items[i];
i;
i
}
sort :: (items: []T, lo: s64, hi: s64) {

View File

@@ -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));
}

View File

@@ -8,9 +8,9 @@
do_it :: () -> bool {
inline if OS != .ios { return false; }
true;
true
}
main :: () -> s32 {
if do_it() then 0 else 1;
if do_it() then 0 else 1
}

View File

@@ -32,5 +32,5 @@ main :: () -> s32 {
print("total: {}\n", p.total());
print("scaled: {}\n", p.scaled(3));
0;
0
}

View File

@@ -35,5 +35,5 @@ main :: () -> s32 {
// Inline unroll over a heterogeneous pack.
each(A.{ x = 1 }, B.{ s = "hi" }, A.{ x = 3 });
0;
0
}

View File

@@ -21,5 +21,5 @@ main :: () -> s32 {
case .none: { print("none\n"); }
}
}
0;
0
}

View File

@@ -5,13 +5,13 @@
Box :: struct {
v: s64;
boxed :: (self: Box) -> s64 { self.v; } // value receiver
boxed :: (self: Box) -> s64 { self.v } // value receiver
}
sum_ptr :: (xs: *List(s64)) -> s64 {
total : s64 = 0;
for xs: (n) { total = total + n; } // iterate through a *List
total;
total
}
main :: () -> s32 {
@@ -36,5 +36,5 @@ main :: () -> s32 {
bt : s64 = 0;
for bs: (*b) { bt = bt + b.boxed(); } // *Box receiver, value-self method
print("boxes {}\n", bt); // 7
0;
0
}

View File

@@ -4,18 +4,18 @@
#import "modules/test.sx";
pkg :: #import "modules/testpkg";
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 }
// P4 edge: Chained default→default calls
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)
}
}

View File

@@ -48,7 +48,7 @@ main :: () {
}
// If block as expression
ibe := 10 + if true { 5; } else { 0; };
ibe := 10 + if true { 5 } else { 0 };
print("if-block-expr: {}\n", ibe);
// While basic

View File

@@ -4,24 +4,24 @@
#import "modules/test.sx";
pkg :: #import "modules/testpkg";
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 :: () {
@@ -29,12 +29,12 @@ void_return :: () {
}
implicit_return :: (x: s32) -> s32 {
x * 2;
x * 2
}
early_return :: (x: s32) -> s32 {
if x > 10 { return 99; }
x;
x
}
main :: () {
@@ -84,7 +84,7 @@ main :: () {
print("lambda-ret: {}\n", halve(10.0));
// Local function (non-lambda)
local_add :: (a: s32, b: s32) -> s32 { a + b; }
local_add :: (a: s32, b: s32) -> s32 { a + b }
print("local-fn: {}\n", local_add(3, 4));
// Nested function calls

View File

@@ -19,11 +19,11 @@ main :: () {
// ========================================================
print("=== 19. Local Fn Return ===\n");
{
local_pt :: () -> Point { Point.{42, 99}; }
local_pt :: () -> Point { Point.{42, 99} }
lp := local_pt();
print("local-struct: {} {}\n", lp.x, lp.y);
local_sh :: () -> Shape { .circle(2.5); }
local_sh :: () -> Shape { .circle(2.5) }
ls := local_sh();
print("local-enum: {}\n", ls);
}

View File

@@ -6,7 +6,7 @@ pkg :: #import "modules/testpkg";
Point :: struct { x, y: s32; }
point_sum :: (p: Point) -> s32 { p.x + p.y; }
point_sum :: (p: Point) -> s32 { p.x + p.y }
// #run compile-time constants

View File

@@ -14,9 +14,9 @@ main :: () {
print("=== 22. If-Struct ===\n");
{
flag := true;
p := if flag { Point.{10, 20}; } else { Point.{30, 40}; };
p := if flag { Point.{10, 20} } else { Point.{30, 40} };
print("if-struct: {} {}\n", p.x, p.y);
q := if !flag { Point.{10, 20}; } else { Point.{30, 40}; };
q := if !flag { Point.{10, 20} } else { Point.{30, 40} };
print("else-struct: {} {}\n", q.x, q.y);
}
}

View File

@@ -10,7 +10,7 @@ main :: () {
{
print("=== UFCS Aliases ===\n");
num_sum :: (a: s64, b: s64) -> s64 { a + b; }
num_sum :: (a: s64, b: s64) -> s64 { a + b }
sum :: ufcs num_sum;
print("{}\n", num_sum(40, 2)); // 42 — direct call
@@ -20,7 +20,7 @@ main :: () {
print("{}\n", num_sum(40, 2)); // 42 — direct (was tuple full-splat)
print("{}\n", 40 |> sum(2)); // 42 — pipe (was tuple partial-splat)
compute :: (a: s64, b: s64, c: s64, d: s64) -> s64 { a + b * c - d; }
compute :: (a: s64, b: s64, c: s64, d: s64) -> s64 { a + b * c - d }
calc :: ufcs compute;
print("{}\n", compute(1, 2, 3, 4)); // 1+2*3-4 = 3 (was tuple full-splat)
@@ -28,14 +28,14 @@ main :: () {
print("{}\n", 1 |> calc(2, 3, 4)); // same = 3 — pipe UFCS
// Tuple return type
swap :: (a: s64, b: s64) -> (s64, s64) { (b, a); }
swap :: (a: s64, b: s64) -> (s64, s64) { (b, a) }
s := swap(1, 2);
a := s.0;
b := s.1;
print("{}\n", a); // 2
print("{}\n", b); // 1
wrap :: (x: s64) -> (s64) { (x,); }
wrap :: (x: s64) -> (s64) { (x,) }
t := wrap(99);
print("{}\n", t.0); // 99
}

View File

@@ -4,7 +4,7 @@
#import "modules/test.sx";
pkg :: #import "modules/testpkg";
add :: (a: s32, b: s32) -> s32 { a + b; }
add :: (a: s32, b: s32) -> s32 { a + b }
main :: () {

View File

@@ -30,5 +30,5 @@ main :: () -> s32 {
// dead code after a `return` at main's own block level is dropped.
return 0;
print("unreachable\n");
print("unreachable\n")
}

View File

@@ -0,0 +1,46 @@
// Block value rule: a block's value is 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 "forgot to produce a value".
//
// { … expr } → value is `expr`
// { … expr; } → void (value discarded)
//
// Match arms are exempt: the arm `;` is an arm terminator, so `case .x: expr;`
// still yields `expr` (only an explicit inner braced block follows the rule).
#import "modules/std.sx";
// Implicit return: trailing expression, no `;`.
double :: (n: s32) -> s32 { n * 2 }
// if/else as a value — each branch's last expression has no `;`.
sign :: (n: s32) -> s32 {
if n < 0 { -1 } else if n > 0 { 1 } else { 0 }
}
// A value-producing block bound to a name.
sum3 :: (a: s32, b: s32, c: s32) -> s32 {
t := { x := a + b; x + c }; // block value is `x + c`
t
}
// Match arms keep their `;` (exempt): the arm `;` is an arm terminator, so each
// arm still yields its expression as the match value.
classify :: (n: s32) -> s32 {
if n == {
case 0: 100;
case 1: 10;
else: 7;
}
}
main :: () -> s32 {
total : s32 = 0;
total = total + double(10); // 20
total = total + sign(-7); // -1
total = total + sum3(1, 2, 3); // 6
total = total + classify(1); // 10
print("block-value total: {}\n", total); // 20 - 1 + 6 + 10 = 35
total
}

View File

@@ -0,0 +1,13 @@
// Rejection counterpart to 0040: a value-position block whose last expression's
// value is discarded by a trailing `;` produces no value. A `-> T` function
// whose body ends that way is a compile error (it used to silently return a
// zero default). Drop the `;` to return the value, or use an explicit `return`.
#import "modules/std.sx";
// `n * 2;` discards the value → the function returns nothing.
double :: (n: s32) -> s32 {
n * 2;
}
main :: () -> s32 { double(5) }

View File

@@ -0,0 +1,25 @@
// A value-position block (`x := { … }`, a call argument, …) parses any
// statement form in its body — including a destructure decl — and yields its
// trailing expression as the value. Previously a braced value block routed
// through a restricted expression parser that rejected destructures with
// "expected ';'".
//
// Regression (issue 0065).
#import "modules/std.sx";
pair :: () -> (s32, s32) { (5, 7) }
main :: () -> s32 {
// destructure decl inside a value-bound block
sum := {
a, b := pair();
a + b // trailing expression → the block's value
};
print("sum: {}\n", sum); // 12
// block expression directly as a call argument
print("sq: {}\n", { x := 4; x * x }); // 16
sum
}

View File

@@ -0,0 +1,31 @@
// A value-position match (`if subject == { case … }`) returning a small
// integer type works when arms mix positive and negated literals: every arm
// value is lowered against the merge's result type, so the phi operands all
// share one width.
//
// Regression (issue 0066): a negated-literal arm (`else: -1`) previously
// lowered at a narrower width than the positive arms, tripping LLVM's
// "PHI node operands are not the same type as the result".
#import "modules/std.sx";
sign :: (n: s32) -> s32 {
if n == {
case 0: 0;
else: if n > 0 then 1 else -1;
}
}
classify :: (n: s32) -> s32 {
if n == {
case 0: 100;
case 1: 10;
else: -1;
}
}
main :: () -> s32 {
print("sign: {} {} {}\n", sign(-9), sign(0), sign(9)); // -1 0 1
print("classify: {} {} {}\n", classify(0), classify(1), classify(5)); // 100 10 -1
0
}

View File

@@ -6,11 +6,11 @@
Handle :: u32;
ok :: () -> Handle { 0; }
ok :: () -> Handle { 0 }
g : Handle = 0;
main :: () -> s32 {
g = ok();
if g == 0 then 0 else 1;
if g == 0 then 0 else 1
}

View File

@@ -30,5 +30,5 @@ main :: () -> s32 {
print("size_of(Maybe) = {}\n", size_of(Maybe));
print("size_of(Arr) = {}\n", size_of(Arr));
print("size_of(Cb) = {}\n", size_of(Cb));
0;
0
}

View File

@@ -18,5 +18,5 @@ main :: () -> s32 {
print("align chain: {}\n", align_of(MyChain));
print("size struct-alias: {}\n", size_of(WideAlias));
print("align struct-alias:{}\n", align_of(WideAlias));
0;
0
}

View File

@@ -24,5 +24,5 @@ main :: () -> s32 {
invoke_fn(@b, "hello");
if g_s.len == 0 { print("FAIL: empty\n"); return 1; }
print("got: <{}>\n", g_s);
0;
0
}

View File

@@ -8,8 +8,8 @@
Box :: struct { xs: (s32, s32); }
swap :: (a: s64, b: s64) -> (s64, s64) { (b, a); }
fst :: (t: (s64, s64)) -> s64 { t.0; }
swap :: (a: s64, b: s64) -> (s64, s64) { (b, a) }
fst :: (t: (s64, s64)) -> s64 { t.0 }
main :: () -> s32 {
// Inferred positional tuple + numeric field access.
@@ -51,5 +51,5 @@ main :: () -> s32 {
// typed correctly (s64 prints as a number, string as text).
mixed := (42, "hi");
print("mixed {} {}\n", mixed.0, mixed.1);
0;
0
}

View File

@@ -21,5 +21,5 @@ main :: () -> s32 {
print("p: x={} y={} .0={}\n", p.x, p.y, p.0);
p.0 = 33; // position write reaches the same slot as .x
print("p.x after .0=33: {}\n", p.x);
0;
0
}

View File

@@ -34,12 +34,12 @@ MyFloat :: f64;
Status :: enum u8 { ok; err; timeout; }
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 }
vec3 :: (x: f32, y: f32, z: f32) -> Vector(3, f32) {
.[x, y, z];
.[x, y, z]
}
// Global variable for address-of test

View File

@@ -87,7 +87,7 @@ main :: () {
// Destructure from function return
{
dswap :: (a: s64, b: s64) -> (s64, s64) { (b, a); }
dswap :: (a: s64, b: s64) -> (s64, s64) { (b, a) }
dx, dy := dswap(1, 2);
print("fn: {} {}\n", dx, dy);
}

View File

@@ -15,24 +15,24 @@ OptInner :: struct { val: s32; }
OptOuter :: struct { inner: ?OptInner; }
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 }
apply :: (f: (s32, s32) -> s32, x: s32, y: s32) -> s32 {
f(x, y);
f(x, y)
}
// P4 edge: Chained default→default calls
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)
}
}
@@ -258,8 +258,8 @@ main :: () {
{
check :: (v: ?s32) -> s32 {
return if v == {
case .some: (val) { val; }
case .none: { 0; }
case .some: (val) { val }
case .none: { 0 }
};
}
a: ?s32 = 55;
@@ -909,9 +909,9 @@ main :: () {
p_f5.on_resize(p_f5.title, 800, 600);
// C5.E6: protocol value passed through multiple function calls
step3 :: (a: Allocator) -> *void { a.alloc(8); }
step2 :: (a: Allocator) -> *void { step3(a); }
step1 :: (a: Allocator) -> *void { step2(a); }
step3 :: (a: Allocator) -> *void { a.alloc(8) }
step2 :: (a: Allocator) -> *void { step3(a) }
step1 :: (a: Allocator) -> *void { step2(a) }
gpa_e6 := GPA.init();
a_e6 : Allocator = xx gpa_e6;
ptr_e6 := step1(a_e6);

View File

@@ -6,7 +6,7 @@ pkg :: #import "modules/testpkg";
Point :: struct { x, y: s32; }
add :: (a: s32, b: s32) -> s32 { a + b; }
add :: (a: s32, b: s32) -> s32 { a + b }
Counter :: protocol {
inc :: ();
@@ -21,11 +21,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
@@ -38,7 +38,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
}
}
@@ -104,7 +104,7 @@ main :: () {
// IB5: init block + auto type erasure combined
{
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(SimpleCounter.{ val = 0 } {
self.val = 50;
});
@@ -123,11 +123,11 @@ main :: () {
h: f32;
create :: (w: f32, h: f32) -> Dims {
Dims.{ w = w, h = h };
Dims.{ w = w, h = h }
}
square :: (size: f32) -> Dims {
Dims.{ w = size, h = size };
Dims.{ w = size, h = size }
}
}
use_dims :: (d: Dims) { print("SM1: {} {}\n", d.w, d.h); }
@@ -142,7 +142,7 @@ main :: () {
b: s64;
make :: (a: s64, b: s64) -> Pair {
Pair.{ a = a, b = b };
Pair.{ a = a, b = b }
}
}
p : Pair = .make(10, 20);

View File

@@ -18,7 +18,7 @@ Complex :: ($T:Type) -> Type {
Vec3 :: Vec(3, f32);
vec3 :: (x:f32, y:f32, z:f32) -> Vector(3,f32) {
.[x, y, z];
.[x, y, z]
}
Foo :: Complex(u32);

View File

@@ -16,7 +16,7 @@ Size :: struct {
impl Lerpable for Size {
lerp :: (self: Size, b: Size, t: f32) -> Size {
Size.{ width = self.width + (b.width - self.width) * t,
height = self.height + (b.height - self.height) * t };
height = self.height + (b.height - self.height) * t }
}
}
@@ -32,7 +32,7 @@ Animated :: struct ($T: Lerpable) {
Animated(T).{
current = value, from = value, to = value,
elapsed = 0.0, duration = 0.0, active = false
};
}
}
set_immediate :: (self: *Animated(T), value: T) {

View File

@@ -10,9 +10,9 @@
Holder :: struct {
n: s64;
plain :: (self: *Holder) -> s64 { self.n; }
sized :: (self: *Holder, $T: Type) -> s64 { size_of(T); }
taking :: (self: *Holder, $T: Type, v: T) -> T { v; }
plain :: (self: *Holder) -> s64 { self.n }
sized :: (self: *Holder, $T: Type) -> s64 { size_of(T) }
taking :: (self: *Holder, $T: Type, v: T) -> T { v }
}
main :: () -> s32 {
@@ -24,5 +24,5 @@ main :: () -> s32 {
print("sized s64: {}\n", h.sized(s64));
print("taking explicit: {}\n", h.taking(s32, 42));
print("taking inferred: {}\n", h.taking(99));
0;
0
}

View File

@@ -33,5 +33,5 @@ main :: () -> s32 {
if g_a != 10 { print("FAIL: g_a={}\n", g_a); return 1; }
if g_b != 20 { print("FAIL: g_b={}\n", g_b); return 1; }
print("generic-into-block ok: a={} b={}\n", g_a, g_b);
0;
0
}

View File

@@ -2,7 +2,7 @@
main :: () {
fx :: (s:s3) -> s3 {
s;
s
}
print("{}\n", fx(133));

View File

@@ -1,10 +1,10 @@
#import "modules/std.sx";
add :: (a: s32, b: s32) -> s32 { a + b; }
mul :: (a: s32, b: s32) -> s32 { a * b; }
add :: (a: s32, b: s32) -> s32 { a + b }
mul :: (a: s32, b: s32) -> s32 { a * b }
apply :: (f: (s32, s32) -> s32, x: s32, y: s32) -> s32 {
f(x, y);
f(x, y)
}
main :: () {

View File

@@ -9,11 +9,11 @@ MyProtocol :: protocol {
MyImpl :: struct { value: s64; }
impl MyProtocol for MyImpl {
get_value :: (self: *MyImpl) -> s64 { self.value; }
get_value :: (self: *MyImpl) -> s64 { self.value }
}
make_thing :: () -> MyProtocol {
MyImpl.{ value = 42 };
MyImpl.{ value = 42 }
}
main :: () -> void {

View File

@@ -16,5 +16,5 @@ main :: () -> s32 {
}};
c.on(.b);
c.on(.a);
0;
0
}

View File

@@ -17,7 +17,7 @@ Tracer :: struct {
init :: () -> *Tracer {
t : *Tracer = xx libc_malloc(size_of(Tracer));
t.count = 0;
t;
t
}
}
@@ -43,5 +43,5 @@ main :: () -> s32 {
_ = add_capture(1);
}
print("Tracer.count = {}\n", tracer.count);
0;
0
}

View File

@@ -24,5 +24,5 @@ main :: () -> s32 {
// argument-position, 3 params (s64, s64, string).
print("q={}\n", apply3((a, b, c) => a + b + c.len, 1, 2, "xyz")); // 1+2+3 = 6
0;
0
}

View File

@@ -7,11 +7,11 @@ Lerpable :: protocol #inline {
}
impl Lerpable for f32 {
lerp :: (self: f32, b: f32, t: f32) -> f32 { self + (b - self) * t; }
lerp :: (self: f32, b: f32, t: f32) -> f32 { self + (b - self) * t }
}
do_lerp :: (a: Lerpable, b: f32, t: f32) -> f32 {
a.lerp(b, t);
a.lerp(b, t)
}
main :: () -> void {

View File

@@ -10,7 +10,7 @@ Sizable :: protocol {
Widget :: struct { value: s64; }
impl Sizable for Widget {
size :: (self: *Widget) -> s64 { self.value; }
size :: (self: *Widget) -> s64 { self.value }
}
// Wrapper struct with a protocol field (like ViewChild)

View File

@@ -10,7 +10,7 @@ Sizable :: protocol {
Leaf :: struct { value: s64; }
impl Sizable for Leaf {
size :: (self: *Leaf) -> s64 { self.value; }
size :: (self: *Leaf) -> s64 { self.value }
}
add :: (items: *List(Sizable), w: Leaf) {

View File

@@ -10,7 +10,7 @@ Sizable :: protocol {
Leaf :: struct { value: s64; }
impl Sizable for Leaf {
size :: (self: *Leaf) -> s64 { self.value; }
size :: (self: *Leaf) -> s64 { self.value }
}
add :: (items: *List(Sizable), w: Leaf) {

View File

@@ -13,18 +13,18 @@ Drawable :: protocol {
Circle :: struct { radius: s32; }
impl Drawable for Circle {
draw :: (self: *Circle) -> s32 { self.radius; }
name :: (self: *Circle) -> string { "circle"; }
layout :: (self: *Circle, x: s32) -> s32 { x + self.radius; }
handle :: (self: *Circle, event: s32) -> bool { event > 0; }
draw :: (self: *Circle) -> s32 { self.radius }
name :: (self: *Circle) -> string { "circle" }
layout :: (self: *Circle, x: s32) -> s32 { x + self.radius }
handle :: (self: *Circle, event: s32) -> bool { event > 0 }
}
Square :: struct { side: s32; }
impl Drawable for Square {
draw :: (self: *Square) -> s32 { self.side * self.side; }
name :: (self: *Square) -> string { "square"; }
layout :: (self: *Square, x: s32) -> s32 { x + self.side; }
handle :: (self: *Square, event: s32) -> bool { event > 1; }
draw :: (self: *Square) -> s32 { self.side * self.side }
name :: (self: *Square) -> string { "square" }
layout :: (self: *Square, x: s32) -> s32 { x + self.side }
handle :: (self: *Square, event: s32) -> bool { event > 1 }
}
Rect :: struct {
@@ -32,7 +32,7 @@ Rect :: struct {
y: f32;
w: f32;
h: f32;
zero :: () -> Rect { Rect.{ x = 0.0, y = 0.0, w = 0.0, h = 0.0 }; }
zero :: () -> Rect { Rect.{ x = 0.0, y = 0.0, w = 0.0, h = 0.0 } }
}
Container :: struct {

View File

@@ -19,7 +19,7 @@ impl Proto for Impl {
take :: (f: Fmt) -> s64 {
n : s64 = xx f;
n;
n
}
main :: () -> s32 {
@@ -28,5 +28,5 @@ main :: () -> s32 {
p : Proto = xx @Impl.{};
p.take_fmt(.b);
p.take_fmt(.a);
0;
0
}

View File

@@ -16,7 +16,7 @@ Impl :: struct {
impl Proto for Impl {
get :: (self: *Impl) -> *u8 {
@self.val;
@self.val
}
}
@@ -26,5 +26,5 @@ main :: () -> s32 {
raw : *u8 = p.get();
addr_word : u64 = xx raw;
print("got pointer: {}\n", addr_word != 0);
0;
0
}

View File

@@ -10,11 +10,11 @@ MyTag :: struct { value: s64 = 0; }
impl Into(MyTag) for s64 {
convert :: (self: s64) -> MyTag {
.{ value = self };
.{ value = self }
}
}
main :: () -> s32 {
print("ok\n");
0;
0
}

View File

@@ -12,7 +12,7 @@ GPU :: protocol {
Impl :: struct {}
impl GPU for Impl {
ping :: (self: *Impl) -> s64 { 42; }
ping :: (self: *Impl) -> s64 { 42 }
}
main :: () -> s32 {
@@ -30,5 +30,5 @@ main :: () -> s32 {
} else {
print("BAD: g still null after assign\n");
}
0;
0
}

View File

@@ -26,5 +26,5 @@ main :: () -> s32 {
print("alloc_count: {}\n", gpa.alloc_count);
0;
0
}

View File

@@ -5,6 +5,6 @@
impl Into(Wrap) for s64 {
convert :: (self: s64) -> Wrap {
.{ v = self * 10 };
.{ v = self * 10 }
}
}

View File

@@ -6,5 +6,5 @@
run_user :: () -> s32 {
w : Wrap = xx 7;
print("user: w.v = {}\n", w.v);
0;
0
}

View File

@@ -18,4 +18,4 @@
#import "./0410-protocols-impl-visibility-impl.sx";
#import "./0410-protocols-impl-visibility-user.sx";
main :: () -> s32 { run_user(); }
main :: () -> s32 { run_user() }

View File

@@ -4,6 +4,6 @@
impl Into(Wrap) for s64 {
convert :: (self: s64) -> Wrap {
.{ v = self * 10 };
.{ v = self * 10 }
}
}

View File

@@ -4,6 +4,6 @@
impl Into(Wrap) for s64 {
convert :: (self: s64) -> Wrap {
.{ v = self + 100 };
.{ v = self + 100 }
}
}

View File

@@ -19,5 +19,5 @@
main :: () -> s32 {
w : Wrap = xx 7;
print("w.v = {}\n", w.v);
0;
0
}

View File

@@ -9,11 +9,11 @@
MyA :: struct { v: s64 = 0; }
impl Into(MyA) for s64 {
convert :: (self: s64) -> MyA { .{ v = self }; }
convert :: (self: s64) -> MyA { .{ v = self } }
}
impl Into(MyA) for s64 {
convert :: (self: s64) -> MyA { .{ v = self * 2 }; }
convert :: (self: s64) -> MyA { .{ v = self * 2 } }
}
main :: () -> s32 { 0; }
main :: () -> s32 { 0 }

View File

@@ -18,5 +18,5 @@ main :: () -> s32 {
b : VL(string) = xx StrCell.{ s = "hi" };
print("b.get={}\n", b.get()); // hi (T = string)
0;
0
}

View File

@@ -29,5 +29,5 @@ make :: (..sources: VL) -> VL(s64) {
main :: () -> s32 {
r := make(IntCell.{ v = 1 });
print("{}\n", r.get()); // 99 (dispatch through the erased Combined)
0;
0
}

View File

@@ -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);
}

View File

@@ -6,7 +6,7 @@ pkg :: #import "modules/testpkg";
Point :: struct { x, y: s32; }
add :: (a: s32, b: s32) -> s32 { a + b; }
add :: (a: s32, b: s32) -> s32 { a + b }
Counter :: protocol {
inc :: ();
@@ -21,11 +21,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
@@ -42,7 +42,7 @@ 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 }
}
main :: () {
@@ -52,7 +52,7 @@ main :: () {
// AE1: function argument — concrete passed where protocol expected (no xx)
{
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get(); }
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get() }
sc := SimpleCounter.{ val = 10 };
result := use_counter(sc);
print("AE1: {}\n", result);
@@ -69,14 +69,14 @@ main :: () {
// AE3: struct literal passed directly (no xx)
{
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(SimpleCounter.{ val = 100 });
print("AE3: {}\n", result);
}
// AE4: explicit xx still works (not broken)
{
use_counter :: (c: Counter) -> s32 { c.inc(); c.get(); }
use_counter :: (c: Counter) -> s32 { c.inc(); c.get() }
result := use_counter(xx SimpleCounter.{ val = 50 });
print("AE4: {}\n", result);
}

View File

@@ -5,7 +5,7 @@ sum :: (..args: []s32) -> s32 {
for args: (it) {
result = result + it;
}
result;
result
}
print_all :: (..args: []s32) {
@@ -31,5 +31,5 @@ main :: () -> s32 {
out(" ");
}
out("\n");
0;
0
}

View File

@@ -29,7 +29,7 @@ print_any :: (..args: []Any) {
}
count :: (..args: []Any) -> s32 {
args.len;
args.len
}
main :: () -> s32 {
@@ -43,5 +43,5 @@ main :: () -> s32 {
out(int_to_string(count(1, 2, 3)));
out("\n");
0;
0
}

View File

@@ -34,7 +34,7 @@ impl Into(Block) for Closure(..$args) -> $R {
descriptor = xx @__sx_block_descriptor,
sx_env = self.env,
sx_fn = self.fn_ptr,
};
}
}
}

View File

@@ -35,5 +35,5 @@ main :: () -> s32 {
print("n0={}\n", howmany()); // empty pack
print("n2={}\n", howmany(a, b)); // two elements
print("n3={}\n", howmany(a, b, c)); // heterogeneous: IntBox, IntBox, StrBox
0;
0
}

View File

@@ -20,5 +20,5 @@ main :: () -> s32 {
a := IntBox.{ v = 1 };
n := Naked.{ x = 2 };
print("{}\n", howmany(a, n)); // `n` does not conform to Show
0;
0
}

View File

@@ -27,5 +27,5 @@ main :: () -> s32 {
print("dog+cat={}\n", pair_sum(d, c)); // 3 + 900 = 903 (heterogeneous)
print("cat+dog={}\n", pair_sum(c, d)); // 900 + 3 = 903 (order swapped)
print("dog+dog={}\n", pair_sum(d, Dog.{ age = 4 })); // 3 + 4 = 7
0;
0
}

View File

@@ -26,5 +26,5 @@ describe :: (..xs: Box) -> void {
main :: () -> s32 {
describe(IntCell.{ v = 11 }, StrCell.{ s = "hi" });
describe(StrCell.{ s = "x" }, IntCell.{ v = 99 });
0;
0
}

View File

@@ -18,5 +18,5 @@ leak :: (..xs: Box) -> s64 {
main :: () -> s32 {
print("{}\n", leak(IntCell.{ v = 5 }));
0;
0
}

View File

@@ -24,5 +24,5 @@ show :: (..xs: Box) -> void {
main :: () -> s32 {
show(IntCell.{ v = 42 }, StrCell.{ s = "hi" });
show(StrCell.{ s = "x" }, IntCell.{ v = 7 }); // order swapped → (string, s64)
0;
0
}

View File

@@ -22,5 +22,5 @@ via3 :: (..xs: Box) -> s64 { return add3(..xs.get); }
main :: () -> s32 {
print("two={}\n", via2(IntCell.{ v = 10 }, Dbl.{ n = 5 })); // 10 + 10 = 20
print("three={}\n", via3(Dbl.{ n = 1 }, IntCell.{ v = 2 }, Dbl.{ n = 3 })); // 2 + 2 + 6 = 10
0;
0
}

View File

@@ -21,5 +21,5 @@ snapshot :: (..xs: Box) -> void {
main :: () -> s32 {
snapshot(IntCell.{ v = 42 }, StrCell.{ s = "hi" });
snapshot(StrCell.{ s = "x" }, IntCell.{ v = 7 }); // order swapped → (string, s64)
0;
0
}

View File

@@ -34,5 +34,5 @@ fold :: (..xs: Box) -> s64 {
main :: () -> s32 {
snap(IntCell.{ v = 42 }, StrCell.{ s = "hi" }); // (s64, string)
print("fold={}\n", fold(IntCell.{ v = 10 }, Dbl.{ n = 5 })); // 10 + 10 = 20
0;
0
}

View File

@@ -27,5 +27,5 @@ each :: (..xs: []Show) -> void {
main :: () -> s32 {
each(A.{ x = 1 }, B.{ s = "hi" }, A.{ x = 3 }); // heterogeneous, erased to Show
each(); // empty is fine (len 0)
0;
0
}

View File

@@ -21,5 +21,5 @@ main :: () -> s32 {
call(A.{});
_ = ret(A.{});
iter(A.{});
0;
0
}

View File

@@ -28,5 +28,5 @@ fwd_show :: (..xs: Show) -> s64 { return show_all(xx xs); }
main :: () -> s32 {
print("any={}\n", fwd_any(1, "hi", 2.5)); // 3
print("show={}\n", fwd_show(A.{}, B.{ s = "x" }, A.{})); // A B A, 3
0;
0
}

View File

@@ -21,5 +21,5 @@ main :: () -> s32 {
b : Box(bool, string, bool) = ---; // Ts=[string, bool], pair: (string, bool)
b.pair = ("x", true);
print("b: 0={} 1={}\n", b.pair.0, b.pair.1);
0;
0
}

View File

@@ -25,5 +25,5 @@ main :: () -> s32 {
c.sources = (xx IntCell.{ v = 10 }, xx StrCell.{ s = "hi" });
c.value = 99;
print("{} {} {}\n", c.sources.0.get(), c.sources.1.get(), c.value); // 10 hi 99
0;
0
}

View File

@@ -23,5 +23,5 @@ make :: (..sources: VL) -> s64 {
main :: () -> s32 {
print("{}\n", make(IntCell.{ v = 7 })); // 7
0;
0
}

View File

@@ -24,5 +24,5 @@ build :: (..sources: VL) -> void {
main :: () -> s32 {
build(IntCell.{ v = 10 }, StrCell.{ s = "hi" }); // 10 hi
0;
0
}

View File

@@ -14,5 +14,5 @@ apply :: (mapper: Closure(s64, s64) -> s64, ..sources: VL) -> s64 {
main :: () -> s32 {
print("{}\n", apply((a, b) => a + b, IntCell.{ v = 40 }, IntCell.{ v = 2 })); // 42
0;
0
}

View File

@@ -32,5 +32,5 @@ map :: (mapper: Closure(..sources.T) -> $R, ..sources: VL) -> VL($R) {
main :: () -> s32 {
r := map((a, b) => a + b, IntCell.{ v = 40 }, IntCell.{ v = 2 });
print("{}\n", r.get()); // 42
0;
0
}

View File

@@ -2,7 +2,7 @@
#import "modules/math";
test :: () -> s32 {
0;
0
}
Vec4 :: Vector(4,f32);

View File

@@ -15,12 +15,12 @@ R :: struct { x: s32; }
bug :: (cmd: [:0]u8) -> ?R {
f := popen(cmd, "r");
if cast(s64) f == 0 { return null; }
R.{ x = 1 };
R.{ x = 1 }
}
post_link :: () -> bool {
if r := bug("echo hi") { puts("ok"); } else { puts("null"); }
true;
true
}
configure :: () {

View File

@@ -17,7 +17,7 @@ cb :: () -> bool {
puts("2-arg ok");
c := format("{} {} {}", "x", "y", "z");
puts("3-arg ok");
true;
true
}
configure :: () {

View File

@@ -35,7 +35,7 @@ sum_bytes :: (buf: [*]u8) -> s64 {
s : s64 = 0;
i : s64 = 0;
while i < BUF_SIZE { s += xx buf[i]; i += 1; }
s;
s
}
run_u8 :: () -> s64 {
@@ -45,7 +45,7 @@ run_u8 :: () -> s64 {
p.* = 0x42;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_u16 :: () -> s64 {
@@ -55,7 +55,7 @@ run_u16 :: () -> s64 {
p.* = 0x0102;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_u32 :: () -> s64 {
@@ -65,7 +65,7 @@ run_u32 :: () -> s64 {
p.* = 0x01020304;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_u64 :: () -> s64 {
@@ -75,7 +75,7 @@ run_u64 :: () -> s64 {
p.* = 0x0102030405060708;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_s8 :: () -> s64 {
@@ -85,7 +85,7 @@ run_s8 :: () -> s64 {
p.* = 0x42;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_s16 :: () -> s64 {
@@ -95,7 +95,7 @@ run_s16 :: () -> s64 {
p.* = 0x0102;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_s32 :: () -> s64 {
@@ -105,7 +105,7 @@ run_s32 :: () -> s64 {
p.* = 0x01020304;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_s64 :: () -> s64 {
@@ -115,7 +115,7 @@ run_s64 :: () -> s64 {
p.* = 0x0102030405060708;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_bool :: () -> s64 {
@@ -125,7 +125,7 @@ run_bool :: () -> s64 {
p.* = true;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_f32 :: () -> s64 {
@@ -135,7 +135,7 @@ run_f32 :: () -> s64 {
p.* = 1.0;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
run_f64 :: () -> s64 {
@@ -145,7 +145,7 @@ run_f64 :: () -> s64 {
p.* = 1.0;
s := sum_bytes(buf);
free(xx buf);
s;
s
}
// ── comptime-baked expected checksums ───────────────────────────────
@@ -169,7 +169,7 @@ EXP_F64 :: #run run_f64();
check :: (label: string, got: s64, want: s64) -> bool {
if got == want { return true; }
print("FAIL {}: comptime={} runtime={}\n", label, want, got);
false;
false
}
main :: () -> s32 {

View File

@@ -4,9 +4,9 @@
#import "modules/test.sx";
pkg :: #import "modules/testpkg";
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 }
// #run compile-time constants
CT_VAL :: #run add(10, 15);

View File

@@ -13,4 +13,5 @@ main :: () -> s32 {
v:= std.Vector(3,f32).[1,2,3];
std.print("\n{}\n", v);
0
}

View File

@@ -10,5 +10,5 @@ main :: () -> s32 {
b := multiply(5, 6);
print("add_numbers(10, 20) = {}\n", a);
print("multiply(5, 6) = {}\n", b);
0;
0
}

View File

@@ -6,5 +6,5 @@ main :: () -> s32 {
b := tc.multiply(5, 6);
print("tc.add_numbers(10, 20) = {}\n", a);
print("tc.multiply(5, 6) = {}\n", b);
0;
0
}

View File

@@ -7,6 +7,6 @@ Wrap :: struct { v: s64 = 0; }
impl Into(Wrap) for s64 {
convert :: (self: s64) -> Wrap {
.{ v = self * 10 };
.{ v = self * 10 }
}
}

View File

@@ -9,5 +9,5 @@
main :: () -> s32 {
w : Wrap = xx 3;
print("w.v = {}\n", w.v);
0;
0
}

View File

@@ -14,5 +14,5 @@ main :: () -> s32 {
print("b_only_fn: {}\n", b_only_fn());
print("c_only_fn direct: {}\n", c_only_fn());
print("c_only_const direct: {}\n", c_only_const);
0;
0
}

View File

@@ -1,5 +1,5 @@
#import "c.sx";
b_only_fn :: () -> s32 {
c_only_fn() + c_only_const;
c_only_fn() + c_only_const
}

View File

@@ -1,2 +1,2 @@
c_only_fn :: () -> s32 { 42; }
c_only_fn :: () -> s32 { 42 }
c_only_const :: 7;

Some files were not shown because too many files have changed in this diff Show More