Compare commits
45 Commits
59f0aa7716
...
d8076b9333
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8076b9333 | ||
|
|
515ecebea7 | ||
|
|
70363dda56 | ||
|
|
309f48e1b5 | ||
|
|
b625b74046 | ||
|
|
837b5d375f | ||
|
|
7c7bb2076a | ||
|
|
47110b37cf | ||
|
|
0e890b9992 | ||
|
|
5b18048abb | ||
|
|
7f2b8b5cde | ||
|
|
7d1e23ecc6 | ||
|
|
b3b78e25c0 | ||
|
|
c75cd9c63c | ||
|
|
49a36bb492 | ||
|
|
340be402a5 | ||
|
|
721369a711 | ||
|
|
f2db8ecc53 | ||
|
|
51194a26d8 | ||
|
|
a47ea1416e | ||
|
|
84e0fb0752 | ||
|
|
3e10809d7e | ||
|
|
88bae3c9f5 | ||
|
|
ca5bd52262 | ||
|
|
40805e08ec | ||
|
|
03dc10bba3 | ||
|
|
c229f697bd | ||
|
|
40a94c4734 | ||
|
|
679653fda8 | ||
|
|
c23b76c7d6 | ||
|
|
7f3bd69bd9 | ||
|
|
8908e78943 | ||
|
|
82d6b8da0e | ||
|
|
57979ed8e6 | ||
|
|
a2bd5b0438 | ||
|
|
4f1a9738c7 | ||
|
|
cfe5804288 | ||
|
|
330c3aeef7 | ||
|
|
0b13498e25 | ||
|
|
37bea63302 | ||
|
|
fbbfcb268c | ||
|
|
22552075d5 | ||
|
|
b8748aee16 | ||
|
|
698f75d79a | ||
|
|
12bf61a9fc |
32
CLAUDE.md
32
CLAUDE.md
@@ -16,7 +16,7 @@ Procedure:
|
||||
- **Symptom** — one-line summary + observed vs expected.
|
||||
- **Reproduction** — minimal sx code (inline fenced block). Must
|
||||
reproduce the bug standalone, no project dependencies beyond
|
||||
`modules/std.sx` / `modules/allocators.sx`.
|
||||
`modules/std.sx` / `modules/std/mem.sx`.
|
||||
- **Investigation prompt** — a ready-to-paste prompt the user can
|
||||
drop into a fresh session to fix the bug. Should include: the
|
||||
suspected area of the compiler (file + function), what the
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -541,9 +541,9 @@ foreign calls.
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| [library/modules/platform/bundle.sx](library/modules/platform/bundle.sx) | All four targets (macOS, iOS sim, iOS device, Android). Branches on `BuildOptions.is_macos / is_ios / is_ios_device / is_ios_simulator / is_android` accessors. |
|
||||
| [library/modules/fs.sx](library/modules/fs.sx) | POSIX file stdlib (open / read / write / copy / mkdir / unlink / chmod / rename / exists / basename / dirname). |
|
||||
| [library/modules/process.sx](library/modules/process.sx) | popen-based `run(cmd) -> ?ProcessResult` + `env(name)` + `find_executable(name)`. |
|
||||
| [library/modules/compiler.sx](library/modules/compiler.sx) | `BuildOptions` setters + accessors. Adding a new bundling parameter = add a setter here + a hook in compiler_hooks.zig. |
|
||||
| [library/modules/std/fs.sx](library/modules/std/fs.sx) | POSIX file stdlib (open / read / write / copy / mkdir / unlink / chmod / rename / exists / basename / dirname). |
|
||||
| [library/modules/std/process.sx](library/modules/std/process.sx) | popen-based `run(cmd) -> ?ProcessResult` + `env(name)` + `find_executable(name)`. |
|
||||
| [library/modules/build.sx](library/modules/build.sx) | `BuildOptions` setters + accessors. Adding a new bundling parameter = add a setter here + a hook in compiler_hooks.zig. |
|
||||
| [library/modules/platform/android.sx](library/modules/platform/android.sx) | `AndroidPlatform` (state-on-struct, no module globals). `sx_android_*` helpers take `plat: *AndroidPlatform` as first arg. `logical_w` field drives `dpi_scale = pixel_w / logical_w` so consumer's design-width fits any physical resolution. |
|
||||
| [src/ir/compiler_hooks.zig](src/ir/compiler_hooks.zig) | `BuildConfig` + every `BuildOptions.*` hook. Hook registry is in `Registry.registerDefaults`. |
|
||||
| [src/ir/host_ffi.zig](src/ir/host_ffi.zig) | `dlsym(RTLD_DEFAULT)` + arity-switched cdecl trampolines. Lets `#foreign("c")` decls resolve at `#run` / post-link time against host libc. |
|
||||
@@ -554,7 +554,7 @@ spec — what runs per Apple target vs Android, what each accessor
|
||||
returns, the BuildConfig forwarded from main.zig — lives there.
|
||||
|
||||
Wiring a new bundling step:
|
||||
1. Add the parameter as a setter on `BuildOptions :: struct #compiler { ... }` in [library/modules/compiler.sx](library/modules/compiler.sx).
|
||||
1. Add the parameter as a setter on `BuildOptions :: struct #compiler { ... }` in [library/modules/build.sx](library/modules/build.sx).
|
||||
2. Add the `BuildConfig` field + setter hook + accessor hook in [src/ir/compiler_hooks.zig](src/ir/compiler_hooks.zig). Register both in `Registry.registerDefaults`.
|
||||
3. Optionally forward a CLI flag in [src/main.zig](src/main.zig) before the post-link invocation.
|
||||
4. Read the accessor from [library/modules/platform/bundle.sx](library/modules/platform/bundle.sx).
|
||||
@@ -572,8 +572,16 @@ Wiring a new bundling step:
|
||||
| `current/CHECKPOINT-LANG.md` | **Active** LANG progress tracker. Update after every step. |
|
||||
| `current/PLAN-ERR.md` | **Active** ERR implementation plan (`!` errors, `try` / `catch` / `or` / `onfail`, return traces). |
|
||||
| `current/CHECKPOINT-ERR.md` | **Active** ERR progress tracker. Update after every step. |
|
||||
| `current/PLAN-STDLIB.md` | STDLIB restructure plan — **COMPLETE** (alias carry rule + std/ffi/math layout + full namespace tail). |
|
||||
| `current/PLAN-CONST-AGG.md` | **Active** aggregate-consts + const-ness plan (array/struct `::` consts as immutable globals, const-write rejection, comptime folds, `*const`/`[]const` with full propagation, const decay/slicing). Progress tracked in its `## Status` section — no separate checkpoint file. |
|
||||
| `implementation_plan.md` | Archive of completed work (closures, protocols, etc.). Do not pick up tasks from here. |
|
||||
| `readme.md` | User-facing language overview — **maintained**. Update it whenever a user-facing sx change lands (new/changed syntax, semantics, gating diagnostics, language behavior), per the docs-track-changes rule. |
|
||||
| `CLAUDE.md` | This file. Session instructions. |
|
||||
| `library/modules/std.sx` | The prelude FACADE — pure re-exports (alias decls) over the part-files `std/core.sx` (builtins, libc escape hatch, Context/Allocator/Into/Source_Location/string), `std/fmt.sx` (print/format/*_to_string/string ops), `std/list.sx` (List) + the namespace tail (`mem`/`xml`/`log`/`fs`/`process`/`socket`/`json`/`cli`/`hash`/`test` carried to flat importers). No implementations live here. |
|
||||
| `library/modules/std/` | Stdlib modules: core, fmt, list (the prelude part-files — consumers reach them through std.sx, not directly), mem (allocators), fs, process, socket, json, cli, hash, xml, log, trace, test — all but trace and the part-files carried by the std.sx tail; direct file imports give bare access. |
|
||||
| `library/modules/ffi/` | FFI bindings: objc, objc_block, sdl3, opengl, raylib, stb, stb_truetype, wasm. |
|
||||
| `library/modules/math/` | scalar / vector2 / matrix44 — one spelling: `#import "modules/math"` (directory import). |
|
||||
| `library/modules/build.sx` | `BuildOptions` compile-time build DSL. See "Bundling lives in sx" above. |
|
||||
| `library/modules/platform/bundle.sx` | sx-side `.app` / `.apk` bundler. See "Bundling lives in sx" above. |
|
||||
| `library/modules/fs.sx`, `library/modules/process.sx` | POSIX stdlib for the bundler + general consumer use. |
|
||||
| `library/modules/std/fs.sx`, `library/modules/std/process.sx` | POSIX stdlib for the bundler + general consumer use. |
|
||||
| `tests/fixtures/` | Test-only import fixtures (testpkg/, test_c.sx) — resolve CWD-relative from the repo root, not via the stdlib search path. |
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -5,8 +5,41 @@ Tracking checkpoint for the mem.sx Zig-aligned implementation
|
||||
|
||||
## Last completed step
|
||||
|
||||
- **`resolveType(null) → .s64` silent fallback removed.** `resolveType`
|
||||
now takes a non-optional `*const Node`; the `null → .s64` branch is
|
||||
**Allocator primitive rename — `alloc`→`alloc_bytes`,
|
||||
`dealloc`→`dealloc_bytes` (`88bae3c`, 2026-06-11).** Phase 4's naming
|
||||
piece pulled forward per Agra's call (Option A in the 2.2 naming fork):
|
||||
the bare names free up NOW so the Phase 2.2 helpers land under their
|
||||
FINAL Appendix-A names (`alloc(T,n)` / `free(s)` / `create` / `destroy`
|
||||
/ `clone` / `resize` / `mem_realloc`) with zero later churn. Phase 4
|
||||
shrinks to signature expansion (size/align params + resize/remap +
|
||||
deinit) only.
|
||||
|
||||
What changed (signatures unchanged — 2-method era continues):
|
||||
- `std.sx` protocol decl; `std/mem.sx` 6 impls + internals; library
|
||||
call sites (glyph_cache/json/state/renderer); 13 example .sx files
|
||||
(incl. the two custom `Tracer` impls in 0306/0808 — caught broken
|
||||
mid-step by stdout-diff review BEFORE pinning; the first `--update`
|
||||
had captured their broken output, restored + fixed + re-pinned).
|
||||
- Compiler: `interp.zig` thunk-name lookups
|
||||
(`__thunk_CAllocator_Allocator_alloc_bytes`/`_dealloc_bytes`) — the
|
||||
ONE hard name coupling; `allocViaContext` + default-context emission
|
||||
are slot-positional (rename-safe); ffi.zig's `"alloc"` check is the
|
||||
Obj-C `Cls.alloc()` intercept (unrelated, untouched).
|
||||
- 37 `.ir` snapshots re-pinned (thunk symbols + reflection
|
||||
field-name strings); all 37 verified stdout-clean before update;
|
||||
path-noise churn reverted.
|
||||
- External repos migrated + gated: game (`main.sx`, SxChess.app
|
||||
builds + bundles) and m3te (`board_fx.sx`, `main.sx`,
|
||||
`tools/key_particle.sx`; tools/run_tests.sh 23/23). Obj-C zero-arg
|
||||
`.alloc()` calls are NOT protocol calls — excluded by pattern.
|
||||
|
||||
Gates: zig build 0, zig build test 0, suite 582/582, m3te 23/23,
|
||||
game rebuilt.
|
||||
|
||||
<details><summary>Prior steps (2026-05-25 era)</summary>
|
||||
|
||||
- **`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
|
||||
@@ -21,7 +54,7 @@ Tracking checkpoint for the mem.sx Zig-aligned implementation
|
||||
...`), 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.
|
||||
@@ -128,7 +161,7 @@ Tracking checkpoint for the mem.sx Zig-aligned implementation
|
||||
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
|
||||
@@ -140,8 +173,66 @@ Tracking checkpoint for the mem.sx Zig-aligned implementation
|
||||
of zeros, on both interp and codegen paths. 156/156 example tests
|
||||
+ chess clean.
|
||||
|
||||
</details>
|
||||
|
||||
## Current state
|
||||
|
||||
**std.sx-as-pure-re-exports plan COMPLETE** (2026-06-11,
|
||||
Agra-directed end-to-end). std.sx is now a facade of alias
|
||||
declarations only (`49a36bb`): implementations live in std/core.sx
|
||||
(builtins, libc escape hatch, Context/Allocator/Into/Source_Location/
|
||||
`string` — the reserved name needs and permits no alias), std/fmt.sx
|
||||
(print/format/any_to_string/string ops), std/list.sx (List); the
|
||||
namespace tail is unchanged and `core`/`fmt`/`list` carry alongside
|
||||
it. Consumer surface byte-identical; 37 .ir snapshots re-pinned
|
||||
(pure renumbering, digit-normalized diff empty).
|
||||
|
||||
Issues filed AND resolved along the way (all same-day,
|
||||
Agra-authorized): 0120 generic-struct head aliases (`f2db8ec`,
|
||||
example 0211), 0121 fn aliases of every kind incl. comptime-pack
|
||||
(`721369a`, example 0546), 0122 whole-program passes pinning the
|
||||
source context per decl (`340be40` — latent on master, exposed by
|
||||
the facade; coverage 0129/1047/1049/1052/1053/1056). Protocol
|
||||
aliases (plain + Into's xx path) and #builtin/#foreign aliases
|
||||
probe-verified working; param-protocol impl dot-calls are a designed
|
||||
opt-in gap, not a bug.
|
||||
|
||||
Gates at completion: zig build test 426/426, suite 588/588,
|
||||
m3te 23/23, game SxChess builds + bundles. Suite baseline 588.
|
||||
|
||||
**(2026-06-11) Phase-by-phase ground truth** (verified against the
|
||||
tree; the sections below this one are the 2026-05-25 era record):
|
||||
- Phase 1 DONE (xx heap-copy via context.allocator; serializer; the
|
||||
whole implicit-Context refactor).
|
||||
- Phase 2.1 DONE-equivalent: allocators.sx became `std/mem.sx` via the
|
||||
STDLIB restructure (`59f0aa7`).
|
||||
- **Primitive rename DONE (`88bae3c`)** — see "Last completed step".
|
||||
Protocol is `alloc_bytes(size)` / `dealloc_bytes(ptr)` (2-method
|
||||
era signatures).
|
||||
- Phase 2.2 helpers: NOT YET — next step. Final names per Appendix A:
|
||||
`create(a,$T)->*T`, `alloc(a,$T,n)->[]T`, `destroy(a,ptr)`,
|
||||
`free(a,slice)`, `clone(src,a)`, `resize(slice,a,n)`,
|
||||
`mem_realloc(a,ptr,old,new,align)`. The `free` helper requires
|
||||
REMOVING std.sx's bare `malloc`/`free` foreign decls (lines ~13-16;
|
||||
`libc_malloc`/`libc_free` aliases already exist) and migrating ~30
|
||||
bare uses (mostly examples) — fold into the helpers step.
|
||||
- Phase 2.3: TrackingAllocator exists; FailingAllocator +
|
||||
LoggingAllocator missing.
|
||||
- Phase 3 (caller migration to helpers): not started.
|
||||
- Phase 4: now signature-expansion only (alloc_bytes gains alignment;
|
||||
dealloc_bytes gains size+align; + resize/remap/deinit) — naming
|
||||
piece already landed.
|
||||
- Phase 5 (--leak-check + specs Memory chapter): not started.
|
||||
- NOTE: plan's "init returns Allocator via xx heap-copy" section is
|
||||
SUPERSEDED by the by-value convention (CLAUDE.md "Allocator
|
||||
construction"); BufAlloc.init still returns *BufAlloc (state lives
|
||||
in the caller's buffer — review at Phase 4 whether to align).
|
||||
- Suite baseline 582; gates now: zig build && zig build test &&
|
||||
bash tests/run_examples.sh (+ m3te 23/23 + game build for
|
||||
std-touching steps). The old 148-159 counts below are historical.
|
||||
|
||||
<details><summary>2026-05-25 era state (historical)</summary>
|
||||
|
||||
Phase 0.0c shipped (allocator API on one-line `init` returning `*T`;
|
||||
TrackingAllocator added). 148/148 tests pass.
|
||||
|
||||
@@ -164,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"`.
|
||||
@@ -179,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
|
||||
@@ -243,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
|
||||
@@ -270,8 +361,77 @@ bypass) is FULLY CLOSED. User-typed `context.allocator.X` flows
|
||||
through the real protocol vtable at codegen *and* runs the same
|
||||
chain at comptime in the interp. No remaining shortcut.
|
||||
|
||||
</details>
|
||||
|
||||
## Current state
|
||||
|
||||
**Opt-in UFCS landed (`a47ea14`, 2026-06-11)** — the canonical dot
|
||||
surface now works: `context.allocator.create(Session)`,
|
||||
`slice.clone(a)`. Agra specified the model in-session (three
|
||||
clarifying rulings): free-fn dot-calls are OPT-IN via
|
||||
`name :: ufcs (params) { body }` (NEW declaration form) or
|
||||
`name :: ufcs target;` (alias); plain fns are direct/`|>`-only with a
|
||||
tailored rejection. Implementation inverted TWO pre-existing gaps:
|
||||
unannotated fns used to dot-dispatch (removed; 6 example files
|
||||
audited + migrated, ZERO reliance in m3te/game) and aliases did NOT
|
||||
dot-dispatch at all (0036 only ever pinned direct+pipe). Generic ufcs
|
||||
fns bind `$T` from the receiver; protocol receivers dispatch own
|
||||
methods first, fall through to ufcs fns for non-members
|
||||
(protocolHasMethod gate in lower/call.zig). Root-cause bonus:
|
||||
`inferGenericReturnType` now delegates to `buildTypeBindings` (ONE
|
||||
binding builder) — structured generic params (`[]$T`) no longer type
|
||||
direct calls as `T{}` stubs. mem.sx helpers marked `ufcs`; specs.md
|
||||
§UFCS rewritten around the opt-in matrix. Tests: 0053 (matrix), 1166
|
||||
(rejection), 0838 re-pinned (dot+pipe+direct). Gates: 585/585, zbt 0,
|
||||
m3te 23/23, game builds. 0119 RESOLVED (final banner).
|
||||
|
||||
**Phase 2.2 DONE (`84e0fb0`, 2026-06-11).** The 0119 block resolved as
|
||||
a LANGUAGE RULING, not a compiler fix (Agra, in-session): dot-form UFCS
|
||||
on generic free functions is not the contract — UFCS free-fn
|
||||
dot-dispatch is the annotated `ufcs` alias mechanism (concrete
|
||||
targets), and the FLUENT spelling for free functions is the pipe:
|
||||
`context.allocator |> create(Session)` desugars at parse time to the
|
||||
direct call, which dispatches generics through normal monomorphization
|
||||
(verified for protocol + slice receivers). specs.md §UFCS corrected
|
||||
(it overstated "generic functions" for the dot form). Issue 0119
|
||||
carries the RESOLVED banner; residual unfiled corner: a `ufcs` alias
|
||||
naming a generic target doesn't dot-dispatch either.
|
||||
|
||||
Landed:
|
||||
- `std/mem.sx` typed helpers, era-complete bodies, final names:
|
||||
`create(a,$T)->*T`, `destroy(a,*$T)`, `alloc(a,$T,n)->[]T`,
|
||||
`free(a,[]$T)`, `clone(src,a)`, `resize(slice,a,n)` (fresh storage +
|
||||
copy + free-old; old slice dangles), `mem_realloc(a,ptr,old,new,
|
||||
align)` (alloc+copy+dealloc; align unhonored until the protocol
|
||||
carries alignment — documented inline). NO zero-init (Zig-aligned).
|
||||
- std.sx bare `malloc`/`free` decls REMOVED (libc_malloc/libc_free
|
||||
stay as the raw escape hatch); users migrated: examples
|
||||
0205/0604/0804/0806/0808/1610 + game/chess/pieces.sx.
|
||||
- Regression: examples/0838-memory-helpers.sx (whole surface, direct +
|
||||
`|>` spellings, TrackingAllocator balances 8/8). 37 .ir re-pins
|
||||
(constant-pool renumbering from the removed decls — the
|
||||
ISSUE-MEM-004 cascade; all verified stdout-clean pre-update).
|
||||
- KNOWN GAP: `string` does NOT bind a `[]$T` param (probe: "unknown
|
||||
type 'T'") — string-clone story deferred (sx string is [:0]u8-shaped;
|
||||
decide at Phase 4/5).
|
||||
|
||||
Gates: zig build 0, zbt 0, suite 583/583, m3te 23/23, game SxChess.app
|
||||
builds.
|
||||
|
||||
## Next step
|
||||
|
||||
**Phase 2.3 — diagnostic wrappers**: `FailingAllocator` (delegates to
|
||||
parent while budget remains, then alloc returns null) and
|
||||
`LoggingAllocator` (tag-prefixed prints, delegates) in `std/mem.sx`,
|
||||
2-method-era bodies, by-value `init` per the CLAUDE.md convention.
|
||||
Then Phase 3 (migrate std.sx/library/example callers to the helpers —
|
||||
NOTE std.sx itself cannot import mem.sx (circular); its internals keep
|
||||
alloc_bytes), Phase 4 (protocol signature expansion: alignment + size
|
||||
on the primitives, resize/remap/deinit — naming already landed),
|
||||
Phase 5 (--leak-check + specs Memory chapter).
|
||||
|
||||
<details><summary>2026-05-25 era next-step record (historical)</summary>
|
||||
|
||||
Phase 1.3 (closure env allocation through context) shipped in commit
|
||||
`8e21cc5`. Phase 1.4 (codegen serializer for all interp Value
|
||||
variants) shipped this session. Phase 1.2 (free / malloc through
|
||||
@@ -300,6 +460,8 @@ Open follow-ups, in roughly the order they make sense:
|
||||
the canonical buffer/string case is already handled by `[]T` /
|
||||
`string`.
|
||||
|
||||
</details>
|
||||
|
||||
## Phase 0.3 audit findings — chess allocator usage (closed)
|
||||
|
||||
After Step 5 / matchContextAllocCall removal, every consumer call
|
||||
@@ -324,7 +486,84 @@ Allocator value naturally.
|
||||
|
||||
## Log
|
||||
|
||||
- **2026-05-25 (latest)** — `resolveType(null) → .s64` removed.
|
||||
- **2026-06-11 (latest)** — Redundant flat `#import "modules/std/mem.sx"`
|
||||
dropped from the facade (`c75cd9c`, Agra spotted it): the tail's
|
||||
`mem ::` import already covers the graph needs (ufcs helpers +
|
||||
CAllocator); the double import was duplicating lowered IR (~2.5k
|
||||
lines across 37 re-pinned .ir snapshots, output byte-identical).
|
||||
Gates: suite 588/588, zbt 0, m3te 23/23, game builds.
|
||||
- **2026-06-11 (prior)** — std.sx restructured to a pure re-export
|
||||
facade (`49a36bb`): all implementations moved to std/core.sx /
|
||||
std/fmt.sx / std/list.sx; std.sx = alias decls + namespace tail.
|
||||
En route, two more issues filed AND resolved: 0121 fn aliases
|
||||
(`721369a` — renamed aliases were broken for EVERY fn kind, not
|
||||
just packs; scan-time fn_ast_map registration via the shared
|
||||
alias-chain walk; example 0546) and 0122 ambient source-context
|
||||
bugs in convergeClosureShapeSets / checkErrorFlow / unknown-type
|
||||
loop (`340be40` — latent on master, exposed by the facade).
|
||||
Probe-verified before executing: protocols (plain + Into xx path),
|
||||
#builtin / #foreign aliases, reserved `string` (no alias needed or
|
||||
possible). 37 .ir re-pins (pure renumbering). Gates: zbt 426/426,
|
||||
suite 588/588, m3te 23/23, game builds + bundles.
|
||||
- **2026-06-11 (prior)** — Issue 0120 filed AND resolved (Agra-directed
|
||||
same-session fix). Found probing the std.sx-as-pure-re-exports
|
||||
restructure: generic-struct head alias (`BoxAlias :: Box;`) lowered
|
||||
silently to `.unresolved` → LLVM backend panic; cross-module
|
||||
`Box :: r.Box;` re-export invisible. Fix: `selectGenericStructHead`
|
||||
follows const-alias decls hop-by-hop from each ALIAS AUTHOR's source
|
||||
(`aliasedStructTemplate`, nominal.zig; `namespaceAliasVerdictFrom`
|
||||
for `ns.X` RHS), checked before the template map so a facade's
|
||||
same-name re-export beats an invisible global template; plus the
|
||||
missing "unknown type" diagnostic on the `.call` type-head tail
|
||||
(resolveTypeCallWithBindings). Also fixed PRE-EXISTING stale unit
|
||||
test (calls.test.zig UFCS plan — predated a47ea14's opt-in model;
|
||||
master was 425/426). specs.md Type Aliases + readme re-export
|
||||
section + Decisions Log updated. Regression: examples/0211 (+rich/
|
||||
+facade companions). Gates: zbt 426/426, suite 587/587.
|
||||
- **2026-06-11 (prior)** — BufAlloc.init by-value (`51194a2`, Agra
|
||||
request): init no longer carves its state struct off the buffer's
|
||||
head (`-> BufAlloc`, plain literal; the old `-> *BufAlloc` cost 24
|
||||
bytes of every buffer and returned null under min-size). The
|
||||
CLAUDE.md by-value convention now holds for ALL allocators.
|
||||
Regression: examples/0839 (full-capacity 64+64 on a 128 buffer —
|
||||
failed pre-fix; exact-fit, overflow, reset). 0129's pinned output
|
||||
unchanged. .ir churn: init's signature (sret) + renumbering.
|
||||
Gates: 586/586, zbt 0, m3te 23/23, game builds.
|
||||
- **2026-06-11 (earlier)** — Opt-in UFCS (`a47ea14`). Agra's model:
|
||||
dot-calls opt-in via `:: ufcs (...)` marker or `:: ufcs target;`
|
||||
alias; plain fns direct/`|>`-only. Parser (ufcs-fn form,
|
||||
FnDecl.is_ufcs), call-plan + lowering gates (calls.zig,
|
||||
lower/call.zig), protocol-receiver fall-through, generic dispatch
|
||||
with receiver-bound `$T`, inferGenericReturnType → buildTypeBindings
|
||||
(fixes pre-existing `T{}` mis-typing of structured-param generics).
|
||||
6 reliant examples migrated; mem helpers marked ufcs; specs §UFCS
|
||||
rewritten; tests 0053+1166+0838. 585/585, zbt 0, m3te 23/23, game
|
||||
builds. 0119 closed with the full arc in its banner.
|
||||
- **2026-06-11 (later)** — Phase 2.2 shipped (`84e0fb0`). Typed helpers
|
||||
in std/mem.sx (create/destroy/alloc/free/clone/resize/mem_realloc,
|
||||
era-complete bodies, no zero-init); bare malloc/free dropped from
|
||||
std.sx (6 example files + game pieces.sx migrated to libc_*). The
|
||||
0119 "blocker" resolved as Agra's language ruling: generic free fns
|
||||
are NOT dot-rewritten — fluent spelling is `|>` (parse-time desugar
|
||||
→ direct call → normal monomorphization; verified on protocol +
|
||||
slice receivers). specs.md §UFCS corrected; 0119 RESOLVED banner.
|
||||
Regression examples/0838 (direct + |> spellings; tracker 8/8).
|
||||
37 .ir re-pins (const-pool renumbering). Gates: 583/583, zbt 0,
|
||||
m3te 23/23, game builds. String-clone deferred (string doesn't bind
|
||||
[]$T — known gap noted).
|
||||
- **2026-06-11** — Allocator primitive rename (`88bae3c`): protocol
|
||||
`alloc`→`alloc_bytes`, `dealloc`→`dealloc_bytes` (2-method-era
|
||||
signatures unchanged). Phase 4's naming piece pulled forward (Agra
|
||||
Option A) so Phase 2.2 helpers land final-named once. Touched:
|
||||
std.sx decl, mem.sx 6 impls, 4 library files, 13 examples (incl.
|
||||
two custom Tracer impls — initially missed, caught by pre-pin
|
||||
stdout review after the first --update captured their broken
|
||||
output; restored, fixed, re-pinned), interp.zig thunk-name strings,
|
||||
37 .ir snapshots. Externals migrated + gated: game (SxChess.app
|
||||
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) → .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
|
||||
@@ -334,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
|
||||
@@ -377,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.
|
||||
@@ -448,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
|
||||
@@ -520,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
|
||||
@@ -542,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`.
|
||||
|
||||
@@ -556,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;
|
||||
}
|
||||
@@ -565,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
|
||||
@@ -681,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
// block (only one terminator per block).
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/build.sx";
|
||||
|
||||
do_it :: () -> bool {
|
||||
inline if OS != .ios { return false; }
|
||||
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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
Color :: enum { red; green; blue; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
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));
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
Point :: struct { x, y: i32; }
|
||||
|
||||
Shape :: enum {
|
||||
circle: f32;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
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
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
Point :: struct { x, y: s32; }
|
||||
Point :: struct { x, y: i32; }
|
||||
|
||||
main :: () {
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
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 :: (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 :: (c: *Counter) { c.n = 0; }
|
||||
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
|
||||
|
||||
35
examples/0053-basic-ufcs-opt-in.sx
Normal file
35
examples/0053-basic-ufcs-opt-in.sx
Normal file
@@ -0,0 +1,35 @@
|
||||
// Free-function dot-calls are OPT-IN. Two opt-in spellings:
|
||||
// name :: ufcs (params) { body } — the fn itself is dot-callable
|
||||
// name :: ufcs target; — dot-callable (renaming) alias
|
||||
// A plain fn is callable directly or via `|>` only (see 1166 for the
|
||||
// rejection). Generic ufcs fns dispatch through normal monomorphization,
|
||||
// and the plan-side return type binds from the receiver (structured
|
||||
// params like `[]$T` included).
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
bump :: (x: i64) -> i64 { x + 1 }
|
||||
bump2 :: ufcs (x: i64) -> i64 { x + 2 }
|
||||
bump3 :: ufcs bump;
|
||||
|
||||
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 : i64 = 40;
|
||||
print("marked: {}\n", f.bump2()); // 42
|
||||
print("alias: {}\n", f.bump3()); // 41
|
||||
print("direct: {}\n", bump(f)); // 41 — plain fn, direct
|
||||
print("pipe: {}\n", f |> bump()); // 41 — plain fn, pipe
|
||||
print("marked-direct: {}\n", bump2(f)); // 42 — marked fn callable directly
|
||||
|
||||
c := Counter.{ n = 10 };
|
||||
print("ptr-recv: {}\n", c.inc(5)); // 15 — auto address-of receiver
|
||||
|
||||
arr := .[7, 8, 9];
|
||||
xs : []i64 = arr;
|
||||
print("generic-dot: {}\n", xs.gfirst()); // 7
|
||||
print("generic-direct: {}\n", gfirst(xs)); // 7 — plan types it i64, not a T stub
|
||||
}
|
||||
33
examples/0054-basic-dot-call-default-args.sx
Normal file
33
examples/0054-basic-dot-call-default-args.sx
Normal file
@@ -0,0 +1,33 @@
|
||||
// Trailing parameter defaults fill on method and ufcs dot-calls (the
|
||||
// receiver-prepending dispatch paths), matching bare-call expansion (0044);
|
||||
// a `#caller_location` default and a slice variadic keep their flexible
|
||||
// arity under the call-arity check.
|
||||
// Regression (issue 0123).
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
Point :: struct {
|
||||
x: i64;
|
||||
scaled :: (self: Point, k: i64 = 2) -> i64 { return self.x * k; }
|
||||
}
|
||||
|
||||
bump :: ufcs (p: Point, by: i64 = 10) -> i64 { return p.x + by; }
|
||||
|
||||
sum_var :: (..xs: []i64) -> i64 {
|
||||
t := 0;
|
||||
for xs (x) { t = t + x; }
|
||||
return t;
|
||||
}
|
||||
|
||||
here :: (loc: Source_Location = #caller_location) -> i64 { return loc.line; }
|
||||
|
||||
main :: () {
|
||||
p := Point.{ x = 5 };
|
||||
print("{}\n", p.scaled()); // default filled on method dispatch
|
||||
print("{}\n", p.scaled(3)); // explicit overrides
|
||||
print("{}\n", p.bump()); // default filled on ufcs dispatch
|
||||
print("{}\n", p.bump(1));
|
||||
print("{}\n", sum_var()); // variadic: zero args
|
||||
print("{}\n", sum_var(1, 2, 3)); // variadic: many args
|
||||
print("{}\n", here() > 0); // #caller_location default
|
||||
}
|
||||
45
examples/0055-basic-large-stack-array.sx
Normal file
45
examples/0055-basic-large-stack-array.sx
Normal file
@@ -0,0 +1,45 @@
|
||||
// Large (64KB+) stack arrays compile and are accessed in place: `---`
|
||||
// emits no initializer store, and element reads GEP the array's storage
|
||||
// instead of loading the whole array as a value.
|
||||
//
|
||||
// Regression (issue 0124): both whole-aggregate shapes — the undef
|
||||
// store from `---` and `index_get` on the loaded array value —
|
||||
// scalarized into one SelectionDAG node per element and segfaulted
|
||||
// `sx build` at [65536]u8.
|
||||
//
|
||||
// Results print via out/int_to_string: `{}` formatting would pull the
|
||||
// any_to_string dispatcher, whose array arms materialize every interned
|
||||
// array type BY VALUE — the separate issue 0125.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
checksum :: () -> i64 {
|
||||
buf : [65536]u8 = ---;
|
||||
i := 0;
|
||||
while i < 65536 {
|
||||
buf[i] = xx (i % 251);
|
||||
i += 1;
|
||||
}
|
||||
sum := 0;
|
||||
i = 0;
|
||||
while i < 65536 {
|
||||
sum += xx buf[i];
|
||||
i += 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
big :: () -> i64 {
|
||||
buf : [131072]i64 = ---;
|
||||
buf[0] = 11;
|
||||
buf[131071] = 31;
|
||||
return buf[0] + buf[131071];
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
out(int_to_string(checksum()));
|
||||
out("\n");
|
||||
out(int_to_string(big()));
|
||||
out("\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
@@ -25,7 +25,7 @@ Container :: struct {
|
||||
}
|
||||
|
||||
main :: () -> void {
|
||||
obj : *Container = xx context.allocator.alloc(size_of(Container));
|
||||
obj : *Container = xx context.allocator.alloc_bytes(size_of(Container));
|
||||
memset(obj, 0, size_of(Container));
|
||||
obj.sentinel = 0xDEADBEEF;
|
||||
|
||||
|
||||
@@ -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,28 +7,28 @@
|
||||
//
|
||||
// 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
|
||||
// step-5.2 impl.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/objc_block.sx";
|
||||
#import "modules/ffi/objc_block.sx";
|
||||
|
||||
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));
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
// and matches the caller's two-register pass on AArch64.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/objc_block.sx";
|
||||
#import "modules/ffi/objc_block.sx";
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
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);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
Perms :: enum flags { read; write; execute; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
main :: () {
|
||||
|
||||
@@ -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
@@ -1,8 +1,8 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
pkg :: #import "tests/fixtures/testpkg";
|
||||
|
||||
// ============================================================
|
||||
// Struct constants test
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math/math.sx";
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/std/test.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
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 :: (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;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user