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.
6.4 KiB
RESOLVED (2026-06-03) Root cause: the
type_name/type_eqreflection builtins resolved theirTypearg's IR type withgetRefIRType(...) orelse TypeId.i64, then gated== .any— so a failed must-succeed lookup silently became "bare i64" (.i64 != .any), reading the wrong value with no diagnostic. Fix: added the sibling classifierLLVMEmitter.reflectArgRepr(src/ir/emit_llvm.zig) which routes the lookup throughargIRTypeOrFail→.unresolvedand returns{ boxed, bare, unresolved }. The three emit sites (src/backend/llvm/ops.zigtype_name+type_eq×2) nowswitchon it:.boxedextracts theAnyvalue field,.bareuses the value directly, and.unresolvedhits a hard@panictripwire — never silently classified as bare. Happy path (real args always resolve) is byte-identical; suite stays 361/0. Secondary (confirmed intentional):src/ir/lower.zig:2531/2532(null_literal/undef_literal→target_type orelse .void) is a typeless-literal default, not a lookup-swallow —emitConstNull/emitConstUndefdeliberately handle.void(null-ptr / undef-i64). Left in place with an invariant comment. Regression test:src/ir/emit_llvm.test.zig— "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection arg ref (issue 0075)" (fail-before withorelse .i64→.bare; pass-after →.unresolved).
0075 — silent getRefIRType(...) orelse TypeId.i64 fallback in reflection builtins
Symptom
One-line: The type_name and type_eq reflection builtins resolve their Type
argument's IR type via getRefIRType(...) orelse TypeId.i64 — the forbidden
silent-type-lookup fallback (.i64 is the exact issue-0042 sentinel the project
rules name) — so a failed must-succeed lookup silently decides "not boxed (!= .any)"
and mis-handles the value with no diagnostic.
Observed (primary — must fix): self.e.getRefIRType(...) orelse TypeId.i64 at:
src/backend/llvm/ops.zig:1023(.type_namebuiltin —arg_ir_ty, gates the== .anyboxed-extract vs bare-i64 decision)src/backend/llvm/ops.zig:1049(.type_eqbuiltin — first operand)src/backend/llvm/ops.zig:1055(.type_eqbuiltin — second operand)
getRefIRType (src/ir/emit_llvm.zig:2229, ?TypeId) returns null only when a ref
is neither a function param nor a block instruction result — a must-not-happen case
for a real builtin argument. On null the code defaults to .i64, then tests
arg_ir_ty == .any; the .i64 default silently means "treat as a bare TypeId index,
not a boxed Any", so a genuinely-boxed arg whose lookup failed would skip the
ExtractValue and use the wrong value — silent miscompile, no diagnostic.
Expected: per CLAUDE.md REJECTED PATTERNS, a failed must-succeed type lookup
surfaces a diagnostic / hard tripwire (e.g. the .unresolved sentinel introduced for
issue 0074), never a real-type default.
Secondary (confirm — borderline)
src/ir/lower.zig:2527—.null_literal => constNull(self.target_type orelse .void)src/ir/lower.zig:2528—.undef_literal => constUndef(self.target_type orelse .void)target_typeis a context hint that may be legitimately absent for a barenull/undefwith no expected type — this may be an INTENTIONAL default rather than a lookup-swallow. The fix session should confirm: if anull/undefliteral reaching here without atarget_typeis actually a must-not-happen case, make it loud; if a typeless null/undef is legitimate, leave it and add a one-line comment stating the invariant.
Audited — intentional language defaults (NO action; documented so they aren't re-flagged)
src/ir/lower.zig:4855—int_literal => constInt(lit.value, info.ty orelse .i64): an untyped integer literal defaulting toi64is standard language semantics, not a lookup failure.src/ir/lower.zig:4856—float_literal => constFloat(..., info.ty orelse .f64): untyped float literal defaults tof64— language semantics.src/ir/type_bridge.zig:334—.tag_type = tag_type orelse .i64: documented ("enum unions are always tagged (default i64)") — an intentional default tag type, not a swallowed lookup.
Provenance / scope
Pre-existing, NOT introduced by the arch-refactor. Discovered during the issue-0074
fix (the fix worker surfaced the reflection .i64 fallbacks as a separate pattern
outside 0074's FFI-arg scope) and confirmed by a manager sweep
(rg "orelse \.(i64|void|...)" src). Filed per the IMPASSIBLE RULE (existing
default-returns that swallow a lookup failure → file, don't fix in place).
Reproduction
Latent / static (same nature as 0074): well-formed IR always gives a builtin arg a
resolvable type, so the .i64 default can't be driven at runtime today — which is why
it's dangerous (a future IR change would silently miscompile type_name/type_eq).
Exercised by the comptime/reflection examples; the fix must keep the suite at 361/0.
Investigation prompt (ready to paste into a fresh session)
In
/Users/agra/projects/sx, the.type_nameand.type_eqreflection builtins insrc/backend/llvm/ops.zig(lines 1023, 1049, 1055) resolve a Type argument's IR type with the forbidden silent fallbackgetRefIRType(...) orelse TypeId.i64, used to gate a== .anyboxed-vs-bare decision. Issue 0074 already added the shared resolverLLVMEmitter.argIRTypeOrFail(src/ir/emit_llvm.zig) returning the dedicated.unresolvedsentinel on a failed lookup. Route these three sites through that helper (or a sibling) so a failed lookup yields.unresolved— never.i64; then==.anyis false for.unresolvedAND you must make the unresolved case loud (diagnostic viaself.diagnostics.addFmt(.err, span, ...)or a hard tripwire), not silently "bare i64". Also resolve the borderlinelower.zig:2527/2528target_type orelse .void(confirm intentional vs make-loud; comment the invariant either way). Leave the audited intentional defaults (lower.zig:4855/4856,type_bridge.zig:334) untouched. Verify:/Users/agra/.zvm/bin/zig build && /Users/agra/.zvm/bin/zig build test && bash tests/run_examples.shstays 361/0; add a*.test.zigregression test asserting the loud.unresolvedpath for atype_name/type_eqarg with an unresolvable ref (fail-before/pass-after). Expected new behavior: an unresolved reflection-builtin arg type surfaces loudly, never silently defaults to.i64.