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