From 95e61d8a86711cca18e1eaaf466b7b93905771b5 Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 27 May 2026 19:16:19 +0300 Subject: [PATCH] =?UTF-8?q?ffi=20M5.A.next.4A.bare.4.A:=20dynamic=20type?= =?UTF-8?q?=5Fname(args[i])=20=E2=80=94=20expected-failing=20lock-in?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 4A final follow-up's lock-in. `type_name()` where is NOT a statically resolvable type expression (e.g. `list[i]` indexing into a `$args`-derived `[]Type` slice) silently folds to "s64" today because `resolveTypeArg`'s index_expr fall-through returns `.s64` (the catch-all `else => .s64` at the bottom of the switch). This is exactly the kind of silent unimplemented arm the project's REJECTED PATTERNS section forbids — the user gets "s64" for every element of an arbitrary pack, not the per- position concrete type they expect. `examples/171-pack-dynamic-type-name.sx` exercises a builder- shaped fn: walks `$args` via runtime indexing, calls `type_name(list[i])` per position, concatenates the results. For `walk(42, "hi")` the expected output is "s64string". Today's output is "s64s64" — the silent fold strikes twice. Cadence shape 2: expected output is the WORKING shape; today's diff fails. Next commit teaches `tryLowerReflectionCall` to detect "arg not statically resolvable" and emit a builtin_call to `.type_name` so the interp's runtime arm (wired in commit 9600ba5, M5.A.next.4.1) handles the dynamic case. 210/210 + 1 expected-failing = 211 total. zig build test green. --- examples/171-pack-dynamic-type-name.sx | 34 +++++++++++++++++++ .../expected/171-pack-dynamic-type-name.exit | 1 + tests/expected/171-pack-dynamic-type-name.txt | 1 + 3 files changed, 36 insertions(+) create mode 100644 examples/171-pack-dynamic-type-name.sx create mode 100644 tests/expected/171-pack-dynamic-type-name.exit create mode 100644 tests/expected/171-pack-dynamic-type-name.txt diff --git a/examples/171-pack-dynamic-type-name.sx b/examples/171-pack-dynamic-type-name.sx new file mode 100644 index 0000000..174ef4a --- /dev/null +++ b/examples/171-pack-dynamic-type-name.sx @@ -0,0 +1,34 @@ +// Variadic heterogeneous type packs — step 4A final-slice +// follow-up. `type_name()` where the argument is +// NOT a static type expression (e.g. `list[i]` indexing into +// a `$args`-derived `[]Type` slice) silently folds to "s64" +// today because `resolveTypeArg`'s index_expr fall-through +// returns `.s64`. That's exactly the kind of silent unimplemented +// arm the CLAUDE.md REJECTED PATTERNS section forbids. +// +// Next commit teaches `tryLowerReflectionCall` to detect "arg +// not statically resolvable" and emit a `builtin_call` to the +// new `.type_name` builtin. The interp's arm (already wired in +// commit 9600ba5) reads the runtime `.type_tag` Value and +// returns the per-position concrete type name. +// +// Expected output after fix: +// s64string + +#import "modules/std.sx"; + +walk :: (..$args) -> string { + list := $args; + s := ""; + i : s64 = 0; + while i < list.len { + s = concat(s, type_name(list[i])); + i = i + 1; + } + return s; +} + +main :: () -> s32 { + print("{}\n", walk(42, "hi")); + return 0; +} diff --git a/tests/expected/171-pack-dynamic-type-name.exit b/tests/expected/171-pack-dynamic-type-name.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/171-pack-dynamic-type-name.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/171-pack-dynamic-type-name.txt b/tests/expected/171-pack-dynamic-type-name.txt new file mode 100644 index 0000000..d3a2664 --- /dev/null +++ b/tests/expected/171-pack-dynamic-type-name.txt @@ -0,0 +1 @@ +s64string