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:
12
CLAUDE.md
12
CLAUDE.md
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.19–1.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.15–1.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.1–R.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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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); }
|
||||
...
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
main :: () -> i32 {
|
||||
if false then 40 else 42
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#import "modules/std.sx";
|
||||
main :: () -> s32 {
|
||||
main :: () -> i32 {
|
||||
x := 42;
|
||||
{
|
||||
print("scope opened\n");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#import "modules/std.sx";
|
||||
main :: () -> s32 {
|
||||
main :: () -> i32 {
|
||||
defer print("still here\n");
|
||||
return 42;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
sumOf10 :: () -> s32 {
|
||||
sumOf10 :: () -> i32 {
|
||||
i:= 1;
|
||||
s:=0;
|
||||
while i <= 10 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,6 @@ do_it :: () -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
main :: () -> s32 {
|
||||
main :: () -> i32 {
|
||||
if do_it() then 0 else 1
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 :: () {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
Json :: enum {
|
||||
str: string;
|
||||
int_: s64;
|
||||
int_: i64;
|
||||
null_;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -5,7 +5,7 @@ Vec4 :: struct {
|
||||
|
||||
Complex :: struct {
|
||||
foo : enum {
|
||||
S: s32;
|
||||
S: i32;
|
||||
B: struct {
|
||||
val: string;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 :: () {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Overlay :: union {
|
||||
f: f32;
|
||||
i: s32;
|
||||
i: i32;
|
||||
}
|
||||
|
||||
Vec2 :: union {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"); }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: ");
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
Foo :: struct {
|
||||
running: bool = true;
|
||||
x: s32 = 42;
|
||||
x: i32 = 42;
|
||||
name: string = "default";
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ ok :: () -> Handle { 0 }
|
||||
|
||||
g : Handle = 0;
|
||||
|
||||
main :: () -> s32 {
|
||||
main :: () -> i32 {
|
||||
g = ok();
|
||||
if g == 0 then 0 else 1
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 :: () {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 } ];
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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]]));
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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 = ---;
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) = ---;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user