lang: rename signed integer types sN -> iN

Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.

Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).

Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.

zig build test: 426/426; examples suite: 595/595.
This commit is contained in:
agra
2026-06-12 09:31:53 +03:00
parent 515ecebea7
commit d8076b9333
1054 changed files with 6836 additions and 6839 deletions

View File

@@ -73,9 +73,9 @@ There is no "wrap up first" option.
a lookup fails in the compiler. Examples of the pattern to root out:
```zig
// NEVER write this — lookup fails, return s64 and pretend nothing
// NEVER write this — lookup fails, return i64 and pretend nothing
// happened. Any caller asking `what type is this?` gets a lie.
return self.module.types.findByName(name_id) orelse .s64;
return self.module.types.findByName(name_id) orelse .i64;
// NEVER write this — same shape, dressed up:
return scope.lookup(name) orelse default_type;
@@ -86,9 +86,9 @@ These defaults silently produce wrong results in cases the implementer
didn't think of. The classic failure mode: the default coincidentally
matches the size/shape of one common case, so the test suite passes
and the bug ships invisibly. issue-0042 lived for years because
`resolveTypeArg`'s `orelse .s64` returned 8 bytes for unresolved
`resolveTypeArg`'s `orelse .i64` returned 8 bytes for unresolved
type-alias names — coincidentally correct for any 8-byte target
(`s64`, `*T`, `f64`, function pointers), and silently wrong for
(`i64`, `*T`, `f64`, function pointers), and silently wrong for
everything else.
**Required:** when a lookup that *must* succeed fails, emit a
@@ -102,7 +102,7 @@ as a silently-corrupted size or alignment.
`void` is a real, heavily-checked type (void returns, void params, "no
value" markers), and pervasive `if (ty == .void) { skip / return-nothing }`
checks would silently swallow the failure — trading one silent default
(`.s64`) for another (`.void`) one layer down. The same objection rules
(`.i64`) for another (`.void`) one layer down. The same objection rules
out `noreturn` (diverging expressions) and any other load-bearing builtin.
Instead, add a **distinct** `.unresolved`-style `TypeId` whose sole meaning
is "resolution failed". A dedicated value (1) can't be mistaken for a real
@@ -311,7 +311,7 @@ LongLived :: struct {
}
// Direct allocs too:
grow_buf :: (self: *LongLived, n: s64) {
grow_buf :: (self: *LongLived, n: i64) {
self.buf = self.own_allocator.alloc(n);
}
}

View File

@@ -30,7 +30,7 @@ build_block_convert($args, $R)`.
| issue-0050 fix | `5316bf7` | Same isolation pattern as 0048 applied to `monomorphizeFunction`. |
| 5.1.A xfail | `3bd6f26` | `build_block_convert(args: []Type, $ret: Type) -> string` undefined — pin output format via `examples/176-build-block-convert.sx` across 3 void shapes + 1 non-void shape. |
| 5.1.B fix | `aeb950b` | Builder added to `library/modules/std/objc_block.sx`. Emits nested `callconv(.c)` trampoline + Block literal source. |
| 5.2.A xfail | `f5342e9` | Generic `Into(Block)` impl absent — `Closure(s64, s64) -> void` (uncovered by hand-rolled impls) emits the "no Into(Block) for cl_s64_s64__void" diagnostic per `examples/177-generic-into-block.sx`. |
| 5.2.A xfail | `f5342e9` | Generic `Into(Block)` impl absent — `Closure(i64, i64) -> void` (uncovered by hand-rolled impls) emits the "no Into(Block) for cl_i64_i64__void" diagnostic per `examples/177-generic-into-block.sx`. |
| 5.2.B fix | `165b621` | Generic impl `Closure(..$args) -> $R` added with `#insert build_block_convert($args, $R)`. `lowerExpr`'s `.comptime_pack_ref` + `resolveTypeArg` + `type_bridge.isTypeShapedAstNode` extended so impl-mono `$args` (pack_bindings) and `$R` (type_bindings) resolve in both expr and type positions. |
| 5.3 | `2eaf932` | Delete hand-rolled `__block_invoke_void` + `__block_invoke_bool` + the two per-shape impls. The generic impl covers both at runtime. |
@@ -40,11 +40,11 @@ What's now possible end-to-end (from
```sx
#import "modules/std/objc_block.sx";
main :: () -> s32 {
cl := (a: s64, b: s64) => { g_a = a; g_b = b; };
main :: () -> i32 {
cl := (a: i64, b: i64) => { g_a = a; g_b = b; };
blk : Block = xx cl; // generic impl mono'd for
// Closure(s64, s64) -> void
invoke_fn : (*Block, s64, s64) -> void callconv(.c) = xx blk.invoke;
// Closure(i64, i64) -> void
invoke_fn : (*Block, i64, i64) -> void callconv(.c) = xx blk.invoke;
invoke_fn(@blk, 10, 20);
0;
}
@@ -53,9 +53,9 @@ main :: () -> s32 {
The `xx cl : Block` site monomorphises the generic
`Into(Block) for Closure(..$args) -> $R` impl. Inside the impl
mono, `#insert build_block_convert($args, $R)` evaluates the
builder at comptime with `$args = [s64, s64]` and `$R = void`,
builder at comptime with `$args = [i64, i64]` and `$R = void`,
and substitutes the resulting source — a nested
`__invoke :: (block_self: *Block, arg0: s64, arg1: s64) -> void
`__invoke :: (block_self: *Block, arg0: i64, arg1: i64) -> void
callconv(.c) { ... }` trampoline plus the Block literal that
points its `invoke` slot at `@__invoke`. Stack-local block layout
matches Apple's published spec; UIKit / Foundation consumers can
@@ -96,7 +96,7 @@ generic Into(Block) builder body rests on.
|---|---|---|
| 4A.bare.1.A | `c792642` | Expected-failing lock-in for bare `$args` (parser rejection diff). |
| 4A.bare.1.B | `5a4a19b` | Parser makes `[` optional after `$<pack_name>`; new `ComptimePackRef` AST node + sema no-op arms + `lowerExpr` arm calling new `buildPackSliceValue(arg_types)` helper. Helper emits `alloca [N x Any]`, one `const_type(arg_tys[i])` per slot, then a `{data_ptr, len}` slice aggregate. emit_llvm's `const_type` arm relaxed to silent undef-i64 (storage of Type values in runtime aggregates is harmless; loud bail moves to USE sites). |
| 4A.bare.4.A | `95e61d8` | Expected-failing lock-in for `type_name(list[i])` silently returning "s64" via `resolveTypeArg`'s catch-all `else => .s64`. |
| 4A.bare.4.A | `95e61d8` | Expected-failing lock-in for `type_name(list[i])` silently returning "i64" via `resolveTypeArg`'s catch-all `else => .i64`. |
| 4A.bare.4.B | `d99c0fd` | `tryLowerReflectionCall` splits on new `isStaticTypeArg(node)` helper. Static args fold to const_string (today's fast path); dynamic args emit `callBuiltin(.type_name, [arg_ref])` for the interp's arm. emit_llvm's reflection-builtin arm relaxed to silent undef-i64 — same reasoning as const_type: storage-position misuse is impossible, use-site misuse caught by the interp arm's `asTypeId orelse bailDetail`. |
| 4A.bare.5 | `2162662` | End-to-end smoke `examples/172-pack-builder-smoke.sx`. `describe(..$args)` walks `$args` at #run time, calls `type_name(list[i])` per position. Four call shapes (empty, one-arg, two-arg, four-mixed) verify the full chain works. |
@@ -106,7 +106,7 @@ What now works end-to-end (from `examples/172-pack-builder-smoke.sx`):
describe :: (..$args) -> string {
list := $args;
s := "[";
i : s64 = 0;
i : i64 = 0;
while i < list.len {
if i > 0 { s = concat(s, ", "); }
s = concat(s, type_name(list[i]));
@@ -117,7 +117,7 @@ describe :: (..$args) -> string {
}
#run { print("{}\n", describe(true, 3.14, "x", 99)); }
// → [bool, f64, string, s64]
// → [bool, f64, string, i64]
```
The pack flows through a real `[]Type` slice value; the loop
@@ -130,7 +130,7 @@ Known follow-ups (not blocking step 5):
- `type_eq` / `has_impl` dynamic-arg dispatch — should follow
the same `isStaticTypeArg` split that `type_name` got in
4A.bare.4.B. Today their dynamic-arg case still silently
folds via the same `resolveTypeArg .s64` fall-through.
folds via the same `resolveTypeArg .i64` fall-through.
Wire when a real use case needs them.
- `has_impl` interp arm — still bails "not yet wired".
Needs a protocol-map snapshot on `Interpreter.init`.
@@ -160,7 +160,7 @@ helpers, source-language `$args[$i]` in expression position.
| 4.0 foundation | `ac60d98` | New `Op.const_type: TypeId` opcode (dedicated, never piggybacks on `const_int`). Interp emits `Value.type_tag(tid)`. emit_llvm bails loudly (Type is comptime-only; LLVM never sees one). `Value.asTypeId() ?TypeId` helper. `evalCmp` arm for `.type_tag, .type_tag` — TypeId equality. Mixed `.type_tag` vs `.int` falls through to `typeErrorDetail`. Zig unit tests confirm the variant. |
| 4.1 reflection arms | `9600ba5` | `BuiltinId.type_name` / `.type_eq` / `.has_impl` for the interp-time fallback when lowering can't fold the call statically. Static-arg calls keep the existing `tryLowerReflectionCall` const-emission fast path. `has_impl` interp arm bails with "not yet wired" — interp-time has_impl needs a queryable snapshot of the host's protocol maps (its own follow-up). emit_llvm bails loudly on all three (comptime-only). |
| 4.2 audit + bitcast guard | `55c72af` | `box_any`/`unbox_any` audit: layout was already correct (tag stays `.int`; value field can be `.type_tag`). `bitcast` interp arm guards against `.type_tag → <non-Any, non-identity>` casts — catches the `xx val to string` shape in `any_to_string`'s `case type:` arm that pre-dates type_tag and would silently mis-coerce. |
| 4.3 source construction | `fd03b58` | Parser accepts `$<pack>[<int_literal>]` in expression position (yields the same `pack_index_type_expr` AST node already used in type positions in step 3). Lowering: `lowerExpr` arm emits `const_type(arg_tys[index])`; `resolveTypeArg` arm reads `pack_arg_types[name][index]` directly so lower-time fold paths (`tryLowerReflectionCall`, `tryConstBoolCondition`) see the bound TypeId rather than falling through to the `.s64` silent-arm default. |
| 4.3 source construction | `fd03b58` | Parser accepts `$<pack>[<int_literal>]` in expression position (yields the same `pack_index_type_expr` AST node already used in type positions in step 3). Lowering: `lowerExpr` arm emits `const_type(arg_tys[index])`; `resolveTypeArg` arm reads `pack_arg_types[name][index]` directly so lower-time fold paths (`tryLowerReflectionCall`, `tryConstBoolCondition`) see the bound TypeId rather than falling through to the `.i64` silent-arm default. |
Audit summary — every Value-switch in interp.zig was checked
for silent fall-through. Findings:
@@ -180,11 +180,11 @@ What's now possible end-to-end (from `examples/169-pack-value-dispatch.sx`):
```sx
show :: (..$args) -> string => type_name($args[0]);
show(42) // "s64"
show(42) // "i64"
show("hi") // "string"
describe :: (..$args) -> string {
inline if type_eq($args[0], s64) { return "got s64"; }
inline if type_eq($args[0], i64) { return "got i64"; }
inline if type_eq($args[0], string) { return "got string"; }
...
}
@@ -231,29 +231,29 @@ What works:
- `x : $args[1] = args[1]` — local-var annotation.
- `fp : (*void, $args[0]) -> $args[1] = handler;` — fn-pointer
type literal (the shape step 5's generic trampoline body needs).
- `inline if type_eq($args[0], s64) { ... }` (when the `$args[0]`
- `inline if type_eq($args[0], i64) { ... }` (when the `$args[0]`
argument is in a type position — `type_eq` reads call args via
`resolveTypeArg` which routes to `resolveTypeWithBindings`).
- `has_impl(Hash, s64)` (plain protocols).
- `has_impl(Into(Block), s64)` (parameterised protocols).
- `has_impl(Hash, i64)` (plain protocols).
- `has_impl(Into(Block), i64)` (parameterised protocols).
New tests:
- `examples/165-pack-type-position.sx` — return type + local
var annotation; two heterogeneous call shapes (s64+string,
string+s64) confirm distinct monos.
var annotation; two heterogeneous call shapes (i64+string,
string+i64) confirm distinct monos.
- `examples/166-pack-type-position-three.sx` — `args[2]` (third
element) as return type across three (s64,s64,string),
(bool,f64,s64), (string,string,bool) shapes.
element) as return type across three (i64,i64,string),
(bool,f64,i64), (string,string,bool) shapes.
- `examples/167-pack-type-fnptr.sx` — fn-pointer type literal
with `$args[$i]` in both param + return positions.
- `examples/168-pack-reflection-intrinsics.sx` — type_name,
type_eq (with inline-if folding), has_impl for both plain
(Allocator/CAllocator) and parameterised (custom Wrap(s64)
for s32) protocols.
(Allocator/CAllocator) and parameterised (custom Wrap(i64)
for i32) protocols.
Out of scope (deferred):
- `$args[$i]` in EXPRESSION position (the parser only accepts
it in type positions today — `type_eq($args[0], s64)` works
it in type positions today — `type_eq($args[0], i64)` works
because the call-arg path resolves through `resolveTypeArg`,
but bare `$args[0]` as a value would need an extra parser arm).
- `$args[$i]` in struct field types.
@@ -280,7 +280,7 @@ $args[1], ...) -> $R` per-position types.
Closes the nested-comptime-call + return bug. The pack-fn face
was incidentally fixed by step 2b's mono refactor (pack-fn
calls now bypass the inline-return-slot setup that leaked into
nested comptime). The plain `($x: s32)` comptime face stayed
nested comptime). The plain `($x: i32)` comptime face stayed
on the inline path until this fix.
`createComptimeFunction` wraps a comptime expression into a
@@ -305,7 +305,7 @@ only `func` / `current_block` / `inst_counter` / `scope` /
fn-local flags the wrapper's lowering needs fresh.
`examples/issue-0046.sx` (regression test): `helper :: ($x:
s32) -> s64 { print("inside\n"); return 42; }` called from
i32) -> i64 { print("inside\n"); return 42; }` called from
`main`. Pre-fix: interp panic at storeAtRawPtr. Post-fix:
prints "inside" then "n=42".
@@ -339,7 +339,7 @@ New tests:
`log_count(items: []Any)`.
- `examples/163-pack-runtime-index.sx` — `while i < args.len
{ args[i] }` over a 4-arg pack.
- `examples/164-pack-mixed-comptime.sx` — `tagged($tag: s32,
- `examples/164-pack-mixed-comptime.sx` — `tagged($tag: i32,
..$args)` called with different `tag` values gets distinct
monos (`tagged__ct_7__pack_*`, `tagged__ct_9__pack`).
@@ -364,7 +364,7 @@ caller's basic block.
`examples/158-pack-mono-dedup.sx` confirms end-to-end:
`count(), count(1), count(2), count(1,2,3), count("x", true)`
produces `0 1 1 3 2` at runtime AND emits exactly 4 monos in
IR — the two `s64` calls share one mono.
IR — the two `i64` calls share one mono.
Plumbing in `src/ir/lower.zig`:
- `isPackFn(fd)` — true when the only comptime param is a
@@ -492,7 +492,7 @@ Out of scope:
"unresolved 'result'" because nested comptime inlining
loses the scope where stdlib's `#insert build_format`
declared `result`. Same class as the
`helper :: ($x: s32) -> s64 { print(...); return 42; }`
`helper :: ($x: i32) -> i64 { print(...); return 42; }`
pattern; pre-dates step 2. Worth filing if step 2's later
slices need it; today's typed-indexing test exercises only
field access and arithmetic, no nested print.
@@ -516,7 +516,7 @@ Root cause was broader than packs: `format`/`print` use arrow
form (`=> expr`) or `#insert`-only bodies, so no stdlib comptime
fn took the `return`-with-trailing-statements path. Step 1.b
made `..$args` parseable; the natural smoke test
`foo :: (..$args) -> s64 { return 42; }` was the first body to
`foo :: (..$args) -> i64 { return 42; }` was the first body to
hit it.
Fix in `src/ir/lower.zig`:
@@ -575,10 +575,10 @@ New plumbing in [src/ir/lower.zig](../src/ir/lower.zig):
concrete source closure during monomorphisation.
`examples/155-pack-impl-match.sx` flips from the
"no Into(Block) for cl_s32_bool__bool" lock-in diagnostic to
"no Into(Block) for cl_i32_bool__bool" lock-in diagnostic to
"pack impl match ok": one user-declared
`impl Into(Block) for Closure(..$args) -> $R` covers a
`Closure(s32, bool) -> bool` source that stdlib has no
`Closure(i32, bool) -> bool` source that stdlib has no
hand-rolled impl for. The constructed Block isn't invoked
(invoke=null) — the test exercises matching + monomorphisation,
not the trampoline (step 5 of the plan).
@@ -607,9 +607,9 @@ Replaces a hand-rolled Into impl in stdlib once step 2 + step
Pinned today's matching behaviour ahead of 1d.B. A user-declared
`impl Into(Block) for Closure(..$args) -> $R` registers under a
pack-shaped source key in `param_impl_map`; the xx site mangles
the concrete `Closure(s32, bool) -> bool` source and finds
the concrete `Closure(i32, bool) -> bool` source and finds
nothing → the existing focused diagnostic fires ("no `Into(Block)
for cl_s32_bool__bool` impl — add a per-signature
for cl_i32_bool__bool` impl — add a per-signature
`__block_invoke_<sig>` trampoline + Into impl..."). The pack impl
is reachable in the file but never considered.
@@ -691,7 +691,7 @@ current parser-rejection behavior so the next commit's parser
extension shows up as a behavior shift.
New: `examples/150-pack-parse.sx` declares
`foo :: (..$args) -> s64`. Today's parser hits `..` where it
`foo :: (..$args) -> i64`. Today's parser hits `..` where it
expects a parameter name (after parsing the leading `dollar`
sigil that doesn't appear) and emits "expected parameter name"
at column 9 of line 15. Expected output captures this rejection.
@@ -742,7 +742,7 @@ lookup. Infrastructure only; populated but not yet read.
Added stand-ins for the opaque Obj-C runtime types to
`library/modules/std/objc.sx`: `id`, `Class`, `SEL` resolve to
`*void`; `BOOL` to `s8`. All zero-cost at the LLVM layer; the
`*void`; `BOOL` to `i8`. All zero-cost at the LLVM layer; the
header's old caveat about lacking aliases is gone.
`141-objc-type-aliases.sx` exercises them against the real macOS
Obj-C runtime via `isKindOfClass`.
@@ -793,13 +793,13 @@ Four design questions still open (see roadmap).
A trailing variadic param on a `#foreign` declaration now maps to the
C calling convention's `...` instead of sx's slice-packing path. Drops
the existing per-arity shim pattern (`__log_2i :: (prio, tag, fmt, a:
s32, b: s32) -> s32 #foreign __android_log_print;`) for a single
i32, b: i32) -> i32 #foreign __android_log_print;`) for a single
declarative form:
```sx
sx_ffi_sum_ints :: (n: s32, args: ..s32) -> s64 #foreign;
sx_ffi_sum_ints :: (n: i32, args: ..i32) -> i64 #foreign;
main :: () -> s32 {
main :: () -> i32 {
print("{}\n", sx_ffi_sum_ints(3, 10, 20, 30)); // → 60
}
```
@@ -821,15 +821,15 @@ locks in the green state in one commit):
getting boxed into a typed slice.
3. **C default argument promotion**. New `promoteCVariadicArgs`
([src/ir/lower.zig](src/ir/lower.zig)) applies the standard
promotions to args past the fixed param count: `bool/s8/s16/u8/u16
s32` via sext/zext, `f32 → f64` via fpext. Wired into the two
promotions to args past the fixed param count: `bool/i8/i16/u8/u16
i32` via sext/zext, `f32 → f64` via fpext. Wired into the two
`lowerCall` paths right after `coerceCallArgs`.
`examples/ffi-foreign-cvariadic.sx` + `.c` lock the matrix end-to-end:
`sum_ints(3, 10, 20, 30) → 60`, `sum_ints(0) → 0`, `avg_doubles(2,
1.5, 2.5) → 2.0`, `avg_doubles(3, 1.0, 2.0, 3.0) → 2.0`, and a
null-terminated `count_args` chain of `*u8` strings → `3`. All four
return shapes (s64 / f64 / s32) and three element types (s32 / f64 /
return shapes (i64 / f64 / i32) and three element types (i32 / f64 /
*u8) exercise the variadic-slot ABI through the C `va_arg` machinery
in the .c helper.
@@ -887,7 +887,7 @@ plus 2 codegen fixes surfaced along the way.**
|------|-------------------------------|---------------------------------------------------------------------------------------|
| 0.0 | tests/cross_compile.sh | empty tuple list, exits 0; skip-with-warning when toolchains missing |
| 0.1 | ffi-01-primitives.sx | every primitive type round-trips through `#import c { #source / #include }` |
| 0.2 | ffi-02-small-struct.sx | Vec2 (8 B), Vec4f (16 B HFA), Pair64 (2×s64), Quad32 (4×s32) — four ABI slots |
| 0.2 | ffi-02-small-struct.sx | Vec2 (8 B), Vec4f (16 B HFA), Pair64 (2×i64), Quad32 (4×i32) — four ABI slots |
| 0.3 | ffi-03-large-struct.sx | Big24 (24 B), Big48 (48 B) via byval params + sret return |
| 0.4 | ffi-04-fp-struct.sx | FQuad (16 B HFA), DQuad (32 B HFA — UIEdgeInsets-shape) |
| 0.5 | ffi-05-string-args.sx | [:0]u8, sx `string` slice-decay, [*]u8 + len, mutate-via-C, C-returned pointer |
@@ -1110,8 +1110,8 @@ describes is implemented in [src/ir/lower.zig](../src/ir/lower.zig)
Apple's runtime DSL encoding table:
- `v` = void, `i` = s32, `q` = s64, `f` = f32, `d` = f64, `B` = bool,
- `c` = s8/BOOL, `C` = u8, `s` = s16, `S` = u16, `l/L` = long,
- `v` = void, `i` = i32, `q` = i64, `f` = f32, `d` = f64, `B` = bool,
- `c` = i8/BOOL, `C` = u8, `s` = i16, `S` = u16, `l/L` = long,
`Q` = u64, `*` = `[*]u8`,
- `@` = id (object), `#` = Class, `:` = SEL, `^v` = `*void`.
- Struct: `{Name=field0field1...}`, nested + cycle-broken.
@@ -1159,7 +1159,7 @@ landing the parallel JNI codegen.
| 1.29 | `uikit_create_gl_context` — `alloc` / `initWithAPI:` / `setCurrentContext:` + duplicate of 1.27's screen-scale read | done |
| 1.30 | `uikit_subscribe_keyboard_notifications` — first standalone 4-keyword selector exercise (`addObserver:selector:name:object:`) | done |
| 1.31 | `uikit_scene_will_connect_ios` — biggest cluster; the iOS scene-lifecycle entry. UIWindow / UIViewController / SxGLView wiring; EAGL drawable-properties dict build; `nativeScale` + `setContentScaleFactor:` DPI path; `displayLinkWithTarget:selector:` + run-loop install. Exercises every return shape used in uikit.sx. Net -44 lines (104 → 60). | done (b3558c3) |
| 1.32 | `uikit_keyboard_will_change_frame` — `userInfo` / `objectForKey:` / `CGRectValue` / `doubleValue` / `unsignedLongValue` / `screen.bounds`. First standalone exercise of `#objc_call(CGRect)` (HFA, structurally equivalent to UIEdgeInsets) and `#objc_call(u64)` (LLVM-equivalent to s64). Net -14 lines. Runtime-verified by the locked-in test `examples/ffi-objc-call-12-rect-u64-returns.sx` (ac78490). | done (e1d300c) |
| 1.32 | `uikit_keyboard_will_change_frame` — `userInfo` / `objectForKey:` / `CGRectValue` / `doubleValue` / `unsignedLongValue` / `screen.bounds`. First standalone exercise of `#objc_call(CGRect)` (HFA, structurally equivalent to UIEdgeInsets) and `#objc_call(u64)` (LLVM-equivalent to i64). Net -14 lines. Runtime-verified by the locked-in test `examples/ffi-objc-call-12-rect-u64-returns.sx` (ac78490). | done (e1d300c) |
| 1.33 | **uikit.sx sweep — all remaining dispatch sites.** `renderbufferStorage:fromDrawable:` (bool, GL setup); `presentRenderbuffer:` (bool, every frame); `targetTimestamp` / `duration` (f64, every frame in `uikit_gl_view_tick`); `bounds` (CGRect, `uikit_compute_layer_pixel_size`); `locationInView:` (CGPoint HFA, every touch); `anyObject` (*void, every touch). First standalone `#objc_call(CGPoint)` exercise. Net -15 lines. Runtime-verified end-to-end: tapped a black pawn in iOS-sim chess and the move played correctly (1...d5, 2...d4). | done |
Verification per cluster: zig build / zig test / run_examples /
@@ -1178,9 +1178,9 @@ the work that remains is lowering + emit_llvm.
| 1.15 | `#jni_call(void)` codegen — new `.jni_msg_send` IR opcode + emit_llvm expansion: load `*env` for the vtable, GEP into slots 31 (GetObjectClass), 33 (GetMethodID), 61 (CallVoidMethod). No method-ID caching yet; static dispatch + non-void returns drop to `LLVMGetUndef` until 1.18+. | done (134c197 xfail + 9afcaa5 fix) |
| 1.16 | Lock in pre-caching IR shape — two `#jni_call` sites with literal `("noop", "()V")` emit two independent `GetMethodID` calls. IR snapshot at `tests/expected/ffi-jni-call-03-methodid-sharing.ir`. | done (13018ef) |
| 1.17 | Literal-keyed slot interning — `JniMsgSend.cache_key: ?CacheKey` carries the literal `(name, sig)` pair from `lower.zig`; `emit_llvm.getOrCreateJniSlots` interns `@SX_JNI_CLS_<key>` and `@SX_JNI_MID_<key>` globals per unique pair; per-call lowering does null-check + lazy populate via `GetObjectClass` → `NewGlobalRef` (slot 21) → `GetMethodID` on miss. Two literal sites now share one slot pair. | done (0d883b4) |
| 1.18 | `#jni_call(s32)` → CallIntMethod (vtable slot 49). One arm added to the `call_method_offset` switch; reuses the 1.17 cache. | done (1d7ea72 xfail + ebcfe4c fix) |
| 1.18 | `#jni_call(i32)` → CallIntMethod (vtable slot 49). One arm added to the `call_method_offset` switch; reuses the 1.17 cache. | done (1d7ea72 xfail + ebcfe4c fix) |
| 1.18+ | Lift JNI vtable offsets into a `const Jni` named-constants struct. Pre-loaded Object/Boolean/Long/Float/Double slots so 1.191.22 are one-line switch arms. | done (c1877fc) |
| 1.19 | `#jni_call(s64)` → CallLongMethod (vtable slot 52). One arm added. | done (da5b635 xfail + 5945a8c fix) |
| 1.19 | `#jni_call(i64)` → CallLongMethod (vtable slot 52). One arm added. | done (da5b635 xfail + 5945a8c fix) |
| 1.20 | `#jni_call(f64)` → CallDoubleMethod (vtable slot 58). First non-integer JNI return. | done (xfail + ca4ba75 fix) |
| 1.21 | `#jni_call(bool)` → CallBooleanMethod (vtable slot 37). | done (xfail + b0e8659 fix) |
| 1.22 | `#jni_call(*void)` → CallObjectMethod (vtable slot 34). Pointer-return detected via `TypeInfo.pointer | .many_pointer` ahead of the primitive switch. LocalRef cleanup deferred — chess consumes objects inline. | done (xfail + b5694cc fix) |
@@ -1200,7 +1200,7 @@ All ten sub-steps (1.151.24) shipped. `#jni_call(T)` and
`#jni_static_call(T)` lower to JNI vtable indirection with shared
`(name, sig)` literal-keyed slot interning (one `jclass GlobalRef` +
one `jmethodID` per unique pair, populated lazily on the first
matching call). Return-type matrix covers `void` / `s32` / `s64` /
matching call). Return-type matrix covers `void` / `i32` / `i64` /
`f64` / `bool` / `*T`. Static dispatch skips `GetObjectClass` and
uses the parallel `GetStaticMethodID` + `CallStatic<Type>Method`
family. Both OS gates verified by `cross_compile.sh` (3/3 tuples
@@ -1284,14 +1284,14 @@ alias; no lowering yet.
| # | What | Status |
|-----|---|---|
| 2.8 | `src/ir/jni_descriptor.zig` + `.test.zig`. `writeType` appends one JNI descriptor for an sx type AST node; `deriveMethod` returns the full `(args)ret` descriptor for a `ForeignMethodDecl`, skipping the implicit `self` on instance methods. `Context.enclosing_path` resolves `*Self` to its `L<path>;` form. Primitive table-driven (void→V, bool→Z, s8/u8→B, s16→S, u16→C, s32→I, s64→J, f32→F, f64→D); arrays `[]T`/`[*]T`/`[N]T` → `[<elem>`. Cross-class `*Foo` → explicit error (lands in 2.9). 10 unit tests pass. **Cadence note**: landed as single commit since internal compiler functions don't have a sx-level snapshot surface yet — the rule re-applies at 2.11 where call-site lowering becomes end-to-end observable. | done (21c4906) |
| 2.8 | `src/ir/jni_descriptor.zig` + `.test.zig`. `writeType` appends one JNI descriptor for an sx type AST node; `deriveMethod` returns the full `(args)ret` descriptor for a `ForeignMethodDecl`, skipping the implicit `self` on instance methods. `Context.enclosing_path` resolves `*Self` to its `L<path>;` form. Primitive table-driven (void→V, bool→Z, i8/u8→B, i16→S, u16→C, i32→I, i64→J, f32→F, f64→D); arrays `[]T`/`[*]T`/`[N]T` → `[<elem>`. Cross-class `*Foo` → explicit error (lands in 2.9). 10 unit tests pass. **Cadence note**: landed as single commit since internal compiler functions don't have a sx-level snapshot surface yet — the rule re-applies at 2.11 where call-site lowering becomes end-to-end observable. | done (21c4906) |
| 2.9 | Cross-class `*Foo` resolves via `Context.classes: ?*const ClassRegistry` (a `StringHashMap` of sx alias → foreign path). `*Self` and `*Foo` share one code path. Retired `CrossClassRefNotYetSupported` in favour of `UnknownClassAlias`, which fires for both "no registry provided" and "alias not in registry". | done (5188265) |
| 2.10 | `deriveMethod` short-circuits to the `jni_descriptor_override` (2.6 escape-hatch) when present, returning the override verbatim through an `allocator.dupe`. Bypasses normal derivation entirely — including resolution failures, which lets users escape `UnknownClassAlias` errors for synthetic-method cases. | done (ca840ff) |
## Phase 2B complete (signature derivation)
`src/ir/jni_descriptor.zig` handles every shape the parser can hand it:
- Primitive types: `void/bool/s8..s64/u8/u16/f32/f64` → JNI single-char.
- Primitive types: `void/bool/i8..i64/u8/u16/f32/f64` → JNI single-char.
- Arrays / slices / many-pointers: `[<elem>` (recursive).
- `*Self` → `L<enclosing_path>;`.
- `*Foo` → looks up Foo's foreign path in the supplied registry.
@@ -1431,8 +1431,8 @@ When sx grows the cross-target story far enough:
forwarding, R.1R.5 retiring the legacy NativeActivity surface — all
landed; chess on Pixel runs end-to-end as the integration witness).
JNI return + parameter type validation lives in lowering with source-
spanned diagnostics; Call<T>Method coverage spans bool / s8 / s16 /
u16 / s32 / s64 / f32 / f64 / pointer; varargs promotion is wired.
spanned diagnostics; Call<T>Method coverage spans bool / i8 / i16 /
u16 / i32 / i64 / f32 / f64 / pointer; varargs promotion is wired.
Phase 3 step 3.0 landed (for real this time): `inst.method(args)` on
an `#objc_class` / `#objc_protocol` receiver derives the selector via
@@ -1534,7 +1534,7 @@ type-check.
turned out to be a red herring: the actual root cause was that
`inferExprType` for a chained call `Cls.static().instance(...)` never
looked the inner call's foreign-class declaration up, so the outer
dispatch saw a `.s64` receiver, the `foreign_class_map.get(...)` lookup
dispatch saw a `.i64` receiver, the `foreign_class_map.get(...)` lookup
missed, and lowering emitted `error: unresolved 'method'`. The macOS
target appeared to work because `inline if OS == .ios { ... }` strips
the gated body before lowering — eliding every call that would have
@@ -1832,11 +1832,11 @@ zig build && zig build test && bash tests/run_examples.sh && bash tests/cross_co
`emitFunctionDecl` ([src/ir/emit_llvm.zig:682](src/ir/emit_llvm.zig#L682))
passes `is_var_arg=1` to `LLVMFunctionType` accordingly. New
`promoteCVariadicArgs` applies C default argument promotion
(`bool/s8/s16/u8/u16 → s32`, `f32 → f64`) to extras past the fixed
(`bool/i8/i16/u8/u16 → i32`, `f32 → f64`) to extras past the fixed
param count. `packVariadicCallArgs` early-outs for foreign+variadic
so the slice-packing path is bypassed entirely. New test
`examples/ffi-foreign-cvariadic.sx` + `.c` exercise s64 / f64 / s32
returns through C `va_arg` over s32 / f64 / `*u8` element types.
`examples/ffi-foreign-cvariadic.sx` + `.c` exercise i64 / f64 / i32
returns through C `va_arg` over i32 / f64 / `*u8` element types.
Stale-snapshot drift from in-progress std.sx additions (`xml_escape`,
`path_join`, `BuildOptions.set_post_link_*`) re-pinned in 12
expected files — verified all diffs were dead-decl additions, string
@@ -1878,10 +1878,10 @@ zig build && zig build test && bash tests/run_examples.sh && bash tests/cross_co
nonvirt : CallNonvirtualByteMethod=70 / Char=73 / Short=76
static : CallStaticByteMethod=120 / Char=123 / Short=126
Each variant's `.jni_msg_send` return-type switch grew rows for
`.s8` / `.s16` / `.u16` (jbyte / jshort / jchar). New
`.i8` / `.i16` / `.u16` (jbyte / jshort / jchar). New
`LLVMEmitter.jniPromoteVararg(val, raw_ty)` handles the call-site
promotion that JNI's variadic Call<T>Method runtime expects:
s8 / s16 → SExt to i32
i8 / i16 → SExt to i32
u8 / u16 / bool → ZExt to i32
f32 → FPExt to f64
Pointers and wide types pass through unchanged. Wired into all
@@ -1895,8 +1895,8 @@ zig build && zig build test && bash tests/run_examples.sh && bash tests/cross_co
hiding the more useful "unsupported return/parameter type at
this token" diagnostic. New cross-compile test
`examples/114-jni-promoted-narrow-types.sx` exercises a
`#jni_class` returning `s8 / s16 / u16` and a varargs method
taking `(s8, s16, u16, f32)`; IR shows the expected
`#jni_class` returning `i8 / i16 / u16` and a varargs method
taking `(i8, i16, u16, f32)`; IR shows the expected
`sext i8 → i32`, `sext i16 → i32`, `zext i16 → i32`, and
`double 1.5e+00` (FPExt folded for the constant) at the call
site. Tests 112 / 113 migrated to use `u32` (Java has no
@@ -1910,7 +1910,7 @@ zig build && zig build test && bash tests/run_examples.sh && bash tests/cross_co
methods) and rejects unsupported parameter types at the type
token's span; `lowerJniCall` validates each method arg's TypeId
post-lowering against the arg expression's span. Same supported
set as returns (bool / s32 / s64 / f32 / f64 / pointer) minus
set as returns (bool / i32 / i64 / f32 / f64 / pointer) minus
`void` for params. Refactor splits `validateJniReturnType` /
`validateJniParamType` over a shared `validateJniType` core that
formats the diagnostic with a "return type" / "parameter type"
@@ -1923,7 +1923,7 @@ zig build && zig build test && bash tests/run_examples.sh && bash tests/cross_co
`examples/113-jni-unsupported-param-type.sx` locks in the
parameter-type diagnostic shape (e.g.
`examples/113-jni-unsupported-param-type.sx:16:30: error: JNI
call 'Foo.take': unsupported parameter type 's8' (...)`). 143
call 'Foo.take': unsupported parameter type 'i8' (...)`). 143
host + 9 cross tests green; chess on Pixel still builds clean.
- 2026-05-20: JNI return-type validation lifted from emit_llvm
@@ -1931,11 +1931,11 @@ zig build && zig build test && bash tests/run_examples.sh && bash tests/cross_co
in `src/ir/lower.zig`) so the diagnostic carries the
return-type slot's source span. New
`Lowering.validateJniReturnType` helper mirrors the supported
set in emit_llvm's `.jni_msg_send` switch (void / bool / s32 /
s64 / f32 / f64 / pointer types); a `*Foo.bad()` call where
set in emit_llvm's `.jni_msg_send` switch (void / bool / i32 /
i64 / f32 / f64 / pointer types); a `*Foo.bad()` call where
`bad()` returns an unsupported type now produces e.g.
`examples/112-jni-unsupported-return-type.sx:15:29: error:
JNI call 'Foo.bad': unsupported return type 's8' (JNI lowering
JNI call 'Foo.bad': unsupported return type 'i8' (JNI lowering
supports ...)`. emit_llvm's diagnostic stays as defense in
depth — it would only fire if a future IR path bypasses the
lowering check. New focused test
@@ -1969,13 +1969,13 @@ zig build && zig build test && bash tests/run_examples.sh && bash tests/cross_co
- 2026-05-20: chess-on-Pixel size bug fixed by refactoring
`library/modules/platform/android.sx` to zero module-level
globals. Root cause: android.sx exported `g_viewport_w : s32 = 0`
and `g_viewport_h : s32 = 0` at module scope; chess's `main.sx`
globals. Root cause: android.sx exported `g_viewport_w : i32 = 0`
and `g_viewport_h : i32 = 0` at module scope; chess's `main.sx`
declared its own `g_viewport_w : f32 = 800.0` at module scope.
When chess `#import`ed android.sx, the imported public global
shadowed chess's local decl for the unqualified name resolution,
so chess's writes (`g_viewport_w = fc.viewport_w`) silently
clobbered android.sx's s32 with the logical f32 cast to s32 (414
clobbered android.sx's i32 with the logical f32 cast to i32 (414
instead of 1080). `Gles3Gpu.pixel_w` then fed `glViewport(0,0,
414,831)`, clipping rendering to a 414-pixel box in the GL-
bottom-left. Refactor moved every piece of Android backend state
@@ -2008,7 +2008,7 @@ zig build && zig build test && bash tests/run_examples.sh && bash tests/cross_co
`lowerObjcStaticCall` route through the same helper so the IR Ref's
recorded ret_ty matches what `inferExprType` reports. Pre-fix:
`UIWindow.alloc().initWithWindowScene(scene)` (and any other chained
shape) collapsed the inner ret to `.s64`, the next dispatch's
shape) collapsed the inner ret to `.i64`, the next dispatch's
`foreign_class_map.get(...)` missed, and lowering emitted
`error: unresolved 'initWithWindowScene'`. The "lazy-lower" wording in
the issue file is a red herring — the bug fires on direct calls too;

View File

@@ -38,8 +38,8 @@ game rebuilt.
<details><summary>Prior steps (2026-05-25 era)</summary>
- **`resolveType(null) → .s64` silent fallback removed.** `resolveType`
now takes a non-optional `*const Node`; the `null → .s64` branch is
- **`resolveType(null) → .i64` silent fallback removed.** `resolveType`
now takes a non-optional `*const Node`; the `null → .i64` branch is
gone. Callers that legitimately had no annotation handle it
themselves: top-level `var_decl` at `lower.zig:630` infers from the
initializer or diagnoses if neither is present (matches the
@@ -54,7 +54,7 @@ game rebuilt.
...`), which makes the always-non-null path obvious from the type.
Real-world impact: `g_pi := 3.14;` at the top level used to be
silently typed as `s64`. Now it infers as `f64`. Regression at
silently typed as `i64`. Now it infers as `f64`. Regression at
`examples/137-toplevel-var-type-inference.sx` (count/pi/flag — int /
float / bool inferred correctly). 159/159 example tests + chess
clean.
@@ -161,7 +161,7 @@ game rebuilt.
recursive heap content).
Also closes the type-inference half of the same bug: `NAME :: #run
expr;` with no annotation used to default to `s64` (silent fallback
expr;` with no annotation used to default to `i64` (silent fallback
in `resolveType(null)`). `lowerComptimeGlobal` now infers from the
expression's return type when no annotation is provided. The
silent fallback in `resolveType` itself is left in place for other
@@ -255,7 +255,7 @@ Phase 0 spike outcomes:
inst.zig BuiltinId, lower.zig (registry + return-type + reflection
handler), interp.zig (fallback), sema.zig (allowed-builtins list),
lsp/server.zig (both completion tables), library/modules/std.sx.
Smoke coverage added in `examples/50-smoke.sx` (u8/s32/s64/Point).
Smoke coverage added in `examples/50-smoke.sx` (u8/i32/i64/Point).
- **0.7** `#import` transitivity — surfaced and fixed via issue-0038.
- **0.8** `#foreign("c")` rename syntax — confirmed
`#foreign libc "name"`.
@@ -270,7 +270,7 @@ verification: `size_of(*u8)=8`, `size_of(Ptr where Ptr::*u8)=8`,
`size_of(?u8)=2`, `size_of(Maybe where Maybe::?u8)=2` — all clean on
interp + codegen.
Also landed during 0041/0042: the silent `.s64` fallback in
Also landed during 0041/0042: the silent `.i64` fallback in
`resolveTypeArg` is gone — unresolved type names now emit a real
diagnostic. Surfaced and removed two bogus `size_of(Complex)` /
`size_of(Sx)` calls in `examples/10-generic-struct.sx` that were
@@ -334,7 +334,7 @@ What landed:
store honours the destination width — no more "assume 8 bytes"
silent clobber. Regression test at
`examples/132-comptime-typed-store-widths.sx` exercises every
primitive width (u8/u16/u32/u64, s8..s64, bool, f32, f64) via
primitive width (u8/u16/u32/u64, i8..i64, bool, f32, f64) via
comptime checksums compared to runtime checksums.
- Call-convention mismatch at bare-fn → fn-pointer coercion is now
a compile error (commit `f886d5f`). The chess-debug sweep that
@@ -563,7 +563,7 @@ Allocator value naturally.
builds) + m3te (23/23). Suite 582/582, zbt 0. Discriminator note:
Obj-C `.alloc()` is zero-arg; Allocator `.alloc(size)` has an arg —
the sed keyed on `\.alloc\((?!\))`.
- **2026-05-25 (latest)** — `resolveType(null) → .s64` removed.
- **2026-05-25 (latest)** — `resolveType(null) → .i64` removed.
Signature changed to non-optional `*const Node`; 12 callers
surveyed and classified. The three unguarded ones — top-level
`var_decl` at `lower.zig:630` (now mirrors lowerVarDecl's
@@ -573,7 +573,7 @@ Allocator value naturally.
`if (x != null)` blocks; cleaned up to optional-payload syntax.
`examples/137-toplevel-var-type-inference.sx` proves the visible
win: `g_pi := 3.14;` at module scope now infers `f64` (used to be
silent `s64`). 159/159 + chess clean.
silent `i64`). 159/159 + chess clean.
- **2026-05-25 (penultimate)** — Phase 1.4a shipped. `valueToLLVMConst`
takes IR `TypeId` (not LLVM type) + an interpreter handle.
String/slice fat pointers are serialized by capturing the
@@ -616,8 +616,8 @@ Allocator value naturally.
at line 676 now passes `global.name` so the diagnostic locates the
offending `#run` site. `lowerComptimeGlobal` (`lower.zig:6384`)
infers the return type from the expression when the user omits
the type annotation — closes the silent-s64 default for `NAME ::
#run expr;` bindings. The broader `resolveType(null) -> .s64`
the type annotation — closes the silent-i64 default for `NAME ::
#run expr;` bindings. The broader `resolveType(null) -> .i64`
fallback is left in place for other callers — flagged for a
follow-up audit. Regression at
`examples/134-comptime-aggregate-global.sx`. 156/156 + chess green.
@@ -687,7 +687,7 @@ Allocator value naturally.
(`Tracer.count = 1`) — interp + codegen parity. 152/152 +
chess green.
- **2026-05-24** — issue-0041 and issue-0042 both fixed end-to-end.
Also removed the silent `.s64` fallback in `resolveTypeArg`,
Also removed the silent `.i64` fallback in `resolveTypeArg`,
guarded the two upstream callers (`buildTypeBindings`,
`inferGenericReturnType`) with `type_bridge.isTypeShapedAstNode`,
and fixed three parser regressions introduced by the 0041 work
@@ -759,8 +759,8 @@ Allocator value naturally.
`mi.ret_type == void_ptr`, but `*void` is overloaded — both
Self-disguised-as-*void AND a literal `-> *void` return appear as
the same `TypeId`. With `target_type` leaking from the enclosing
function's return type (e.g. `main -> s32`), every `*void` return
was loaded as `s32`, yielding 0 → null. Fix: stash `ret_is_self`
function's return type (e.g. `main -> i32`), every `*void` return
was loaded as `i32`, yielding 0 → null. Fix: stash `ret_is_self`
on `ProtocolMethodInfo` during `registerProtocolDecl` (set when
the AST return-type node is the `Self` type-expr), and gate the
unbox on that flag. Regression at
@@ -781,12 +781,12 @@ Allocator value naturally.
## Known issues (discovered during execution)
### ISSUE-MEM-001: Type inference defaults `p := malloc(64)` to `s64`
### ISSUE-MEM-001: Type inference defaults `p := malloc(64)` to `i64`
**Severity:** medium (workaround exists; bites unexpectedly).
**Symptom:** Writing `p := malloc(64)` (no explicit type) infers
`p: s64` instead of `p: *void`. Subsequent `free(p)` then fails LLVM
`p: i64` instead of `p: *void`. Subsequent `free(p)` then fails LLVM
verification with "Call parameter type does not match function
signature!" because `free` expects `ptr` but receives `i64`.
@@ -795,8 +795,8 @@ or `xx malloc(64);` at the call site.
**Reproduction:**
```sx
main :: () -> s32 {
p := malloc(64); // p inferred as s64
main :: () -> i32 {
p := malloc(64); // p inferred as i64
free(p); // LLVM verify fails: ptr expected, i64 given
0;
}
@@ -804,7 +804,7 @@ main :: () -> s32 {
**Root cause:** Likely in the inference path for `:=` declarations
when the RHS is a `*void`-returning #builtin. The compiler defaults
the binding to s64 instead of matching the return type. To
the binding to i64 instead of matching the return type. To
investigate in a future session.
**Status:** Open. Not blocking mem.sx work but worth fixing as a
@@ -920,9 +920,9 @@ where users need the underlying state (TrackingAllocator).
its auto-unbox path on `mi.ret_type == void_ptr`, but the same
`TypeId` covers both Self-disguised-as-*void and a literal
`-> *void`. With `target_type` leaking from the surrounding
function (e.g. `main -> s32`), every protocol call returning
function (e.g. `main -> i32`), every protocol call returning
`*void` got its result loaded as `sizeof(target_type)` bytes — for
`s32` that's the first 4 bytes of the malloc'd block, which were
`i32` that's the first 4 bytes of the malloc'd block, which were
zero, comparing equal to null.
**Fix:** Tag `ProtocolMethodInfo` with `ret_is_self: bool`, set in

View File

@@ -12,14 +12,14 @@ wrapped around them. A function that can fail adds a trailing `!` to its
return type:
```sx
parse_digit :: (s: string) -> (s32, !) {
parse_digit :: (s: string) -> (i32, !) {
if s.len == 0 raise error.Empty;
if !is_digit(s[0]) raise error.BadDigit;
return s[0] - '0';
}
```
The `(s32, !)` says "returns an `s32` on success, or an error." The `!`
The `(i32, !)` says "returns an `i32` on success, or an error." The `!`
is one more slot in sx's normal multi-return — the error rides
alongside the values, it doesn't replace them.
@@ -61,7 +61,7 @@ in the signature:
```sx
ParseErr :: error { Empty, BadDigit, Overflow };
parse_int :: (s: string) -> (s32, !ParseErr) {
parse_int :: (s: string) -> (i32, !ParseErr) {
if s.len == 0 raise error.Empty;
if overflowed raise error.Overflow;
...
@@ -109,7 +109,7 @@ When you call a failable function and want its error to bubble up to
*your* caller, prefix the call with `try`:
```sx
two_digits :: (s: string) -> (s32, !) {
two_digits :: (s: string) -> (i32, !) {
a := try parse_digit(s); // if this fails, two_digits fails
b := try parse_digit(s[1..]);
return a * 10 + b;
@@ -153,7 +153,7 @@ attempt.
port := parse_port(s) or 8080; // if parsing fails, port = 8080
```
The error is absorbed; `port` is a plain `s32`.
The error is absorbed; `port` is a plain `i32`.
### Chain attempts — first success wins
@@ -347,7 +347,7 @@ For human-readable context, use `log` on the error path — the tag tells
you *what* failed, the log tells you the *details*:
```sx
parse :: (s: string) -> (s32, !) {
parse :: (s: string) -> (i32, !) {
onfail e { log.warn("parsing {}: {}", s, e); }
...
}

View File

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

View File

@@ -1,5 +1,5 @@
#import "modules/std.sx";
main :: () -> s32 {
main :: () -> i32 {
x := 42;
{
print("scope opened\n");

View File

@@ -1,5 +1,5 @@
#import "modules/std.sx";
main :: () -> s32 {
main :: () -> i32 {
defer print("still here\n");
return 42;
}

View File

@@ -1,6 +1,6 @@
#import "modules/std.sx";
sumOf10 :: () -> s32 {
sumOf10 :: () -> i32 {
i:= 1;
s:=0;
while i <= 10 {

View File

@@ -1,7 +1,7 @@
#import "modules/std.sx";
quick_sort :: (items: []$T) {
partition :: (items: []T, lo: s64, hi: s64) -> s64 {
partition :: (items: []T, lo: i64, hi: i64) -> i64 {
pivot := items[hi];
i := lo - 1;
j := lo;
@@ -17,7 +17,7 @@ quick_sort :: (items: []$T) {
i
}
sort :: (items: []T, lo: s64, hi: s64) {
sort :: (items: []T, lo: i64, hi: i64) {
if lo < hi {
pi := partition(items, lo, hi);
sort(items, lo, pi - 1);
@@ -29,7 +29,7 @@ quick_sort :: (items: []$T) {
}
main :: () {
arr : []s64 = .[333, 2, 3, 5, 2, 2, 3, 4, 5, 6, 6, 1];
arr : []i64 = .[333, 2, 3, 5, 2, 2, 3, 4, 5, 6, 6, 1];
quick_sort(arr);
print("{}\n", arr);
}

View File

@@ -24,7 +24,7 @@ Vec2 :: struct {
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) }
len :: (v: Vec2) -> i32 { xx (v.x + v.y) }
}
EdgeInsets :: struct {
@@ -39,16 +39,16 @@ EdgeInsets :: struct {
}
Trio :: struct {
a: s32;
b: s32;
c: s32;
a: i32;
b: i32;
c: i32;
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: i32, b: i32, c: i32) -> Trio { Trio.{ a = a, b = b, c = c } }
sum :: (t: Trio) -> i32 { t.a + t.b + t.c }
}
Result :: enum {
ok: s32;
ok: i32;
err: string;
}
@@ -121,7 +121,7 @@ main :: () {
// T8: .variant(payload) as function argument (match-as-expression)
{
describe :: (sh: Shape) -> s32 {
describe :: (sh: Shape) -> i32 {
if sh == {
case .circle: 10;
case .rect: 20;
@@ -145,7 +145,7 @@ main :: () {
// T10: Match as expression returning tagged enum
{
select :: (n: s32) -> Shape {
select :: (n: i32) -> Shape {
if n == {
case 0: .none;
case 1: .circle(1.0);
@@ -243,7 +243,7 @@ main :: () {
// E2: Function taking both types — each resolves correctly
{
use_both :: (sh: Shape, v: Vec2) {
ms : s32 = 0;
ms : i32 = 0;
if sh == { case .circle: (r) { ms = xx r; } else: {} }
print("E2: {} {} {}\n", ms, v.x, v.y);
}
@@ -252,7 +252,7 @@ main :: () {
// E3: Bare .variant (no parens) as function arg
{
check_none :: (sh: Shape) -> s32 {
check_none :: (sh: Shape) -> i32 {
if sh == { case .none: 1; else: 0; }
}
print("E3: {}\n", check_none(.none));
@@ -268,7 +268,7 @@ main :: () {
// E5: Tagged enum .variant(payload) in match-as-expression
{
sh : Shape = .circle(42.0);
r : s32 = 0;
r : i32 = 0;
if sh == {
case .circle: (v) { r = xx v; }
case .rect: (sz) { r = xx sz.w; }
@@ -280,7 +280,7 @@ main :: () {
// E6: Color enum (plain, not tagged) still works with bare .variant
{
c : Color = .green;
ci : s32 = xx c;
ci : i32 = xx c;
print("E6: {}\n", ci);
}

View File

@@ -11,6 +11,6 @@ do_it :: () -> bool {
true
}
main :: () -> s32 {
main :: () -> i32 {
if do_it() then 0 else 1
}

View File

@@ -9,21 +9,21 @@
#import "modules/std.sx";
double :: (x: s32) -> s32 => x * 2;
double :: (x: i32) -> i32 => x * 2;
sum :: (a: s32, b: s32) -> s32 => a + b;
sum :: (a: i32, b: i32) -> i32 => a + b;
answer :: () -> s32 => 42;
answer :: () -> i32 => 42;
Point :: struct {
x: s32;
y: s32;
x: i32;
y: i32;
total :: (self: *Point) -> s32 => self.x + self.y;
scaled :: (self: *Point, by: s32) -> s32 => (self.x + self.y) * by;
total :: (self: *Point) -> i32 => self.x + self.y;
scaled :: (self: *Point, by: i32) -> i32 => (self.x + self.y) * by;
}
main :: () -> s32 {
main :: () -> i32 {
print("double: {}\n", double(7));
print("sum: {}\n", sum(3, 4));
print("answer: {}\n", answer());

View File

@@ -9,7 +9,7 @@
Show :: protocol {
show :: () -> string;
}
A :: struct { x: s64; }
A :: struct { x: i64; }
B :: struct { s: string; }
impl Show for A { show :: (self: *A) -> string => "A"; }
impl Show for B { show :: (self: *B) -> string => "B"; }
@@ -21,7 +21,7 @@ each :: (..xs: Show) -> void {
}
}
main :: () -> s32 {
main :: () -> i32 {
// Runtime range, cursor used.
for 0..3 (i) { print("i={}\n", i); }

View File

@@ -7,9 +7,9 @@ Shape :: enum {
none;
}
main :: () -> s32 {
main :: () -> i32 {
// By-ref mutation writes back into the array (impossible with a value copy).
xs : [3]s64 = .[1, 2, 3];
xs : [3]i64 = .[1, 2, 3];
for xs (*x) { x.* = x + 100; }
print("{} {} {}\n", xs[0], xs[1], xs[2]);

View File

@@ -4,23 +4,23 @@
#import "modules/std.sx";
Box :: struct {
v: s64;
boxed :: (self: Box) -> s64 { self.v } // value receiver
v: i64;
boxed :: (self: Box) -> i64 { self.v } // value receiver
}
sum_ptr :: (xs: *List(s64)) -> s64 {
total : s64 = 0;
sum_ptr :: (xs: *List(i64)) -> i64 {
total : i64 = 0;
for xs (n) { total = total + n; } // iterate through a *List
total
}
main :: () -> s32 {
xs := List(s64).{};
main :: () -> i32 {
xs := List(i64).{};
xs.append(10);
xs.append(20);
xs.append(30);
s : s64 = 0;
s : i64 = 0;
for xs (n) { s = s + n; } // value capture
print("sum {}\n", s); // 60
@@ -33,7 +33,7 @@ main :: () -> s32 {
bs := List(Box).{};
bs.append(.{ v = 7 });
bt : s64 = 0;
bt : i64 = 0;
for bs (*b) { bt = bt + b.boxed(); } // *Box receiver, value-self method
print("boxes {}\n", bt); // 7
0

View File

@@ -45,7 +45,7 @@ END;
print("heredoc: {}\n", hd);
// Undefined with type
undef_val : s32 = ---;
undef_val : i32 = ---;
undef_val = 77;
print("undef-then-set: {}\n", undef_val);
@@ -54,7 +54,7 @@ END;
print("enum-lit: {}\n", c);
// Null pointer
np : *s32 = null;
np : *i32 = null;
print("null-ptr: {}\n", np);
// String .len

View File

@@ -4,17 +4,17 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
add :: (a: s32, b: s32) -> s32 { a + b }
add :: (a: i32, b: i32) -> i32 { a + b }
mul :: (a: s32, b: s32) -> s32 { a * b }
mul :: (a: i32, b: i32) -> i32 { a * b }
// P4 edge: Chained default→default calls
Chained :: protocol {
base :: (msg: string) -> s32;
wrap :: (msg: string) -> s32 {
base :: (msg: string) -> i32;
wrap :: (msg: string) -> i32 {
self.base(msg) + 1
}
double_wrap :: (msg: string) -> s32 {
double_wrap :: (msg: string) -> i32 {
self.wrap(msg) + self.wrap(msg)
}
}
@@ -136,31 +136,31 @@ main :: () {
// Implicit widening conversions
wu : u8 = 200;
ws : s64 = wu;
print("widen-u8-s64: {}\n", ws);
ws : i64 = wu;
print("widen-u8-i64: {}\n", ws);
wi3 : s32 = 42;
wi3 : i32 = 42;
wf : f64 = wi3;
print("widen-s32-f64: {}\n", wf);
print("widen-i32-f64: {}\n", wf);
wf32 : f32 = 1.5;
wf64 : f64 = wf32;
print("widen-f32-f64: {}\n", wf64);
wu2 : u8 = 100;
ws2 : s16 = wu2;
print("widen-u8-s16: {}\n", ws2);
ws2 : i16 = wu2;
print("widen-u8-i16: {}\n", ws2);
// More xx narrowing
xl : s64 = 12345;
xs : s32 = xx xl;
print("xx-s64-s32: {}\n", xs);
xl : i64 = 12345;
xs : i32 = xx xl;
print("xx-i64-i32: {}\n", xs);
xd : f64 = 1.5;
xf : f32 = xx xd;
print("xx-f64-f32: {}\n", xf);
xdf : f64 = 7.9;
xdi : s32 = xx xdf;
print("xx-f64-s32: {}\n", xdi);
xdi : i32 = xx xdf;
print("xx-f64-i32: {}\n", xdi);
}

View File

@@ -116,7 +116,7 @@ main :: () {
print("nested-break: {} {}\n", nb_outer, nb_icount);
// For loop basic
farr : [4]s32 = .[10, 20, 30, 40];
farr : [4]i32 = .[10, 20, 30, 40];
out("for:");
for farr (it) {
out(" ");
@@ -163,7 +163,7 @@ main :: () {
out("\n");
// For on slice
fsl : []s32 = .[10, 20, 30];
fsl : []i32 = .[10, 20, 30];
out("for-slice:");
for fsl (it) {
print(" {}", it);
@@ -178,8 +178,8 @@ main :: () {
out("\n");
// Nested for
nf_a : [2]s32 = .[0, 1];
nf_b : [2]s32 = .[0, 1];
nf_a : [2]i32 = .[0, 1];
nf_b : [2]i32 = .[0, 1];
out("for-nested:");
for nf_a (oa) {
for nf_b (ob) {
@@ -189,7 +189,7 @@ main :: () {
out("\n");
// For with break preserving index
fbi : [5]s32 = .[10, 20, 30, 40, 50];
fbi : [5]i32 = .[10, 20, 30, 40, 50];
fbi_idx := 0;
for fbi, 0.. (it, ix) {
if it == 30 { fbi_idx = ix; break; }

View File

@@ -4,23 +4,23 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
add :: (a: s32, b: s32) -> s32 { a + b }
add :: (a: i32, b: i32) -> i32 { a + b }
mul :: (a: s32, b: s32) -> s32 { a * b }
mul :: (a: i32, b: i32) -> i32 { a * b }
identity :: (x: $T) -> T { x }
pair_add :: (a: $T, b: $U) -> s64 {
cast(s64) a + cast(s64) b
pair_add :: (a: $T, b: $U) -> i64 {
cast(i64) a + cast(i64) b
}
typed_sum :: (..args: []s32) -> s32 {
typed_sum :: (..args: []i32) -> i32 {
result := 0;
for args (it) { result = result + it; }
result
}
apply :: (f: (s32, s32) -> s32, x: s32, y: s32) -> s32 {
apply :: (f: (i32, i32) -> i32, x: i32, y: i32) -> i32 {
f(x, y)
}
@@ -28,11 +28,11 @@ void_return :: () {
return;
}
implicit_return :: (x: s32) -> s32 {
implicit_return :: (x: i32) -> i32 {
x * 2
}
early_return :: (x: s32) -> s32 {
early_return :: (x: i32) -> i32 {
if x > 10 { return 99; }
x
}
@@ -53,7 +53,7 @@ main :: () {
print("typed-const: {}\n", TYPED_PI);
// Variable with default init
di : s32;
di : i32;
print("default-init: {}\n", di);
// Implicit return
@@ -68,7 +68,7 @@ main :: () {
print("void-return: ok\n");
// Generic — single param
print("generic-s32: {}\n", identity(42));
print("generic-i32: {}\n", identity(42));
print("generic-f32: {}\n", identity(1.5));
print("generic-bool: {}\n", identity(true));
@@ -76,7 +76,7 @@ main :: () {
print("generic-multi: {}\n", pair_add(10, 20));
// Lambda
double :: (x: s32) => x * 2;
double :: (x: i32) => x * 2;
print("lambda: {}\n", double(7));
// Lambda with return type
@@ -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: i32, b: i32) -> i32 { a + b }
print("local-fn: {}\n", local_add(3, 4));
// Nested function calls
@@ -94,11 +94,11 @@ main :: () {
print("varargs: {}\n", typed_sum(1, 2, 3, 4, 5));
// Spread
spread_arr : [3]s32 = .[10, 20, 30];
spread_arr : [3]i32 = .[10, 20, 30];
print("spread: {}\n", typed_sum(..spread_arr));
// Function pointers
fp : (s32, s32) -> s32 = add;
fp : (i32, i32) -> i32 = add;
print("fp: {}\n", fp(3, 4));
fp = mul;
print("fp-reassign: {}\n", fp(3, 4));

View File

@@ -4,7 +4,7 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
Point :: struct { x, y: s32; }
Point :: struct { x, y: i32; }
Color :: enum { red; green; blue; }
@@ -29,14 +29,14 @@ main :: () {
print("sqrt-f64: {}\n", sqrt(16.0));
// size_of
print("sizeof-s32: {}\n", size_of(s32));
print("sizeof-i32: {}\n", size_of(i32));
print("sizeof-f64: {}\n", size_of(f64));
print("sizeof-struct: {}\n", size_of(Point));
// align_of
print("alignof-u8: {}\n", align_of(u8));
print("alignof-s32: {}\n", align_of(s32));
print("alignof-s64: {}\n", align_of(s64));
print("alignof-i32: {}\n", align_of(i32));
print("alignof-i64: {}\n", align_of(i64));
print("alignof-struct: {}\n", align_of(Point));
// type_of + category matching
@@ -121,7 +121,7 @@ main :: () {
// cast
cval : f64 = 3.7;
print("cast: {}\n", cast(s32) cval);
cv2 : s32 = 42;
print("cast: {}\n", cast(i32) cval);
cv2 : i32 = 42;
print("cast-int-f64: {}\n", cast(f64) cv2);
}

View File

@@ -4,7 +4,7 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
Point :: struct { x, y: s32; }
Point :: struct { x, y: i32; }
Shape :: enum {
circle: f32;

View File

@@ -4,9 +4,9 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
Point :: struct { x, y: s32; }
Point :: struct { x, y: i32; }
point_sum :: (p: Point) -> s32 { p.x + p.y }
point_sum :: (p: Point) -> i32 { p.x + p.y }
// #run compile-time constants

View File

@@ -4,7 +4,7 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
Point :: struct { x, y: s32; }
Point :: struct { x, y: i32; }
main :: () {

View File

@@ -11,7 +11,7 @@ main :: () {
// ========================================================
print("=== 25. Array Loop Mutation ===\n");
{
arr : [4]s32 = .[0, 0, 0, 0];
arr : [4]i32 = .[0, 0, 0, 0];
i := 0;
while i < 4 {
arr[i] = xx (i + 1);

View File

@@ -10,7 +10,7 @@ main :: () {
{
print("=== UFCS Aliases ===\n");
num_sum :: (a: s64, b: s64) -> s64 { a + b }
num_sum :: (a: i64, b: i64) -> i64 { 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: i64, b: i64, c: i64, d: i64) -> i64 { 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: i64, b: i64) -> (i64, i64) { (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: i64) -> (i64) { (x,) }
t := wrap(99);
print("{}\n", t.0); // 99
}

View File

@@ -4,7 +4,7 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
add :: (a: s32, b: s32) -> s32 { a + b }
add :: (a: i32, b: i32) -> i32 { a + b }
main :: () {
@@ -23,12 +23,12 @@ main :: () {
assert(v.w == 4.0);
// Function call with trailing comma
add :: (a: s64, b: s64) -> s64 { return a + b; }
add :: (a: i64, b: i64) -> i64 { return a + b; }
r := add(10, 20,);
assert(r == 30);
// Array literal with trailing comma
arr := s64.[1, 2, 3,];
arr := i64.[1, 2, 3,];
assert(arr[2] == 3);
print("trailing commas ok\n");

View File

@@ -14,15 +14,15 @@
E :: error { Neg }
// dead `return 99;` after an unconditional return
const_one :: () -> s64 { return 1; return 99; }
const_one :: () -> i64 { return 1; return 99; }
// dead `return x;` after an unconditional raise (the failable closure shape)
always_raise :: (x: s64) -> (s64, !E) { raise error.Neg; return x; }
always_raise :: (x: i64) -> (i64, !E) { raise error.Neg; return x; }
// guard: a conditional return must still fall through to the trailing return
clamp :: (x: s64) -> s64 { if x > 10 { return 10; } return x; }
clamp :: (x: i64) -> i64 { if x > 10 { return 10; } return x; }
main :: () -> s32 {
main :: () -> i32 {
print("const_one={}\n", const_one()); // 1
print("raised={}\n", always_raise(5) catch (e) 0); // 0
print("clamp_hi={}\n", clamp(42)); // 10

View File

@@ -7,14 +7,14 @@
#import "modules/std.sx";
Counter :: struct { n: s32; }
Counter :: struct { n: i32; }
// FREE functions (defined outside the struct), pointer first param.
bump :: ufcs (c: *Counter) -> s32 { c.n += 1; return c.n; }
bump :: ufcs (c: *Counter) -> i32 { c.n += 1; return c.n; }
// reached ONLY via UFCS — must still be emitted.
reset :: ufcs (c: *Counter) { c.n = 0; }
main :: () -> s32 {
main :: () -> i32 {
c := Counter.{ n = 10 };
a := c.bump(); // 11, mutates c
b := c.bump(); // 12

View File

@@ -12,22 +12,22 @@
#import "modules/std.sx";
// Implicit return: trailing expression, no `;`.
double :: (n: s32) -> s32 { n * 2 }
double :: (n: i32) -> i32 { n * 2 }
// if/else as a value — each branch's last expression has no `;`.
sign :: (n: s32) -> s32 {
sign :: (n: i32) -> i32 {
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 {
sum3 :: (a: i32, b: i32, c: i32) -> i32 {
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 {
classify :: (n: i32) -> i32 {
if n == {
case 0: 100;
case 1: 10;
@@ -35,8 +35,8 @@ classify :: (n: s32) -> s32 {
}
}
main :: () -> s32 {
total : s32 = 0;
main :: () -> i32 {
total : i32 = 0;
total = total + double(10); // 20
total = total + sign(-7); // -1
total = total + sum3(1, 2, 3); // 6

View File

@@ -6,8 +6,8 @@
#import "modules/std.sx";
// `n * 2;` discards the value → the function returns nothing.
double :: (n: s32) -> s32 {
double :: (n: i32) -> i32 {
n * 2;
}
main :: () -> s32 { double(5) }
main :: () -> i32 { double(5) }

View File

@@ -8,9 +8,9 @@
#import "modules/std.sx";
pair :: () -> (s32, s32) { (5, 7) }
pair :: () -> (i32, i32) { (5, 7) }
main :: () -> s32 {
main :: () -> i32 {
// destructure decl inside a value-bound block
sum := {
a, b := pair();

View File

@@ -9,14 +9,14 @@
#import "modules/std.sx";
sign :: (n: s32) -> s32 {
sign :: (n: i32) -> i32 {
if n == {
case 0: 0;
else: if n > 0 then 1 else -1;
}
}
classify :: (n: s32) -> s32 {
classify :: (n: i32) -> i32 {
if n == {
case 0: 100;
case 1: 10;
@@ -24,7 +24,7 @@ classify :: (n: s32) -> s32 {
}
}
main :: () -> s32 {
main :: () -> i32 {
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,9 +6,9 @@
#import "modules/std.sx";
scale :: (n: s32, factor: s32 = 2) -> s32 { n * factor }
scale :: (n: i32, factor: i32 = 2) -> i32 { n * factor }
label :: (n: s32, prefix: string = "v", suffix: string = "!") -> s32 {
label :: (n: i32, prefix: string = "v", suffix: string = "!") -> i32 {
print("{}{}{}\n", prefix, n, suffix);
n
}

View File

@@ -11,7 +11,7 @@
Json :: enum {
str: string;
int_: s64;
int_: i64;
null_;
}

View File

@@ -1,9 +1,9 @@
// Integer `{}` formatting across the full signed/unsigned range.
//
// Regression (issue 0090): the `{}` formatter was s64-based — it negated
// the value to print the sign (so s64::MIN, whose magnitude is
// unrepresentable as a positive s64, rendered as a bare "-"), and it had
// no unsigned-aware path (so a u64 all-ones value printed as the s64
// Regression (issue 0090): the `{}` formatter was i64-based — it negated
// the value to print the sign (so i64::MIN, whose magnitude is
// unrepresentable as a positive i64, rendered as a bare "-"), and it had
// no unsigned-aware path (so a u64 all-ones value printed as the i64
// reinterpretation, "-1"). Both extremes now render correctly: signed
// MIN prints all its digits, and unsigned integers print as unsigned
// decimal across all 64 bits.
@@ -12,16 +12,16 @@
main :: () {
// Signed extreme: magnitude is never negated, so MIN survives.
print("s64.min={}\n", s64.min);
print("s64.max={}\n", s64.max);
print("i64.min={}\n", i64.min);
print("i64.max={}\n", i64.max);
// Unsigned extreme: all 64 bits as unsigned decimal, not -1.
print("u64.max={}\n", u64.max);
// Spread across widths — signed.
print("s8.min={} s8.max={}\n", s8.min, s8.max);
print("s16.min={} s16.max={}\n", s16.min, s16.max);
print("s32.min={} s32.max={}\n", s32.min, s32.max);
print("i8.min={} i8.max={}\n", i8.min, i8.max);
print("i16.min={} i16.max={}\n", i16.min, i16.max);
print("i32.min={} i32.max={}\n", i32.min, i32.max);
// Spread across widths — unsigned (max is all-ones for that width).
print("u8.max={} u16.max={}\n", u8.max, u16.max);
@@ -31,7 +31,7 @@ main :: () {
print("u8.min={} u64.min={} zero={}\n", u8.min, u64.min, 0);
// Ordinary signed/unsigned values still print correctly.
neg : s32 = -42;
neg : i32 = -42;
pos : u32 = 4000000000;
print("neg={} pos={}\n", neg, pos);
}

View File

@@ -8,10 +8,10 @@
#import "modules/std.sx";
main :: () -> s32 {
main :: () -> i32 {
sum := 0;
for 0..1000000 (i) {
buf : [128]s64 = ---;
buf : [128]i64 = ---;
buf[0] = i;
sum += buf[0];
}

View File

@@ -8,8 +8,8 @@
#import "modules/std.sx";
main :: () -> s32 {
arr : [4096]s64 = ---;
main :: () -> i32 {
arr : [4096]i64 = ---;
i := 0;
while i < 4096 { arr[i] = i; i += 1; }
sum := 0;
@@ -17,7 +17,7 @@ main :: () -> s32 {
print("sum={}\n", sum);
// By-value capture is a copy: mutating it leaves the array untouched.
small : [3]s64 = .[10, 20, 30];
small : [3]i64 = .[10, 20, 30];
for small (x) { x += 100; }
print("copy-guard: {} {} {}\n", small[0], small[1], small[2]);
0

View File

@@ -6,7 +6,7 @@
#import "modules/std.sx";
main :: () -> s32 {
main :: () -> i32 {
for 0..3 (i) {
defer print("cleanup {}\n", i);
if i == 1 { break; }

View File

@@ -1,29 +1,29 @@
#import "modules/std.sx";
pair_sum :: (xs: []s64, ys: []s64) -> s64 {
pair_sum :: (xs: []i64, ys: []i64) -> i64 {
total := 0;
for xs, ys (x, y) { total += x * y; }
total
}
make :: () -> [3]s64 {
r : [3]s64 = .[7, 8, 9];
make :: () -> [3]i64 {
r : [3]i64 = .[7, 8, 9];
r
}
main :: () -> s32 {
main :: () -> i32 {
// Agra's example: a 1..5 inclusive, b open-ended following along.
for 1..=5, 0.. (a, b) { print("{}:{} ", a, b); }
print("\n");
// Index idiom replacing the old (x, i) form.
xs : [3]s64 = .[10, 20, 30];
xs : [3]i64 = .[10, 20, 30];
for xs, 0.. (x, i) { print("[{}]={} ", i, x); }
print("\n");
// Parallel slices.
a4 : [4]s64 = .[1, 2, 3, 4];
b4 : [4]s64 = .[10, 20, 30, 40];
a4 : [4]i64 = .[1, 2, 3, 4];
b4 : [4]i64 = .[10, 20, 30, 40];
print("dot={}\n", pair_sum(a4, b4));
// Arrow bodies.

View File

@@ -7,7 +7,7 @@
#import "modules/std.sx";
main :: () -> s32 {
main :: () -> i32 {
for 0<..<5 (i) { print("{} ", i); }
print("| 0<..<5\n");
for 0=..=5 (i) { print("{} ", i); }
@@ -22,7 +22,7 @@ main :: () -> s32 {
print("| 0..=5\n");
// Exclusive-start open range following a bounded first iterable.
xs : [3]s64 = .[10, 20, 30];
xs : [3]i64 = .[10, 20, 30];
for xs, 2<.. (x, i) { print("{}@{} ", x, i); }
print("| xs, 2<..\n");

View File

@@ -6,15 +6,15 @@
#import "modules/std.sx";
dump :: (s: []s64, tag: string) {
dump :: (s: []i64, tag: string) {
print("{}: ", tag);
for s (v) { print("{} ", v); }
print("(len {})\n", s.len);
}
main :: () -> s32 {
xs : [6]s64 = .[10, 11, 12, 13, 14, 15];
full : []s64 = xs[0..6];
main :: () -> i32 {
xs : [6]i64 = .[10, 11, 12, 13, 14, 15];
full : []i64 = xs[0..6];
dump(full[1..=3], "1..=3"); // 11 12 13
dump(full[0<..<4], "0<..<4"); // 11 12 13

View File

@@ -8,17 +8,17 @@
#import "modules/std.sx";
bump :: (x: s64) -> s64 { x + 1 }
bump2 :: ufcs (x: s64) -> s64 { x + 2 }
bump :: (x: i64) -> i64 { x + 1 }
bump2 :: ufcs (x: i64) -> i64 { x + 2 }
bump3 :: ufcs bump;
Counter :: struct { n: s64; }
inc :: ufcs (c: *Counter, by: s64) -> s64 { c.n += by; c.n }
Counter :: struct { n: i64; }
inc :: ufcs (c: *Counter, by: i64) -> i64 { c.n += by; c.n }
gfirst :: ufcs (xs: []$T) -> T { xs[0] }
main :: () {
f : s64 = 40;
f : i64 = 40;
print("marked: {}\n", f.bump2()); // 42
print("alias: {}\n", f.bump3()); // 41
print("direct: {}\n", bump(f)); // 41 — plain fn, direct
@@ -29,7 +29,7 @@ main :: () {
print("ptr-recv: {}\n", c.inc(5)); // 15 — auto address-of receiver
arr := .[7, 8, 9];
xs : []s64 = arr;
xs : []i64 = arr;
print("generic-dot: {}\n", xs.gfirst()); // 7
print("generic-direct: {}\n", gfirst(xs)); // 7 — plan types it s64, not a T stub
print("generic-direct: {}\n", gfirst(xs)); // 7 — plan types it i64, not a T stub
}

View File

@@ -7,19 +7,19 @@
#import "modules/std.sx";
Point :: struct {
x: s64;
scaled :: (self: Point, k: s64 = 2) -> s64 { return self.x * k; }
x: i64;
scaled :: (self: Point, k: i64 = 2) -> i64 { return self.x * k; }
}
bump :: ufcs (p: Point, by: s64 = 10) -> s64 { return p.x + by; }
bump :: ufcs (p: Point, by: i64 = 10) -> i64 { return p.x + by; }
sum_var :: (..xs: []s64) -> s64 {
sum_var :: (..xs: []i64) -> i64 {
t := 0;
for xs (x) { t = t + x; }
return t;
}
here :: (loc: Source_Location = #caller_location) -> s64 { return loc.line; }
here :: (loc: Source_Location = #caller_location) -> i64 { return loc.line; }
main :: () {
p := Point.{ x = 5 };

View File

@@ -13,7 +13,7 @@
#import "modules/std.sx";
checksum :: () -> s64 {
checksum :: () -> i64 {
buf : [65536]u8 = ---;
i := 0;
while i < 65536 {
@@ -29,14 +29,14 @@ checksum :: () -> s64 {
return sum;
}
big :: () -> s64 {
buf : [131072]s64 = ---;
big :: () -> i64 {
buf : [131072]i64 = ---;
buf[0] = 11;
buf[131071] = 31;
return buf[0] + buf[131071];
}
main :: () -> s32 {
main :: () -> i32 {
out(int_to_string(checksum()));
out("\n");
out(int_to_string(big()));

View File

@@ -5,7 +5,7 @@ Vec4 :: struct {
Complex :: struct {
foo : enum {
S: s32;
S: i32;
B: struct {
val: string;
};

View File

@@ -1,7 +1,7 @@
#import "modules/std.sx";
SPECIAL_VALUE :u8: 42;
resolve :: (x: u8) -> s32 {
resolve :: (x: u8) -> i32 {
return 12 + x;
}
@@ -9,7 +9,7 @@ Foo :: struct {
a : u2; // this will have 0 as default
b : u8 = SPECIAL_VALUE;
c : u8 = ---; // default for c is undefined
d : u8 = #run xx resolve(5); // converts s32 to u8
d : u8 = #run xx resolve(5); // converts i32 to u8
}
main :: () {
@@ -38,6 +38,6 @@ Pack :: struct {
c: u8;
d: u32;
f: u64;
v: s32;
v: i32;
x: f32;
}

View File

@@ -1,11 +1,11 @@
#import "modules/std.sx";
Point :: struct {
x, y: s32;
x, y: i32;
}
Color :: struct {
r, g, b: s32;
r, g, b: i32;
}
main :: () {

View File

@@ -2,7 +2,7 @@
Overlay :: union {
f: f32;
i: s32;
i: i32;
}
Vec2 :: union {

View File

@@ -54,12 +54,12 @@ main :: () {
// Explicit values
w :WindowFlags = .vsync | .resizable;
print("\nwindow: {}\n", w);
print("raw value: {}\n", cast(s64) w);
print("raw value: {}\n", cast(i64) w);
// Backing type on plain enums
c :Color = .blue;
print("\ncolor: {}\n", c);
print("raw: {}\n", cast(s64) c);
print("raw: {}\n", cast(i64) c);
// Bitwise ops work on plain integers too
x := 0xFF & 0x0F;

View File

@@ -3,16 +3,16 @@
#import "modules/std.sx";
g_add : s64 = 10;
g_sub : s64 = 10;
g_mul : s64 = 10;
g_div : s64 = 100;
g_mod : s64 = 10;
g_and : s64 = 0xff;
g_or : s64 = 0x0f;
g_xor : s64 = 0xff;
g_shl : s64 = 1;
g_shr : s64 = 256;
g_add : i64 = 10;
g_sub : i64 = 10;
g_mul : i64 = 10;
g_div : i64 = 100;
g_mod : i64 = 10;
g_and : i64 = 0xff;
g_or : i64 = 0x0f;
g_xor : i64 = 0xff;
g_shl : i64 = 1;
g_shr : i64 = 256;
main :: () -> void {
// += repeated: should accumulate, not reset

View File

@@ -5,7 +5,7 @@
#import "modules/std.sx";
main :: () -> void {
x : s64 = 42;
x : i64 = 42;
// OK: comparison in statement context
if x != 0 { out("ok\n"); }

View File

@@ -5,19 +5,19 @@
#import "modules/std.sx";
// 40-byte struct — triggers the bug.
// Shrink to [4]s64 (32 bytes) and the bug goes away.
// Shrink to [4]i64 (32 bytes) and the bug goes away.
BigNode :: struct {
data: [5]s64; // 40 bytes
data: [5]i64; // 40 bytes
}
Tree :: struct {
nodes: List(BigNode); // items(8) + len(8) + cap(8) = 24 bytes
generation: s64; // 8 bytes — total 32 bytes
generation: i64; // 8 bytes — total 32 bytes
}
Container :: struct {
tree: Tree;
sentinel: s64;
sentinel: i64;
do_work :: (self: *Container) {
self.tree.nodes.items = xx 0; // BUG: corrupts self.sentinel

View File

@@ -3,7 +3,7 @@
#import "modules/std.sx";
g_counter : s64 = 0;
g_counter : i64 = 0;
tick :: () {
g_counter += 1;
@@ -14,7 +14,7 @@ main :: () -> void {
// Test 1: += always produces 1 (BUG)
out("--- Test 1: += (broken) ---\n");
out("Expected: 1, 2, 3\n");
i : s64 = 0;
i : i64 = 0;
while i < 3 {
tick();
i += 1;

View File

@@ -3,7 +3,7 @@
#import "modules/std.sx";
VALS : [4]s32 = .[-2, -1, 42, 99];
VALS : [4]i32 = .[-2, -1, 42, 99];
main :: () {
out("VALS: ");

View File

@@ -5,7 +5,7 @@
Foo :: struct {
running: bool = true;
x: s32 = 42;
x: i32 = 42;
name: string = "default";
}

View File

@@ -10,7 +10,7 @@ ok :: () -> Handle { 0 }
g : Handle = 0;
main :: () -> s32 {
main :: () -> i32 {
g = ok();
if g == 0 then 0 else 1
}

View File

@@ -1,7 +1,7 @@
// Pre-fix: `resolveType(null)` silently returned `.s64`, so a top-level
// var without a type annotation got typed as `s64` regardless of what
// Pre-fix: `resolveType(null)` silently returned `.i64`, so a top-level
// var without a type annotation got typed as `i64` regardless of what
// the initializer was. For `g_pi := 3.14;` this meant the float literal
// was assigned to an s64 slot, producing a wrong value at runtime or
// was assigned to an i64 slot, producing a wrong value at runtime or
// the wrong codegen shape.
//
// After the fix `lowerVarDecl` at the top level mirrors the local-scope
@@ -9,11 +9,11 @@
// the initializer's type. Mirrors how `:=` already worked for locals.
#import "modules/std.sx";
g_count := 42; // inferred s64
g_pi := 3.14; // inferred f64 — used to silently become s64
g_count := 42; // inferred i64
g_pi := 3.14; // inferred f64 — used to silently become i64
g_flag := true; // inferred bool
main :: () -> s32 {
main :: () -> i32 {
print("count = {}\n", g_count);
print("pi = {}\n", g_pi);
print("flag = {}\n", g_flag);

View File

@@ -7,7 +7,7 @@
//
// This test exercises the builder directly (no `#insert`, no impl
// wiring) — three pack shapes through the same `void`-returning
// wrapper plus one non-void `s32`-returning wrapper to pin the
// wrapper plus one non-void `i32`-returning wrapper to pin the
// `return typed_fn(...)` branch. The expected output captures the
// generated source verbatim so any formatting drift surfaces here
// rather than as a downstream compile error inside the eventual
@@ -20,15 +20,15 @@ preview_void :: (..$args) -> string {
return build_block_convert($args, void);
}
preview_s32 :: (..$args) -> string {
return build_block_convert($args, s32);
preview_i32 :: (..$args) -> string {
return build_block_convert($args, i32);
}
run_all :: () {
print("--- void / 0 args ---\n{}\n", preview_void());
print("--- void / bool ---\n{}\n", preview_void(true));
print("--- void / s64, string ---\n{}\n", preview_void(42, "hi"));
print("--- s32 / f64 ---\n{}\n", preview_s32(3.14));
print("--- void / i64, string ---\n{}\n", preview_void(42, "hi"));
print("--- i32 / f64 ---\n{}\n", preview_i32(3.14));
}
#run run_all();

View File

@@ -2,7 +2,7 @@
// `align_of` accept pointer (`*T`), optional (`?T`), array (`[N]T`),
// function (`(A) -> B`), and tuple (`(A, B)`) types directly. Also
// const-decl RHS aliases through the same forms (`Ptr :: *u8;` etc).
// Same shape as the existing `size_of(s32)` baseline path.
// Same shape as the existing `size_of(i32)` baseline path.
#import "modules/std.sx";
@@ -10,9 +10,9 @@
Ptr :: *u8;
Maybe :: ?u8;
Arr :: [3]u8;
Cb :: (s32) -> s32;
Cb :: (i32) -> i32;
main :: () -> s32 {
main :: () -> i32 {
// Direct: parser fix for *T, ?T + existing [N]T path.
print("size_of(*u8) = {}\n", size_of(*u8));
print("align_of(*u8) = {}\n", align_of(*u8));
@@ -20,10 +20,10 @@ main :: () -> s32 {
print("size_of([3]u8) = {}\n", size_of([3]u8));
// Function-type literal in expression position.
print("size_of((s32)->s32) = {}\n", size_of((s32) -> s32));
print("size_of((i32)->i32) = {}\n", size_of((i32) -> i32));
// Tuple literal reinterpreted as tuple type at the type-demanding site.
print("size_of((s32, s32)) = {}\n", size_of((s32, s32)));
print("size_of((i32, i32)) = {}\n", size_of((i32, i32)));
// Aliases.
print("size_of(Ptr) = {}\n", size_of(Ptr));

View File

@@ -1,19 +1,19 @@
// Type alias resolution through `size_of` / `align_of` — a const-decl
// alias (`MyInt :: s32;`, `MyChain :: MyInt;`, `WideAlias :: Wide;`)
// alias (`MyInt :: i32;`, `MyChain :: MyInt;`, `WideAlias :: Wide;`)
// resolves through `type_alias_map` when used as a `$T: Type` argument.
// Covers chains and struct-name aliases, not just structural-type
// aliases (those land in `examples/182-compound-type-in-expression.sx`).
#import "modules/std.sx";
MyInt :: s32;
MyInt :: i32;
MyChain :: MyInt;
Wide :: struct { a: s64; b: s64; }
Wide :: struct { a: i64; b: i64; }
WideAlias :: Wide;
main :: () -> s32 {
print("direct s32: {}\n", size_of(s32));
print("alias s32: {}\n", size_of(MyInt));
print("chain s32: {}\n", size_of(MyChain));
main :: () -> i32 {
print("direct i32: {}\n", size_of(i32));
print("alias i32: {}\n", size_of(MyInt));
print("chain i32: {}\n", size_of(MyChain));
print("align alias: {}\n", align_of(MyInt));
print("align chain: {}\n", align_of(MyChain));
print("size struct-alias: {}\n", size_of(WideAlias));

View File

@@ -17,7 +17,7 @@
g_s: string = "";
main :: () -> s32 {
main :: () -> i32 {
cl := (s: string) => { g_s = s; };
b : Block = xx cl;
invoke_fn : (*Block, string) -> void callconv(.c) = xx b.invoke;

View File

@@ -12,7 +12,7 @@
// ── Test fixtures ─────────────────────────────────────────────
Point :: struct { x: s32; y: s32; }
Point :: struct { x: i32; y: i32; }
Color :: enum { red; green; blue; }
@@ -24,7 +24,7 @@ identity :: ($T: Type, val: T) -> T => val;
// time fold via type_eq → const_bool → inline-if folds the
// branch away).
describe :: ($T: Type) -> string {
inline if type_eq(T, s64) { return "int64"; }
inline if type_eq(T, i64) { return "int64"; }
inline if type_eq(T, string) { return "text"; }
inline if type_eq(T, bool) { return "boolean"; }
return "other";
@@ -34,7 +34,7 @@ describe :: ($T: Type) -> string {
type_list :: (..$args) -> string {
list := $args;
s := "[";
i : s64 = 0;
i : i64 = 0;
while i < list.len {
if i > 0 { s = concat(s, ", "); }
s = concat(s, type_name(list[i]));
@@ -46,21 +46,21 @@ type_list :: (..$args) -> string {
// Type stored in a struct field.
TypeHolder :: struct { t: Type; }
main :: () -> s32 {
main :: () -> i32 {
// ── 1. Type literal equality ────────────────────────────
print("=== 1. literal == ===\n");
print("s64 == s64: {}\n", s64 == s64);
print("s64 == string: {}\n", s64 == string);
print("i64 == i64: {}\n", i64 == i64);
print("i64 == string: {}\n", i64 == string);
print("*u8 == *u8: {}\n", *u8 == *u8);
print("?s64 == ?s64: {}\n", ?s64 == ?s64);
print("?s64 == ?s32: {}\n", ?s64 == ?s32);
print("?i64 == ?i64: {}\n", ?i64 == ?i64);
print("?i64 == ?i32: {}\n", ?i64 == ?i32);
// ── 2. type_of(value) ───────────────────────────────────
print("=== 2. type_of(value) == T ===\n");
a : s64 = 42;
a : i64 = 42;
b : f64 = 3.14;
s : string = "hi";
print("type_of(a) == s64: {}\n", type_of(a) == s64);
print("type_of(a) == i64: {}\n", type_of(a) == i64);
print("type_of(b) == f64: {}\n", type_of(b) == f64);
print("type_of(s) == string: {}\n", type_of(s) == string);
print("type_of(a) == f64: {}\n", type_of(a) == f64);
@@ -77,7 +77,7 @@ main :: () -> s32 {
// ── 4. type_name on literals + variables ────────────────
print("=== 4. type_name ===\n");
print("type_name(s64): {}\n", type_name(s64));
print("type_name(i64): {}\n", type_name(i64));
print("type_name(*u8): {}\n", type_name(*u8));
print("type_name(Point): {}\n", type_name(Point));
print("type_name(Color): {}\n", type_name(Color));
@@ -86,39 +86,39 @@ main :: () -> s32 {
// ── 5. Print Type values directly ───────────────────────
print("=== 5. print Type values ===\n");
print("literal: {}\n", s64);
print("literal: {}\n", i64);
t = string;
print("var: {}\n", t);
print("type_of(b): {}\n", type_of(b));
// ── 6. Generic dispatch via $T: Type ────────────────────
print("=== 6. generic dispatch ===\n");
print("describe(s64): {}\n", describe(s64));
print("describe(i64): {}\n", describe(i64));
print("describe(string): {}\n", describe(string));
print("describe(bool): {}\n", describe(bool));
print("describe(f64): {}\n", describe(f64));
// ── 7. identity(T, val) ─────────────────────────────────
print("=== 7. identity($T, val) ===\n");
print("identity(s64, 7): {}\n", identity(s64, 7));
print("identity(i64, 7): {}\n", identity(i64, 7));
print("identity(string, hi): {}\n", identity(string, "hi"));
print("identity(bool, true): {}\n", identity(bool, true));
// ── 8. Comptime-generated struct (Wrap($T)) ─────────────
print("=== 8. Wrap($T) ===\n");
w_int := Wrap(s64).{ v = 42 };
w_int := Wrap(i64).{ v = 42 };
w_str := Wrap(string).{ v = "wrapped" };
print("Wrap(s64).v: {}\n", w_int.v);
print("Wrap(i64).v: {}\n", w_int.v);
print("Wrap(string).v: {}\n", w_str.v);
// ── 9. Reflection builtins on Types ─────────────────────
print("=== 9. reflection on Type ===\n");
print("size_of(s64): {}\n", size_of(s64));
print("size_of(i64): {}\n", size_of(i64));
print("size_of(*u8): {}\n", size_of(*u8));
print("align_of(f64): {}\n", align_of(f64));
print("field_count(Point): {}\n", field_count(Point));
print("type_eq(s64, s64): {}\n", type_eq(s64, s64));
print("type_eq(s64, string): {}\n", type_eq(s64, string));
print("type_eq(i64, i64): {}\n", type_eq(i64, i64));
print("type_eq(i64, string): {}\n", type_eq(i64, string));
// ── 10. Type pack (..$args) walking ─────────────────────
print("=== 10. ..$args walking ===\n");
@@ -129,15 +129,15 @@ main :: () -> s32 {
// ── 11. Type in struct field ────────────────────────────
print("=== 11. Type in struct field ===\n");
h := TypeHolder.{ t = s64 };
print("h.t == s64: {}\n", h.t == s64);
h := TypeHolder.{ t = i64 };
print("h.t == i64: {}\n", h.t == i64);
print("h.t == string: {}\n", h.t == string);
print("type_name(h.t): {}\n", type_name(h.t));
// ── 12. Compound type literals ──────────────────────────
print("=== 12. compound literals ===\n");
print("type_name(*Point): {}\n", type_name(*Point));
print("type_name([4]s32): {}\n", type_name([4]s32));
print("type_name([4]i32): {}\n", type_name([4]i32));
print("type_name([]bool): {}\n", type_name([]bool));
print("type_name(?f64): {}\n", type_name(?f64));
@@ -146,13 +146,13 @@ main :: () -> s32 {
// ** stdout **
// === 1. literal == ===
// s64 == s64: true
// s64 == string: false
// i64 == i64: true
// i64 == string: false
// *u8 == *u8: true
// ?s64 == ?s64: true
// ?s64 == ?s32: false
// ?i64 == ?i64: true
// ?i64 == ?i32: false
// === 2. type_of(value) == T ===
// type_of(a) == s64: true
// type_of(a) == i64: true
// type_of(b) == f64: true
// type_of(s) == string: true
// type_of(a) == f64: false
@@ -162,45 +162,45 @@ main :: () -> s32 {
// after reassign t == string: true
// t == bool: true
// === 4. type_name ===
// type_name(s64): s64
// type_name(i64): i64
// type_name(*u8): *u8
// type_name(Point): Point
// type_name(Color): Color
// type_name(t): f64
// === 5. print Type values ===
// literal: s64
// literal: i64
// var: string
// type_of(b): f64
// === 6. generic dispatch ===
// describe(s64): int64
// describe(i64): int64
// describe(string): text
// describe(bool): boolean
// describe(f64): other
// === 7. identity($T, val) ===
// identity(s64, 7): 7
// identity(i64, 7): 7
// identity(string, hi): hi
// identity(bool, true): true
// === 8. Wrap($T) ===
// Wrap(s64).v: 42
// Wrap(i64).v: 42
// Wrap(string).v: wrapped
// === 9. reflection on Type ===
// size_of(s64): 8
// size_of(i64): 8
// size_of(*u8): 8
// align_of(f64): 8
// field_count(Point): 2
// type_eq(s64, s64): true
// type_eq(s64, string): false
// type_eq(i64, i64): true
// type_eq(i64, string): false
// === 10. ..$args walking ===
// type_list(): []
// type_list(1): [s64]
// type_list(1, "x"): [s64, string]
// type_list(1): [i64]
// type_list(1, "x"): [i64, string]
// type_list(true, 3.14): [bool, f64]
// === 11. Type in struct field ===
// h.t == s64: true
// h.t == i64: true
// h.t == string: false
// type_name(h.t): s64
// type_name(h.t): i64
// === 12. compound literals ===
// type_name(*Point): *Point
// type_name([4]s32): [4]s32
// type_name([4]i32): [4]i32
// type_name([]bool): []bool
// type_name(?f64): ?f64

View File

@@ -2,16 +2,16 @@
// return, and operators. Regression for the tuple-construction bug where
// an inferred `:=` tuple literal lowered its element values under the
// enclosing fn's (narrower) return `target_type`, mismatching the
// independently-inferred s64 field types and yielding garbage on read.
// independently-inferred i64 field types and yielding garbage on read.
#import "modules/std.sx";
Box :: struct { xs: (s32, s32); }
Box :: struct { xs: (i32, i32); }
swap :: (a: s64, b: s64) -> (s64, s64) { (b, a) }
fst :: (t: (s64, s64)) -> s64 { t.0 }
swap :: (a: i64, b: i64) -> (i64, i64) { (b, a) }
fst :: (t: (i64, i64)) -> i64 { t.0 }
main :: () -> s32 {
main :: () -> i32 {
// Inferred positional tuple + numeric field access.
pair := (40, 2);
print("pair {} {}\n", pair.0, pair.1);
@@ -21,8 +21,8 @@ main :: () -> s32 {
print("named {} {} {}\n", named.x, named.0, named.1);
// Element into a typed local (access path, not just print).
a : s64 = pair.0;
b : s64 = pair.1;
a : i64 = pair.0;
b : i64 = pair.1;
print("locals {} {}\n", a, b);
// Tuple-typed struct field: store a tuple value, read both elements.
@@ -46,9 +46,9 @@ main :: () -> s32 {
print("mem {}\n", 3 in (1, 2, 3));
print("lex {}\n", (1, 2) < (1, 3));
// Mixed-size fields: a tuple with both an s64 and a string (16-byte fat
// Mixed-size fields: a tuple with both an i64 and a string (16-byte fat
// pointer). Field types are tracked per-position, so reading each back is
// typed correctly (s64 prints as a number, string as text).
// typed correctly (i64 prints as a number, string as text).
mixed := (42, "hi");
print("mixed {} {}\n", mixed.0, mixed.1);
0

View File

@@ -7,15 +7,15 @@
#import "modules/std.sx";
main :: () -> s32 {
main :: () -> i32 {
// Positional element assignment.
a : (s32, string) = ---;
a : (i32, string) = ---;
a.0 = 11;
a.1 = "x";
print("a: {} {}\n", a.0, a.1);
// Named tuple: write + read by name, and read by position.
p : (x: s32, y: string) = ---;
p : (x: i32, y: string) = ---;
p.x = 22;
p.y = "y";
print("p: x={} y={} .0={}\n", p.x, p.y, p.0);

View File

@@ -4,7 +4,7 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
Point :: struct { x, y: s32; }
Point :: struct { x, y: i32; }
Color :: enum { red; green; blue; }
@@ -16,7 +16,7 @@ Shape :: enum {
Overlay :: union {
f: f32;
i: s32;
i: i32;
}
Vec2 :: union {
@@ -25,27 +25,27 @@ Vec2 :: union {
}
Defaults :: struct {
a: s32;
b: s32 = 99;
c: s32 = ---;
a: i32;
b: i32 = 99;
c: i32 = ---;
}
MyFloat :: f64;
Status :: enum u8 { ok; err; timeout; }
add :: (a: s32, b: s32) -> s32 { a + b }
add :: (a: i32, b: i32) -> i32 { a + b }
mul :: (a: s32, b: s32) -> s32 { a * b }
mul :: (a: i32, b: i32) -> i32 { a * b }
vec3 :: (x: f32, y: f32, z: f32) -> Vector(3, f32) {
.[x, y, z]
}
// Global variable for address-of test
g_smoke_val : s32 = 42;
g_smoke_val : i32 = 42;
write_to_ptr :: (p: *s32) {
write_to_ptr :: (p: *i32) {
p.* = 99;
}
@@ -57,15 +57,15 @@ main :: () {
print("=== 3. Types ===\n");
// Primitive types
v_s8 : s8 = 127;
v_s16 : s16 = 32000;
v_s32 : s32 = 100000;
v_i8 : i8 = 127;
v_i16 : i16 = 32000;
v_i32 : i32 = 100000;
v_u8 : u8 = 255;
v_u16 : u16 = 65000;
v_u32 : u32 = 4000000;
print("s8: {}\n", v_s8);
print("s16: {}\n", v_s16);
print("s32: {}\n", v_s32);
print("i8: {}\n", v_i8);
print("i16: {}\n", v_i16);
print("i32: {}\n", v_i32);
print("u8: {}\n", v_u8);
print("u16: {}\n", v_u16);
print("u32: {}\n", v_u32);
@@ -88,8 +88,8 @@ main :: () {
print("struct-named: {}\n", p3);
// Shorthand (variable name = field name)
x : s32 = 5;
y : s32 = 6;
x : i32 = 5;
y : i32 = 6;
p4 := Point.{ x, y };
print("struct-shorthand: {}\n", p4);
@@ -226,7 +226,7 @@ main :: () {
o : Overlay = ---;
o.f = 3.14;
print("union-f: {}\n", o.f);
// Type punning — read same bits as s32
// Type punning — read same bits as i32
print("union-i: {}\n", o.i);
// Union member promotion
@@ -237,22 +237,22 @@ main :: () {
print("promoted-data0: {}\n", uv.data[0]);
// --- Arrays ---
arr : [5]s32 = .[10, 20, 30, 40, 50];
arr : [5]i32 = .[10, 20, 30, 40, 50];
print("arr[2]: {}\n", arr[2]);
print("arr.len: {}\n", arr.len);
// Array element assignment
aa : [3]s32 = .[1, 2, 3];
aa : [3]i32 = .[1, 2, 3];
aa[1] = 99;
print("arr-assign: {}\n", aa);
// --- Slices ---
sl : []s32 = .[1, 2, 3, 4, 5];
sl : []i32 = .[1, 2, 3, 4, 5];
print("sl[0]: {}\n", sl[0]);
print("sl.len: {}\n", sl.len);
// Slice element write
sla : []s32 = .[10, 20, 30];
sla : []i32 = .[10, 20, 30];
sla[1] = 55;
print("sl-assign: {}\n", sla);
@@ -265,7 +265,7 @@ main :: () {
print("tail: {}\n", tail);
// Slice of slice
sos : []s32 = .[10, 20, 30, 40, 50];
sos : []i32 = .[10, 20, 30, 40, 50];
mid := sos[1..4];
inner := mid[0..2];
print("slice-of-slice: {}\n", inner);
@@ -289,18 +289,18 @@ main :: () {
print("auto-deref: {}\n", ptr.x);
// Many-pointer
mp : [*]s32 = @arr[0];
mp : [*]i32 = @arr[0];
print("mp[0]: {}\n", mp[0]);
print("mp[3]: {}\n", mp[3]);
// Many-pointer write
mpw : [5]s32 = .[10, 20, 30, 40, 50];
mpw_ptr : [*]s32 = @mpw[0];
mpw : [5]i32 = .[10, 20, 30, 40, 50];
mpw_ptr : [*]i32 = @mpw[0];
mpw_ptr[2] = 99;
print("mp-write: {}\n", mpw[2]);
// Pointer-null comparison
np : *s32 = null;
np : *i32 = null;
print("ptr==null: {}\n", np == null);
print("ptr!=null: {}\n", np != null);
np2 := @pv.x;
@@ -309,13 +309,13 @@ main :: () {
// Pointer to nested struct field
Inner3 :: struct { a: f32; b: f32; c: f32; }
Outer3 :: struct { key: s32; inner: Inner3; }
Outer3 :: struct { key: i32; inner: Inner3; }
out3 := Outer3.{ key = 42, inner = Inner3.{ a = 1.0, b = 2.0, c = 3.0 } };
ip3 := @out3.inner;
print("ptr-nested-field: {} {} {}\n", ip3.a, ip3.b, ip3.c);
// Store to many-pointer field must not corrupt adjacent memory
MpHolder :: struct { items: [*]s64; sentinel: s64; }
MpHolder :: struct { items: [*]i64; sentinel: i64; }
mph := MpHolder.{ items = xx 0, sentinel = 42 };
mph.items = xx 0;
print("mp-store-sentinel: {}\n", mph.sentinel);

View File

@@ -42,12 +42,12 @@ main :: () {
print("flags-all: {}\n", pall);
// Cast to int
print("flags-raw: {}\n", cast(s64) perm);
print("flags-raw: {}\n", cast(i64) perm);
// Flags with explicit values
wf : WindowFlags = .vsync | .resizable;
print("flags-explicit: {}\n", wf);
print("flags-explicit-raw: {}\n", cast(s64) wf);
print("flags-explicit-raw: {}\n", cast(i64) wf);
// --- Multi-target assignment (swap) ---
print("--- swap ---\n");
@@ -62,7 +62,7 @@ main :: () {
// Array element swap
{
sarr : [3]s64 = .[1, 2, 3];
sarr : [3]i64 = .[1, 2, 3];
sarr[0], sarr[2] = sarr[2], sarr[0];
print("arr swap: {} {}\n", sarr[0], sarr[2]);
}
@@ -87,7 +87,7 @@ main :: () {
// Destructure from function return
{
dswap :: (a: s64, b: s64) -> (s64, s64) { (b, a) }
dswap :: (a: i64, b: i64) -> (i64, i64) { (b, a) }
dx, dy := dswap(1, 2);
print("fn: {} {}\n", dx, dy);
}

View File

@@ -16,9 +16,9 @@ main :: () {
ca_a += ca_b;
print("f64+=f32: {}\n", ca_a);
ca_c : s64 = 100;
ca_d : s32 = 7;
ca_c : i64 = 100;
ca_d : i32 = 7;
ca_c -= ca_d;
print("s64-=s32: {}\n", ca_c);
print("i64-=i32: {}\n", ca_c);
}
}

View File

@@ -4,7 +4,7 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
Point :: struct { x, y: s32; }
Point :: struct { x, y: i32; }
main :: () {

View File

@@ -1,4 +1,4 @@
// A local declared with a reserved/builtin type-name spelling (`s2` is the
// A local declared with a reserved/builtin type-name spelling (`i2` is the
// arbitrary-width `sN` integer type) is rejected at the declaration site.
// Previously such a name parsed as a `.type_expr`, so address-of sites
// mis-lowered it (load-by-value to a `ptr` param → LLVM verifier abort, or a
@@ -6,8 +6,8 @@
// error at the declaration; exit 1.
#import "modules/std.sx";
main :: () -> s32 {
s2 := 42;
print("s2: {}\n", s2);
main :: () -> i32 {
i2 := 42;
print("i2: {}\n", i2);
return 0;
}

View File

@@ -11,7 +11,7 @@ main :: () {
// ========================================================
print("=== 23. Nested Arrays ===\n");
{
matrix : [2][3]s32 = .[ .[1, 2, 3], .[4, 5, 6] ];
matrix : [2][3]i32 = .[ .[1, 2, 3], .[4, 5, 6] ];
print("m[0][0]: {}\n", matrix[0][0]);
print("m[0][2]: {}\n", matrix[0][2]);
print("m[1][0]: {}\n", matrix[1][0]);

View File

@@ -9,25 +9,25 @@ main :: () {
// === 26. #using struct composition ===
print("=== 26. #using ===\n");
{
UBase :: struct { x: s32; y: s32; }
UExt :: struct { #using UBase; z: s32; }
UBase :: struct { x: i32; y: i32; }
UExt :: struct { #using UBase; z: i32; }
e := UExt.{ x = 1, y = 2, z = 3 };
print("using-x: {}\n", e.x);
print("using-y: {}\n", e.y);
print("using-z: {}\n", e.z);
// #using in middle position
UHeader :: struct { version: s32; }
UPacket :: struct { id: s32; #using UHeader; payload: s32; }
UHeader :: struct { version: i32; }
UPacket :: struct { id: i32; #using UHeader; payload: i32; }
p := UPacket.{ id = 10, version = 42, payload = 99 };
print("pkt-id: {}\n", p.id);
print("pkt-ver: {}\n", p.version);
print("pkt-pay: {}\n", p.payload);
// Multiple #using
UPos :: struct { px: s32; py: s32; }
UCol :: struct { r: s32; g: s32; }
USprite :: struct { #using UPos; #using UCol; scale: s32; }
UPos :: struct { px: i32; py: i32; }
UCol :: struct { r: i32; g: i32; }
USprite :: struct { #using UPos; #using UCol; scale: i32; }
s := USprite.{ px = 10, py = 20, r = 255, g = 128, scale = 1 };
print("sprite-px: {}\n", s.px);
print("sprite-r: {}\n", s.r);

View File

@@ -20,7 +20,7 @@ main :: () {
single := (42,);
print("{}\n", single.0);
zeroed : (s32, s32) = ---;
zeroed : (i32, i32) = ---;
print("{}\n", zeroed.0);
print("{}\n", zeroed.1);
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,28 +4,28 @@
#import "modules/std/test.sx";
pkg :: #import "tests/fixtures/testpkg";
Point :: struct { x, y: s32; }
Point :: struct { x, y: i32; }
add :: (a: s32, b: s32) -> s32 { a + b }
add :: (a: i32, b: i32) -> i32 { a + b }
Counter :: protocol {
inc :: ();
get :: () -> s32;
get :: () -> i32;
}
Summable :: protocol {
sum :: () -> s32;
sum :: () -> i32;
}
SimpleCounter :: struct { val: s32; }
SimpleCounter :: struct { val: i32; }
impl Counter for SimpleCounter {
inc :: (self: *SimpleCounter) { self.val += 1; }
get :: (self: *SimpleCounter) -> s32 { self.val }
get :: (self: *SimpleCounter) -> i32 { self.val }
}
impl Summable for Point {
sum :: (self: *Point) -> s32 { self.x + self.y }
sum :: (self: *Point) -> i32 { self.x + self.y }
}
// Phase 2: #inline protocol for dynamic dispatch
@@ -37,7 +37,7 @@ Pair :: struct ($T: Type) {
}
impl Summable for Pair($T) {
sum :: (self: *Pair(T)) -> s32 {
sum :: (self: *Pair(T)) -> i32 {
xx self.a + xx self.b
}
}
@@ -46,10 +46,10 @@ impl Summable for Pair($T) {
// Init block test struct
Builder :: struct {
total: s32;
count: s32;
total: i32;
count: i32;
add :: (self: *Builder, val: s32) {
add :: (self: *Builder, val: i32) {
self.total += val;
self.count += 1;
}
@@ -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) -> i32 { c.inc(); c.inc(); c.get() }
result := use_counter(SimpleCounter.{ val = 0 } {
self.val = 50;
});
@@ -138,10 +138,10 @@ main :: () {
// SM2: Shorthand in variable declaration with explicit type
{
Pair :: struct {
a: s64;
b: s64;
a: i64;
b: i64;
make :: (a: s64, b: s64) -> Pair {
make :: (a: i64, b: i64) -> Pair {
Pair.{ a = a, b = b }
}
}
@@ -188,13 +188,13 @@ main :: () {
c : usize = a + 8;
print("usize+8: {}\n", c);
// coercion from s32
x : s32 = 10;
// coercion from i32
x : i32 = 10;
y : usize = xx x;
print("s32->usize: {}\n", y);
print("i32->usize: {}\n", y);
// coercion to s64
z : s64 = xx a;
print("usize->s64: {}\n", z);
// coercion to i64
z : i64 = xx a;
print("usize->i64: {}\n", z);
}
}

View File

@@ -1,6 +1,6 @@
// Forward identifier type alias — an alias whose target is declared LATER
// in the file resolves the same as an ordered one. `MyChain :: MyInt;`
// appears before `MyInt :: s32;`, yet `MyChain` resolves to `s32` and a
// appears before `MyInt :: i32;`, yet `MyChain` resolves to `i32` and a
// forward chain (`A :: B; B :: C; C :: u8;`) converges too.
// Regression (issue 0069): the scan only registered identifier aliases whose
// target was already known, so a forward alias was falsely flagged
@@ -8,17 +8,17 @@
#import "modules/std.sx";
MyChain :: MyInt;
MyInt :: s32;
MyInt :: i32;
A :: B;
B :: C;
C :: u8;
main :: () -> s32 {
main :: () -> i32 {
v: MyChain = 7;
n: A = 3;
print("chain s32: {}\n", size_of(MyChain));
print("chain i32: {}\n", size_of(MyChain));
print("forward u8: {}\n", size_of(A));
print("v + n: {}\n", v + cast(s32) n);
print("v + n: {}\n", v + cast(i32) n);
return v;
}

View File

@@ -1,6 +1,6 @@
// Forward identifier type alias as a TOP-LEVEL annotation — a global var
// and a typed module constant whose annotation is a forward alias
// (`A :: B; B :: s32;`) resolve to the alias target, the same as the
// (`A :: B; B :: i32;`) resolve to the alias target, the same as the
// ordered form, instead of a fabricated stub.
// Regression (issue 0070): top-level global / typed-const annotations were
// resolved inside the scan loop BEFORE the forward-alias fixpoint ran, so
@@ -10,12 +10,12 @@
#import "modules/std.sx";
A :: B;
B :: s32;
B :: i32;
g : A = 7;
K : A : 35;
main :: () -> s32 {
main :: () -> i32 {
print("global g: {}\n", g);
print("const K: {}\n", K);
return g + K;

View File

@@ -1,18 +1,18 @@
// Top-level global initialized from a module constant copies the constant's
// value (not a silent zero). `K : A : 42; g : A = K;` resolves the forward
// alias `A` to `s32` and materializes `g`'s static initializer from `K`.
// alias `A` to `i32` and materializes `g`'s static initializer from `K`.
// Regression (issue 0071): `registerTopLevelGlobal`'s init_val switch only
// handled literals/array/struct literals; an identifier initializer fell
// through to a null payload and the global silently zero-initialized.
#import "modules/std.sx";
A :: B;
B :: s32;
B :: i32;
K : A : 42;
g : A = K;
main :: () -> s32 {
main :: () -> i32 {
print("g={}\n", g);
return g;
}

View File

@@ -7,14 +7,14 @@
// type-named identifiers).
#import "modules/std.sx";
Hasher :: struct { total: s64 = 0; count: s64 = 0; }
Hasher :: struct { total: i64 = 0; count: i64 = 0; }
update :: ufcs (self: *Hasher, n: s64) {
update :: ufcs (self: *Hasher, n: i64) {
self.total += n;
self.count += 1;
}
main :: () -> s32 {
main :: () -> i32 {
hasher := Hasher.{ total = 0, count = 0 };
update(@hasher, 10); // explicit address-of receiver
hasher.update(20); // autoref receiver

View File

@@ -9,14 +9,14 @@
#import "modules/std.sx";
g : [3]s64 = .[10, 20, 30];
g : [3]i64 = .[10, 20, 30];
Pair :: struct { a: s64; b: s64; }
Pair :: struct { a: i64; b: i64; }
gp : [2]Pair = .[ .{ a = 1, b = 2 }, .{ a = 3, b = 4 } ];
grid : [2][3]s64 = .[ .[0, 0, 0], .[0, 0, 0] ];
grid : [2][3]i64 = .[ .[0, 0, 0], .[0, 0, 0] ];
write_global :: (i: s64, v: s64) { g[i] = v; }
write_global :: (i: i64, v: i64) { g[i] = v; }
main :: () {
// Scalar global array — const index.
@@ -39,7 +39,7 @@ main :: () {
print("gp[0]={},{}\n", gp[0].a, gp[0].b); // 10,20
print("gp[j]={},{}\n", gp[j].a, gp[j].b); // 30,40
// Nested-array global — element is [3]s64, recursive indexed lvalue.
// Nested-array global — element is [3]i64, recursive indexed lvalue.
grid[1][2] = 7;
r := 0;
grid[r][0] = 5;

View File

@@ -11,8 +11,8 @@
#import "modules/std.sx";
Pair :: struct { a: s64; b: s64; }
WithArr :: struct { id: s64; xs: [3]s64; }
Pair :: struct { a: i64; b: i64; }
WithArr :: struct { id: i64; xs: [3]i64; }
// global array of struct literals
pairs : [2]Pair = .[ .{ a = 1, b = 2 }, .{ a = 3, b = 4 } ];

View File

@@ -7,19 +7,19 @@
// `.null_literal` arm, so a `null` in a pointer field made the whole aggregate
// look non-constant and the global was rejected with "must be initialized by a
// compile-time constant". The fix serializes a null literal to a constant zero
// pointer (the same way a top-level pointer global `p : *s64 = null;` does)
// pointer (the same way a top-level pointer global `p : *i64 = null;` does)
// while still rejecting genuinely non-constant fields (see diagnostics 1126).
#import "modules/std.sx";
Box :: struct { p: *s64; marker: s64; }
Inner :: struct { q: *s64; tag: s64; }
Outer :: struct { inner: Inner; label: s64; }
Box :: struct { p: *i64; marker: i64; }
Inner :: struct { q: *i64; tag: i64; }
Outer :: struct { inner: Inner; label: i64; }
// array-of-struct with null pointer fields + scalar neighbors
boxes : [2]Box = .[ .{ p = null, marker = 11 }, .{ p = null, marker = 22 } ];
// global array of all-null pointers
ptrs : [3]*s64 = .[ null, null, null ];
ptrs : [3]*i64 = .[ null, null, null ];
// nested: struct containing a struct with a null pointer field
nested : [2]Outer = .[
.{ inner = .{ q = null, tag = 1 }, label = 100 },

View File

@@ -19,7 +19,7 @@ Color :: enum u8 { red; green; blue; }
Code :: enum u16 { ok :: 200; not_found :: 404; teapot :: 418; }
Pair :: struct { a: Color; b: Color; }
Row :: struct { status: Code; pad: s64; }
Row :: struct { status: Code; pad: i64; }
// scalar enum global
chosen : Color = .green;

View File

@@ -15,18 +15,18 @@
N :: 4;
M :: 3;
P :: struct { x: s64; y: s64; }
P :: struct { x: i64; y: i64; }
// Type aliases whose dimension is the named const N (stateless registration).
Arr :: [N]s64;
Arr :: [N]i64;
SArr :: [N]string;
// Inline union field with a named-const dimension (stateless registration).
U :: union { a: [N]s64; tag: s64; }
U :: union { a: [N]i64; tag: i64; }
main :: () {
// Scalar elements (direct local): store then read back.
a : [N]s64 = ---;
a : [N]i64 = ---;
a[0] = 7;
a[3] = 42;
print("scalar a0={} a3={}\n", a[0], a[3]);
@@ -35,7 +35,7 @@ main :: () {
s : [N]string = ---;
s[0] = "hi";
s[1] = "yo";
print("string s0={} s1={}\n", s[0], s[1]);
print("string i0={} i1={}\n", s[0], s[1]);
// Struct elements (direct local).
ps : [N]P = ---;
@@ -43,7 +43,7 @@ main :: () {
ps[2] = P.{ x = 5, y = 6 };
print("struct p0x={} p0y={} p2x={}\n", ps[0].x, ps[0].y, ps[2].x);
// Type-alias dimension (scalar): same layout as the direct `[N]s64`.
// Type-alias dimension (scalar): same layout as the direct `[N]i64`.
aa : Arr = ---;
aa[0] = 11;
aa[3] = 99;
@@ -53,10 +53,10 @@ main :: () {
sa : SArr = ---;
sa[0] = "al";
sa[2] = "ok";
print("alias s0={} s2={}\n", sa[0], sa[2]);
print("alias i0={} i2={}\n", sa[0], sa[2]);
// Nested fixed array `[N][M]s64`: both dimensions are named consts.
grid : [N][M]s64 = ---;
// Nested fixed array `[N][M]i64`: both dimensions are named consts.
grid : [N][M]i64 = ---;
grid[0][0] = 1;
grid[3][2] = 8;
print("nested g00={} g32={}\n", grid[0][0], grid[3][2]);

View File

@@ -7,14 +7,14 @@
// bytes and returned garbage (0).
#import "modules/std.sx";
count_nope :: (xs: []string) -> s64 {
count_nope :: (xs: []string) -> i64 {
n := 0;
i := 0;
while i < xs.len { if xs[i] == "nope" { n += 1; } i += 1; }
return n;
}
sum :: (xs: []s64) -> s64 {
sum :: (xs: []i64) -> i64 {
s := 0;
i := 0;
while i < xs.len { s += xs[i]; i += 1; }
@@ -29,6 +29,6 @@ main :: () {
// numeric slice: direct literal vs local-bound — both sum to 100.
print("num direct={}\n", sum(.[10, 20, 30, 40]));
nums : []s64 = .[10, 20, 30, 40];
nums : []i64 = .[10, 20, 30, 40];
print("num local={}\n", sum(nums));
}

View File

@@ -1,5 +1,5 @@
// A nested array/slice literal (`.[.[1, 2], .[3, 4]]`) at an expected slice-of-
// slices type (`[][]s64`) materializes each inner `[N]T` literal as a real `[]T`
// slices type (`[][]i64`) materializes each inner `[N]T` literal as a real `[]T`
// slice, so indexing the inner slice in the callee reads element contents
// correctly — for both the local-bound form and the direct-call-argument form.
// Regression (issue 0085): inner literals were appended as raw `[N]T` arrays
@@ -9,7 +9,7 @@
// recurses with the nesting, so every level coerces.
#import "modules/std.sx";
sum_nested :: (xss: [][]s64) -> s64 {
sum_nested :: (xss: [][]i64) -> i64 {
total := 0;
i := 0;
while i < xss.len {
@@ -20,7 +20,7 @@ sum_nested :: (xss: [][]s64) -> s64 {
return total;
}
count_x :: (xss: [][]string) -> s64 {
count_x :: (xss: [][]string) -> i64 {
n := 0;
i := 0;
while i < xss.len {
@@ -32,8 +32,8 @@ count_x :: (xss: [][]string) -> s64 {
}
main :: () {
// numeric [][]s64 — local-bound vs direct-arg both sum to 10.
local : [][]s64 = .[.[1, 2], .[3, 4]];
// numeric [][]i64 — local-bound vs direct-arg both sum to 10.
local : [][]i64 = .[.[1, 2], .[3, 4]];
print("num local={}\n", sum_nested(local));
print("num direct={}\n", sum_nested(.[.[1, 2], .[3, 4]]));

View File

@@ -1,5 +1,5 @@
// A named-const array dimension lays out identically whether the const is
// TYPED (`N : s64 : 16`) or untyped (`N :: 16`), used DIRECTLY (`a : [N]T`) or
// TYPED (`N : i64 : 16`) or untyped (`N :: 16`), used DIRECTLY (`a : [N]T`) or
// through a type alias (`Arr :: [N]T`), and regardless of whether the const is
// declared before or after the alias that consumes it.
//
@@ -13,22 +13,22 @@
// stateful and stateless paths share one dimension resolver.
#import "modules/std.sx";
NT : s64 : 8; // typed const used as a dimension
NT : i64 : 8; // typed const used as a dimension
P :: struct { x: s64; y: s64; }
P :: struct { x: i64; y: i64; }
// Type aliases whose dimension is the TYPED const NT (stateless registration).
TArr :: [NT]s64;
TArr :: [NT]i64;
TSArr :: [NT]string;
TPArr :: [NT]P;
// Forward reference: this alias is declared BEFORE its dimension const NF.
FArr :: [NF]s64;
FArr :: [NF]i64;
NF :: 5;
main :: () {
// Typed-const dimension, DIRECT local decl.
d : [NT]s64 = ---;
d : [NT]i64 = ---;
d[0] = 3;
d[7] = 21;
print("direct d0={} d7={} len={}\n", d[0], d[7], d.len);
@@ -43,7 +43,7 @@ main :: () {
s : TSArr = ---;
s[0] = "hi";
s[7] = "yo";
print("alias s0={} s7={}\n", s[0], s[7]);
print("alias i0={} i7={}\n", s[0], s[7]);
// Typed-const dimension via ALIAS (struct elements).
ps : TPArr = ---;
@@ -52,7 +52,7 @@ main :: () {
print("alias p0x={} p0y={} p7x={}\n", ps[0].x, ps[0].y, ps[7].x);
// Nested fixed array whose both dimensions are the typed const NT.
grid : [NT][NT]s64 = ---;
grid : [NT][NT]i64 = ---;
grid[0][0] = 1;
grid[7][7] = 10;
print("nested g00={} g77={}\n", grid[0][0], grid[7][7]);

View File

@@ -16,50 +16,50 @@
M :: 4;
N :: 6;
TK : s64 : 2; // typed const, used inside an expression dimension
TK : i64 : 2; // typed const, used inside an expression dimension
P :: struct { x: s64; y: s64; }
P :: struct { x: i64; y: i64; }
AddAlias :: [M + 1]s64; // 5
MulAlias :: [M * N]s64; // 24
SubAlias :: [N - M]s64; // 2
NestAlias :: [M + N - 1]s64; // 9
ParenAlias :: [(M + 1) * 2]s64; // 10
TypedAlias :: [M + TK]s64; // 6
AddAlias :: [M + 1]i64; // 5
MulAlias :: [M * N]i64; // 24
SubAlias :: [N - M]i64; // 2
NestAlias :: [M + N - 1]i64; // 9
ParenAlias :: [(M + 1) * 2]i64; // 10
TypedAlias :: [M + TK]i64; // 6
StrAlias :: [M + 1]string; // 5, slice/pointer elements
StructAlias :: [M + 1]P; // 5, struct elements
main :: () {
// const + literal: direct and via alias resolve to the same length.
add_d : [M + 1]s64 = ---;
add_d : [M + 1]i64 = ---;
add_a : AddAlias = ---;
add_d[4] = 7;
add_a[4] = 7;
print("add direct.len={} alias.len={} d4={} a4={}\n", add_d.len, add_a.len, add_d[4], add_a[4]);
// const * const.
mul_d : [M * N]s64 = ---;
mul_d : [M * N]i64 = ---;
mul_a : MulAlias = ---;
mul_d[23] = 230;
mul_a[23] = 230;
print("mul direct.len={} alias.len={} d23={} a23={}\n", mul_d.len, mul_a.len, mul_d[23], mul_a[23]);
// const - const.
sub_d : [N - M]s64 = ---;
sub_d : [N - M]i64 = ---;
sub_a : SubAlias = ---;
sub_d[1] = 9;
sub_a[1] = 9;
print("sub direct.len={} alias.len={} d1={} a1={}\n", sub_d.len, sub_a.len, sub_d[1], sub_a[1]);
// nested and parenthesised forms (direct vs alias).
nest_d : [M + N - 1]s64 = ---;
nest_d : [M + N - 1]i64 = ---;
nest_a : NestAlias = ---;
paren_d : [(M + 1) * 2]s64 = ---;
paren_d : [(M + 1) * 2]i64 = ---;
paren_a : ParenAlias = ---;
print("nest direct.len={} alias.len={} paren direct.len={} alias.len={}\n", nest_d.len, nest_a.len, paren_d.len, paren_a.len);
// typed const inside the expression dimension.
typ_d : [M + TK]s64 = ---;
typ_d : [M + TK]i64 = ---;
typ_a : TypedAlias = ---;
print("typed direct.len={} alias.len={}\n", typ_d.len, typ_a.len);
@@ -67,7 +67,7 @@ main :: () {
str_a : StrAlias = ---;
str_a[0] = "hi";
str_a[4] = "yo";
print("str alias.len={} s0={} s4={}\n", str_a.len, str_a[0], str_a[4]);
print("str alias.len={} i0={} i4={}\n", str_a.len, str_a[0], str_a[4]);
// struct elements.
ps : StructAlias = ---;

View File

@@ -1,8 +1,8 @@
// An array dimension accepts any compile-time numeric constant whose value is a
// positive INTEGRAL number — an integral float (`4.0`) folds to its integer just
// like `4`. A float-typed const (`N : f64 : 4.0`), an untyped-float const
// (`M :: 4.0`), and a direct float literal (`[4.0]s64`) all lay out the same
// `[4]s64` as the integer spelling, so element store/read is in bounds.
// (`M :: 4.0`), and a direct float literal (`[4.0]i64`) all lay out the same
// `[4]i64` as the integer spelling, so element store/read is in bounds.
//
// Regression (issue 0083 / F0.4 attempt 8, Agra ruling): an integral float used
// as a dimension was wrongly rejected "must be a compile-time integer constant".
@@ -15,15 +15,15 @@ N : f64 : 4.0; // float-typed const
M :: 4.0; // untyped float const
main :: () {
a : [N]s64 = ---; // dim from a float-typed const
a : [N]i64 = ---; // dim from a float-typed const
a[0] = 10; a[3] = 40;
print("a len={} a0={} a3={}\n", a.len, a[0], a[3]);
b : [M]s64 = ---; // dim from an untyped float const
b : [M]i64 = ---; // dim from an untyped float const
b[1] = 21;
print("b len={} b1={}\n", b.len, b[1]);
c : [4.0]s64 = ---; // direct integral-float-literal dim
c : [4.0]i64 = ---; // direct integral-float-literal dim
c[2] = 32;
print("c len={} c2={}\n", c.len, c[2]);
}

View File

@@ -4,9 +4,9 @@
// SAME leaf forms to the SAME value through one shared evaluator
// (`program_index.evalConstIntExpr` / `moduleConstInt`). The leaf forms
// exercised here: untyped int const (`M`), a named const with an EXPRESSION RHS
// (`N :: M + 1`), a typed-int const (`S : s64 : 5`), an integral float const
// (`N :: M + 1`), a typed-int const (`S : i64 : 5`), an integral float const
// (`F :: 4.0` ≡ 4), and an ALIASED integer constraint (`Count :: u32`,
// `Small :: s8`) on a value-param.
// `Small :: i8`) on a value-param.
//
// Regression (issue 0083): two cells of this surface diverged from the rest.
// (1) A named const whose RHS is an expression (`N :: M + 1`) did not fold as a
@@ -20,27 +20,27 @@
M :: 2; // untyped int const
N :: M + 1; // named const, EXPRESSION RHS (== 3)
S : s64 : 5; // typed-int const
S : i64 : 5; // typed-int const
KU : u32 : 3; // typed-u32 const
F :: 4.0; // integral float const (== 4)
Count :: u32; // integer ALIAS — value-param constraint
Small :: s8; // integer ALIAS — value-param constraint
Small :: i8; // integer ALIAS — value-param constraint
ArrN :: [N]s64; // array dim via alias: expression const (3)
ArrF :: [F]s64; // array dim via alias: integral float (4)
ArrS :: [S]s64; // array dim via alias: typed const (5)
ArrN :: [N]i64; // array dim via alias: expression const (3)
ArrF :: [F]i64; // array dim via alias: integral float (4)
ArrS :: [S]i64; // array dim via alias: typed const (5)
Buf :: struct ($K: u32, $T: Type) { data: [K]T; }
BufC :: struct ($K: Count, $T: Type) { data: [K]T; } // ALIASED u32 constraint
BufS :: struct ($K: Small, $T: Type) { data: [K]T; } // ALIASED s8 constraint
BufS :: struct ($K: Small, $T: Type) { data: [K]T; } // ALIASED i8 constraint
Make :: ($K: u32, $T: Type) -> Type { return [K]T; } // type-fn value-param
main :: () {
// array dimension — DIRECT
a : [N]s64 = ---; a[0] = 7; a[2] = 9;
a : [N]i64 = ---; a[0] = 7; a[2] = 9;
print("dim.direct.expr: len={} a0={} a2={}\n", a.len, a[0], a[2]);
f : [F]s64 = ---; f[3] = 40;
f : [F]i64 = ---; f[3] = 40;
print("dim.direct.float: len={} f3={}\n", f.len, f[3]);
// array dimension — via type ALIAS
@@ -54,13 +54,13 @@ main :: () {
v4 : Vector(F, f32) = .[1.0, 2.0, 3.0, 4.0];
print("lane.float4: {}\n", v4.w);
// generic value-param — struct binder: expr const, aliased u32, aliased s8
bn : Buf(N, s64) = ---; bn.data[2] = 30; print("vp.struct.expr: len={} v={}\n", bn.data.len, bn.data[2]);
bc : BufC(KU, s64) = ---; bc.data[2] = 31; print("vp.struct.alias.u32: len={} v={}\n", bc.data.len, bc.data[2]);
bs : BufS(4, s64) = ---; bs.data[3] = 32; print("vp.struct.alias.s8: len={} v={}\n", bs.data.len, bs.data[3]);
// generic value-param — struct binder: expr const, aliased u32, aliased i8
bn : Buf(N, i64) = ---; bn.data[2] = 30; print("vp.struct.expr: len={} v={}\n", bn.data.len, bn.data[2]);
bc : BufC(KU, i64) = ---; bc.data[2] = 31; print("vp.struct.alias.u32: len={} v={}\n", bc.data.len, bc.data[2]);
bs : BufS(4, i64) = ---; bs.data[3] = 32; print("vp.struct.alias.i8: len={} v={}\n", bs.data.len, bs.data[3]);
// generic value-param — type-fn binder: expr const
mk : Make(N, s64) = ---; mk[2] = 33; print("vp.typefn.expr: len={} v={}\n", mk.len, mk[2]);
mk : Make(N, i64) = ---; mk[2] = 33; print("vp.typefn.expr: len={} v={}\n", mk.len, mk[2]);
// inline-for bound — expr const (3) and integral float (4)
s := 0; inline for 0..N (i) { s += i; } print("for.expr: {}\n", s); // 0+1+2 = 3

View File

@@ -8,10 +8,10 @@
// "positive integral", which wrongly implied `[0]T` / `Box(0)` are illegal.
#import "modules/std.sx";
Box :: struct($N: u32) { items: [N]s64; }
Box :: struct($N: u32) { items: [N]i64; }
main :: () {
a : [0]s64 = ---;
a : [0]i64 = ---;
print("array_dim={}\n", a.len);
b : Box(0) = ---;

View File

@@ -5,61 +5,61 @@
// `usize`/`isize` (target-width). Usable in expressions and in array-dimension
// position via the comptime-int path (`[u8.max]T`).
//
// The extreme values that the s64-based integer formatter cannot render
// directly — `s64.min` (i64::MIN) and the all-ones `u64.max`/`usize.max` — are
// The extreme values that the i64-based integer formatter cannot render
// directly — `i64.min` (i64::MIN) and the all-ones `u64.max`/`usize.max` — are
// asserted EXACTLY via comparison and untagged-union bit reinterpret, never via
// the formatter (which prints i64::MIN as a bare "-" and u64.max as "-1").
#import "modules/std.sx";
// Untagged union for the exact u64.max bit-reinterpret check.
UU :: union { u: u64; s: s64; }
UU :: union { u: u64; s: i64; }
main :: () -> s32 {
main :: () -> i32 {
// Sub-byte widths — arbitrary bit-width arithmetic, not a per-name table.
print("s1.min={} s1.max={}\n", s1.min, s1.max); // -1 0
print("s2.min={} s2.max={}\n", s2.min, s2.max); // -2 1
print("s3.max={}\n", s3.max); // 3
print("i1.min={} i1.max={}\n", i1.min, i1.max); // -1 0
print("i2.min={} i2.max={}\n", i2.min, i2.max); // -2 1
print("i3.max={}\n", i3.max); // 3
print("u1.min={} u1.max={}\n", u1.min, u1.max); // 0 1
print("u2.max={}\n", u2.max); // 3
// Byte / word widths.
print("s8.min={} s8.max={}\n", s8.min, s8.max); // -128 127
print("i8.min={} i8.max={}\n", i8.min, i8.max); // -128 127
print("u8.max={}\n", u8.max); // 255
print("s32.min={} s32.max={}\n", s32.min, s32.max); // -2147483648 2147483647
print("i32.min={} i32.max={}\n", i32.min, i32.max); // -2147483648 2147483647
// s64 extremes: max prints; min (i64::MIN) is pinned by relation since the
// i64 extremes: max prints; min (i64::MIN) is pinned by relation since the
// formatter cannot render it (this is independent of this feature).
print("s64.max={}\n", s64.max); // 9223372036854775807
print("s64.min+1 == -(s64.max): {}\n", s64.min + 1 == -9223372036854775807); // true
print("s64.min + s64.max == -1: {}\n", s64.min + s64.max == -1); // true
print("i64.max={}\n", i64.max); // 9223372036854775807
print("i64.min+1 == -(i64.max): {}\n", i64.min + 1 == -9223372036854775807); // true
print("i64.min + i64.max == -1: {}\n", i64.min + i64.max == -1); // true
// u64.max / usize.max = all-ones (18446744073709551615); reinterpret to s64
// u64.max / usize.max = all-ones (18446744073709551615); reinterpret to i64
// to confirm the bit pattern is -1 (and NOT a mangled value).
o : UU = ---;
o.u = u64.max;
print("u64.max as s64 == -1: {}\n", o.s == -1); // true
print("u64.max as i64 == -1: {}\n", o.s == -1); // true
o.u = usize.max;
print("usize.max as s64 == -1: {}\n", o.s == -1); // true (host = u64)
print("usize.max as i64 == -1: {}\n", o.s == -1); // true (host = u64)
print("usize.max == u64.max: {}\n", usize.max == u64.max); // true
print("isize.min == s64.min: {}\n", isize.min == s64.min); // true (host = s64)
print("isize.min == i64.min: {}\n", isize.min == i64.min); // true (host = i64)
// Result carries the QUERIED type: each binding is declared with the queried
// type and round-trips, so a mistyped fold (e.g. boxed as Any / widened)
// would not type-check here.
m3 : s3 = s3.max;
m3 : i3 = i3.max;
mu : u8 = u8.max;
ms : s8 = s8.min;
ms : i8 = i8.min;
print("typed: m3={} mu={} ms={}\n", m3, mu, ms); // 3 255 -128
// Array-dimension / comptime-int path: `[u8.max]T` and `[s16.max]T` are
// Array-dimension / comptime-int path: `[u8.max]T` and `[i16.max]T` are
// valid counts (255 and 32767), usable end-to-end.
a : [u8.max]u8 = ---;
a[254] = 7;
print("[u8.max]u8 len={} a[254]={}\n", a.len, a[254]); // 255 7
b : [s16.max]u8 = ---;
b : [i16.max]u8 = ---;
b[32766] = 9;
print("[s16.max]u8 len={} b[32766]={}\n", b.len, b[32766]); // 32767 9
print("[i16.max]u8 len={} b[32766]={}\n", b.len, b[32766]); // 32767 9
return 0;
}

View File

@@ -8,9 +8,9 @@
// Each case is accurate and located at the access; the program exits non-zero.
#import "modules/std.sx";
MyStruct :: struct { a: s64; }
MyStruct :: struct { a: i64; }
main :: () -> s32 {
main :: () -> i32 {
b := bool.max;
s := MyStruct.min;
v := void.max;

View File

@@ -1,10 +1,10 @@
// Backtick raw-identifier escape: a leading backtick makes the following
// identifier RAW — its text excludes the backtick and it is never the
// reserved/builtin keyword, so a reserved type-name spelling (`s2`, `u8`, …)
// reserved/builtin keyword, so a reserved type-name spelling (`i2`, `u8`, …)
// can be used as an ordinary identifier. Exercised in every VALUE position:
// global, local, param, struct field + member access, function name + call,
// and a later reference. (A raw identifier in TYPE position references a
// backtick-declared type instead — see examples/0154.) A *bare* `s2` is still
// backtick-declared type instead — see examples/0154.) A *bare* `i2` is still
// the reserved type name (see examples/1119), so the escape is the only way to
// spell these as values.
// Regression (issue 0089).
@@ -14,22 +14,22 @@
`u8 := 100;
// Function whose name is a reserved type spelling, with a reserved-name param.
`s2 :: (`s1: s64) -> s64 { return `s1 * 2; }
`i2 :: (`i1: i64) -> i64 { return `i1 * 2; }
Point :: struct {
`s2: f64; // field name is a reserved type spelling
`u16: s64;
`i2: f64; // field name is a reserved type spelling
`u16: i64;
}
main :: () {
// Local with a reserved type spelling; later reference resolves to it.
`s64 := 7;
`s64 = `s64 + 1;
print("local = {}\n", `s64);
`i64 := 7;
`i64 = `i64 + 1;
print("local = {}\n", `i64);
print("global = {}\n", `u8);
print("fn = {}\n", `s2(21)); // calls the `s2 function
print("fn = {}\n", `i2(21)); // calls the `i2 function
p := Point.{ `s2 = 2.5, `u16 = 9 };
print("field = {} {}\n", p.`s2, p.`u16);
p := Point.{ `i2 = 2.5, `u16 = 9 };
print("field = {} {}\n", p.`i2, p.`u16);
}

View File

@@ -1,5 +1,5 @@
// Backtick raw identifier across every control-flow / capture / binding form,
// plus bare later uses. A reserved type-name spelling (`s2`, `u8`, …) works as a
// plus bare later uses. A reserved type-name spelling (`i2`, `u8`, …) works as a
// binding name in a destructure, an `if`/`while` optional binding, a `for`
// capture + index, and a match-arm capture; a backtick-named function is
// bare-callable; and a backtick struct field is bare- or backtick-accessible.
@@ -10,37 +10,37 @@
// Regression (issue 0089 — attempt-2 completeness across binding forms).
#import "modules/std.sx";
pair :: () -> (s64, s64) { (1, 2) }
maybe :: () -> ?s64 { return 42; }
pair :: () -> (i64, i64) { (1, 2) }
maybe :: () -> ?i64 { return 42; }
// Function named with a reserved spelling — bare-callable (no backtick at call).
`s2 :: (n: s64) -> s64 { return n + 1; }
`i2 :: (n: i64) -> i64 { return n + 1; }
Quad :: struct { `s1: s32; `s2: s32; }
Quad :: struct { `i1: i32; `i2: i32; }
main :: () -> s32 {
main :: () -> i32 {
// destructure binding names
`u8, rest := pair();
print("dstr = {} {}\n", `u8, rest);
// if optional binding + bare-position reference inside the branch
if `s16 := maybe() {
print("if = {}\n", `s16);
if `i16 := maybe() {
print("if = {}\n", `i16);
}
// while optional binding (name only — the while binding isn't body-exposed)
while `s32 := maybe() {
while `i32 := maybe() {
break;
}
// for capture + index names
xs := [3]s64.{ 10, 20, 30 };
xs := [3]i64.{ 10, 20, 30 };
for xs, 0.. (`bool, `u16) {
print("for = {} @ {}\n", `bool, `u16);
}
// match-arm capture
opt: ?s64 = 5;
opt: ?i64 = 5;
m := if opt == {
case .some: (`string) { `string * 2 }
case .none: { 0 }
@@ -48,10 +48,10 @@ main :: () -> s32 {
print("match = {}\n", m);
// backtick function called BARE and via backtick — both resolve to the fn
print("call = {} {}\n", s2(10), `s2(10));
print("call = {} {}\n", i2(10), `i2(10));
// struct field named with a reserved spelling: bare + backtick member access
q := Quad.{ `s1 = 7, `s2 = 9 };
print("field = {} {} | {} {}\n", q.s1, q.s2, q.`s1, q.`s2);
q := Quad.{ `i1 = 7, `i2 = 9 };
print("field = {} {} | {} {}\n", q.i1, q.i2, q.`i1, q.`i2);
return 0;
}

View File

@@ -1,22 +1,22 @@
// Backtick raw-identifier escape at the `::` declaration sites: a leading
// backtick makes a CONSTANT name and a FUNCTION name raw, so a reserved type
// spelling (`s2`, `u8`) can be declared and used. Complements examples/0151
// spelling (`i2`, `u8`) can be declared and used. Complements examples/0151
// (var / param / field / global). The backtick fn is callable both via the
// backtick (`` `u8(5) ``) and bare (`u8(5)`) — the bare reserved-name callee
// resolves to the raw fn because its declaration is raw (issue 0089). A *bare*
// `s2 :: …` / `u8 :: …` declaration is still the reserved-name error (see
// `i2 :: …` / `u8 :: …` declaration is still the reserved-name error (see
// examples/1140).
// Regression (issue 0089).
#import "modules/std.sx";
// Constant whose name is a reserved type spelling.
`s2 :: 2.5;
`i2 :: 2.5;
// Function whose name is a reserved type spelling.
`u8 :: (n: s64) -> s64 { return n + 7; }
`u8 :: (n: i64) -> i64 { return n + 7; }
main :: () -> s32 {
print("const = {}\n", `s2);
main :: () -> i32 {
print("const = {}\n", `i2);
print("fn tick = {}\n", `u8(5));
print("fn bare = {}\n", u8(5));
return 0;

View File

@@ -1,8 +1,8 @@
// Backtick raw identifier in TYPE position (the universal model, issue 0089):
// `` `name `` is the LITERAL identifier `name` used as a type reference, never
// the builtin/reserved spelling. A reserved type spelling (`s2`, `u8`, …) can
// the builtin/reserved spelling. A reserved type spelling (`i2`, `u8`, …) can
// therefore both DECLARE a type (struct / enum / union / error-set / alias) and
// be REFERENCED as that type via the backtick — while a BARE `s2` in type
// be REFERENCED as that type via the backtick — while a BARE `i2` in type
// position remains the signed-int type (see `add` below) and a bare reserved-
// name declaration still errors (see examples/1141). The backtick is required
// to declare or reference these names; it is never part of the name's text.
@@ -10,18 +10,18 @@
#import "modules/std.sx";
// Type-introducing decls whose NAME is a reserved spelling.
`s2 :: struct { x: s64; }
`s8 :: enum { A; B; }
`u16 :: union { i: s32; f: f32; }
`i2 :: struct { x: i64; }
`i8 :: enum { A; B; }
`u16 :: union { i: i32; f: f32; }
`u32 :: error { Bad, Empty }
RawAlias :: `s2; // alias to a backtick-declared struct
RawAlias :: `i2; // alias to a backtick-declared struct
// A bare `s2` in type position is still the 2-bit signed int.
add :: (a: s2, b: s2) -> s2 { return a + b; }
// A bare `i2` in type position is still the 2-bit signed int.
add :: (a: i2, b: i2) -> i2 { return a + b; }
main :: () -> s32 {
main :: () -> i32 {
// Reference the backtick struct as a type; field access works.
v : `s2 = ---;
v : `i2 = ---;
v.x = 7;
// Reference via a normal alias too.
@@ -29,7 +29,7 @@ main :: () -> s32 {
a.x = 11;
// Backtick enum / union type references.
e : `s8 = .A;
e : `i8 = .A;
u : `u16 = ---;
u.i = 5;
@@ -37,6 +37,6 @@ main :: () -> s32 {
print("alias = {}\n", a.x);
print("enum = {}\n", e == .A);
print("union = {}\n", u.i);
print("bare = {}\n", add(1, 0)); // bare s2 = the 2-bit int type
print("bare = {}\n", add(1, 0)); // bare i2 = the 2-bit int type
return 0;
}

View File

@@ -1,24 +1,24 @@
// Backtick raw identifier at the two remaining binding positions (issue 0089,
// attempt-4): a TYPED constant (`` `s2 : s64 : 5 ``) and a union TAG / field
// (`` `s2: s32 ``). The typed-const form previously slipped past the decl check
// without a name span (caret at 1:1); a bare `s2 : s64 : 5` is still rejected
// attempt-4): a TYPED constant (`` `i2 : i64 : 5 ``) and a union TAG / field
// (`` `i2: i32 ``). The typed-const form previously slipped past the decl check
// without a name span (caret at 1:1); a bare `i2 : i64 : 5` is still rejected
// with the caret ON the name (see examples/1141). A union tag spelled with a
// reserved name works and is accessible bare or backticked.
// Regression (issue 0089 — attempt-4 typed const + union tag).
#import "modules/std.sx";
// Typed constant whose name is a reserved type spelling.
`s2 : s64 : 5;
`i2 : i64 : 5;
// Union whose tags are reserved type spellings.
Mix :: union { `s1: s32; `u8: f32; }
Mix :: union { `i1: i32; `u8: f32; }
main :: () -> s32 {
print("typed const = {}\n", `s2);
main :: () -> i32 {
print("typed const = {}\n", `i2);
m : Mix = ---;
m.`s1 = 42;
print("union tick = {}\n", m.`s1); // backtick member access
print("union bare = {}\n", m.s1); // bare member access — same field
m.`i1 = 42;
print("union tick = {}\n", m.`i1); // backtick member access
print("union bare = {}\n", m.i1); // bare member access — same field
return 0;
}

View File

@@ -1,7 +1,7 @@
// Backtick raw-identifier escape at a STRUCT-BODY constant — both the untyped
// `` `name :: value `` and the typed `` `name : T : value `` forms. A struct
// member constant is a binding site like any top-level const (examples/0153),
// so a reserved type spelling (`s2`, `u8`) needs the backtick to be used as the
// so a reserved type spelling (`i2`, `u8`) needs the backtick to be used as the
// constant's name; the value is read back via `Holder.`name`. A *bare*
// reserved-name struct const still errors with the caret on the name (see
// examples/1142). The backtick is never part of the name's text.
@@ -10,12 +10,12 @@
#import "modules/std.sx";
Holder :: struct {
`s2 :: 5; // untyped raw struct-body const
`u8 : s64 : 9; // typed raw struct-body const
`i2 :: 5; // untyped raw struct-body const
`u8 : i64 : 9; // typed raw struct-body const
}
main :: () -> s32 {
print("untyped = {}\n", Holder.`s2);
main :: () -> i32 {
print("untyped = {}\n", Holder.`i2);
print("typed = {}\n", Holder.`u8);
return 0;
}

View File

@@ -1,26 +1,26 @@
// Backtick raw identifier in PARAMETERIZED type position. A raw type reference
// (`` `s2 ``) flows through the SAME type-expression continuations as a bare
// (`` `i2 ``) flows through the SAME type-expression continuations as a bare
// name, so a reserved-spelled GENERIC template can be instantiated
// (`` `s2(s64) ``) and the result composes under pointer/field wrappers
// (`` *`s2(s64) ``, a struct field typed `` `s2(s64) ``). A bare `s2` in type
// (`` `i2(i64) ``) and the result composes under pointer/field wrappers
// (`` *`i2(i64) ``, a struct field typed `` `i2(i64) ``). A bare `i2` in type
// position is still the 2-bit signed int. Complements examples/0154 (nullary
// raw type references).
// Regression (issue 0089 — attempt-5: the raw type atom no longer parses as a
// terminal `type_expr`; it reaches the parameterized + wrapper continuations).
#import "modules/std.sx";
`s2 :: struct($T: Type) {
`i2 :: struct($T: Type) {
x: $T;
}
Wrapper :: struct {
inner: `s2(s64); // raw parameterized type as a struct field
inner: `i2(i64); // raw parameterized type as a struct field
}
main :: () -> s32 {
v : `s2(s64);
main :: () -> i32 {
v : `i2(i64);
v.x = 7;
p : *`s2(s64) = @v; // pointer to a raw parameterized type
p : *`i2(i64) = @v; // pointer to a raw parameterized type
w : Wrapper = ---;
w.inner.x = 12;
print("val = {}\n", v.x);

View File

@@ -1,14 +1,14 @@
// Reserved-name MEMBER positions are EXEMPT from the reserved-type-name rule:
// a bare reserved spelling (`s2`, `u8`, `s1`, …) is legal as a struct FIELD
// a bare reserved spelling (`i2`, `u8`, `i1`, …) is legal as a struct FIELD
// name, a union TAG name, and a protocol METHOD-SIGNATURE name. These are
// unambiguous — the name sits in a member slot and is reached via `obj.name`
// (or dispatched by string), so it is never type-classified and never
// mislowers. The backtick form is optional there and resolves to the same
// member. Backtick access (`obj.`s2`) and bare access (`obj.s2`) both work.
// member. Backtick access (`obj.`i2`) and bare access (`obj.i2`) both work.
//
// The exemption stops at member SIGNATURES: an `impl` method DEFINITION is a
// real function, so its name is a declaration site (like a free function) and a
// reserved spelling still needs the backtick (`` `s2 :: (self) ``) — bare would
// reserved spelling still needs the backtick (`` `i2 :: (self) ``) — bare would
// be type-classified and mislower (the issue-0076 protection). A bare reserved
// VALUE binding / declaration name still errors (see examples/1119, 1141, 1142).
// Regression (issue 0089 — attempt-7: pins the Agra-ruled member-name exemption).
@@ -16,40 +16,40 @@
// Struct fields spelled with reserved type names — bare is legal.
Holder :: struct {
s2: s64;
u8: s64;
i2: i64;
u8: i64;
}
// Union tags spelled with reserved type names — bare is legal.
Tag :: union {
s1: s32;
i1: i32;
u16: f64;
}
// Protocol method SIGNATURE spelled with a reserved type name — bare is legal.
Speaker :: protocol {
s2 :: () -> s64;
i2 :: () -> i64;
}
Dog :: struct { n: s64; }
Dog :: struct { n: i64; }
impl Speaker for Dog {
`s2 :: (self: *Dog) -> s64 { self.n } // impl DEFINITION → backtick required
`i2 :: (self: *Dog) -> i64 { self.n } // impl DEFINITION → backtick required
}
main :: () -> s32 {
h := Holder.{ s2 = 10, u8 = 20 };
print("fields bare = {} {}\n", h.s2, h.u8); // bare member access
print("fields tick = {} {}\n", h.`s2, h.`u8); // backtick member access
h.s2 = 11;
main :: () -> i32 {
h := Holder.{ i2 = 10, u8 = 20 };
print("fields bare = {} {}\n", h.i2, h.u8); // bare member access
print("fields tick = {} {}\n", h.`i2, h.`u8); // backtick member access
h.i2 = 11;
h.`u8 = 21; // backtick write
print("fields set = {} {}\n", h.s2, h.u8);
print("fields set = {} {}\n", h.i2, h.u8);
t : Tag = ---;
t.s1 = 5;
print("union = {} {}\n", t.s1, t.`s1); // bare + backtick — same tag
t.i1 = 5;
print("union = {} {}\n", t.i1, t.`i1); // bare + backtick — same tag
items : List(Speaker) = .{};
items.append(Dog.{ n = 7 });
print("dispatch = {}\n", items.items[0].s2()); // bare reserved-name method call
print("dispatch = {}\n", items.items[0].i2()); // bare reserved-name method call
return 0;
}

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