> **RESOLVED** (2026-06-03)
> **Root cause:** the `type_name` / `type_eq` reflection builtins resolved their
> `Type` arg's IR type with `getRefIRType(...) orelse TypeId.s64`, then gated `== .any`
> — so a failed must-succeed lookup silently became "bare i64" (`.s64 != .any`),
> reading the wrong value with no diagnostic.
> **Fix:** added the sibling classifier `LLVMEmitter.reflectArgRepr`
> (`src/ir/emit_llvm.zig`) which routes the lookup through `argIRTypeOrFail` →
> `.unresolved` and returns `{ boxed, bare, unresolved }`. The three emit sites
> (`src/backend/llvm/ops.zig` `type_name` + `type_eq` ×2) now `switch` on it: `.boxed`
> extracts the `Any` value field, `.bare` uses the value directly, and `.unresolved`
> hits a hard `@panic` tripwire — 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`/`emitConstUndef` deliberately 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 with
> `orelse .s64` → `.bare`; pass-after → `.unresolved`).

# 0075 — silent `getRefIRType(...) orelse TypeId.s64` 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.s64` — the forbidden
silent-type-lookup fallback (`.s64` 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.s64` at:
- `src/backend/llvm/ops.zig:1023`  (`.type_name` builtin — `arg_ir_ty`, gates the
  `== .any` boxed-extract vs bare-i64 decision)
- `src/backend/llvm/ops.zig:1049`  (`.type_eq` builtin — first operand)
- `src/backend/llvm/ops.zig:1055`  (`.type_eq` builtin — 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 `.s64`, then tests
`arg_ir_ty == .any`; the `.s64` 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_type` is a context hint that may be legitimately absent for a bare
  `null`/`undef` with no expected type — this may be an INTENTIONAL default rather
  than a lookup-swallow. The fix session should confirm: if a `null`/`undef` literal
  reaching here without a `target_type` is 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 .s64)`:
  an untyped integer literal defaulting to `s64` is standard language semantics, not a
  lookup failure.
- `src/ir/lower.zig:4856` — `float_literal => constFloat(..., info.ty orelse .f64)`:
  untyped float literal defaults to `f64` — language semantics.
- `src/ir/type_bridge.zig:334` — `.tag_type = tag_type orelse .s64`: 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 `.s64` fallbacks as a separate pattern
outside 0074's FFI-arg scope) and confirmed by a manager sweep
(`rg "orelse \.(s64|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 `.s64` 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_name` and `.type_eq` reflection builtins in
> `src/backend/llvm/ops.zig` (lines 1023, 1049, 1055) resolve a Type argument's IR type
> with the forbidden silent fallback `getRefIRType(...) orelse TypeId.s64`, used to gate
> a `== .any` boxed-vs-bare decision. Issue 0074 already added the shared resolver
> `LLVMEmitter.argIRTypeOrFail` (`src/ir/emit_llvm.zig`) returning the dedicated
> `.unresolved` sentinel on a failed lookup. Route these three sites through that helper
> (or a sibling) so a failed lookup yields `.unresolved` — never `.s64`; then `==.any`
> is false for `.unresolved` AND you must make the unresolved case loud (diagnostic via
> `self.diagnostics.addFmt(.err, span, ...)` or a hard tripwire), not silently "bare
> i64". Also resolve the borderline `lower.zig:2527/2528` `target_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.sh` stays 361/0; add a `*.test.zig` regression test asserting
> the loud `.unresolved` path for a `type_name`/`type_eq` arg with an unresolvable ref
> (fail-before/pass-after). Expected new behavior: an unresolved reflection-builtin arg
> type surfaces loudly, never silently defaults to `.s64`.
