# 0144 — unrecognized `$T`-param `#builtin` silently returns 0

## Symptom

A **bodiless `#builtin`** whose signature takes a `$T: Type` parameter, when the
compiler does **not** recognize its name, **silently evaluates to `0`** (process
exit 0) instead of emitting a loud "unknown builtin" diagnostic.

- **Observed:** `mystery(i64, 42)` prints `0`, exit 0.
- **Expected:** a loud compile error naming the unrecognized builtin (e.g.
  `error: unknown #builtin 'mystery'`) + non-zero exit.

Contrast: a bodiless `#builtin` WITHOUT a type parameter (e.g.
`noret :: (x: i64) #builtin;`) is lowered as an ordinary external call and fails
**loudly** at link time (`JIT session error: Symbols not found: [ _noret ]`). So
the silent path is specific to the `$T: Type`-parametrized (reflection-builtin-
shaped) form, which is routed through the reflection/type-arg lowering and falls
through to a zero/`i64` default rather than being rejected.

This is the exact class CLAUDE.md "REJECTED PATTERNS" forbids: a lookup that
fails (here: "is this a recognized builtin?") returns a plausible-looking default
(`0`) instead of surfacing the failure. It was discovered during the atomics
stream — before `atomic_load`/`atomic_store` recognition existed, `Atomic($T)`'s
methods calling those `#builtin`s ran to `0` with exit 0 instead of erroring.

## Reproduction

Standalone (only needs `modules/std.sx`):

```sx
#import "modules/std.sx";

// Bodiless #builtin the compiler does not recognize, with a `$T: Type` param.
mystery :: ($T: Type, x: T) -> T #builtin;

main :: () {
    print("mystery(42) = {}\n", mystery(i64, 42));   // prints 0, exit 0
}
```

Also reproduces through a generic-struct method (the original atomics shape):

```sx
#import "modules/std.sx";
mystery :: ($T: Type, x: T) -> T #builtin;
Box :: struct ($T: Type) {
    v: T;
    get :: (self: *Box(T)) -> T { return mystery(T, self.v); }
}
main :: () {
    b := Box(i64).{ v = 42 };
    print("{}\n", b.get());   // prints 0, exit 0
}
```

## Investigation prompt (paste into a fresh session)

> Fix issue 0144: an unrecognized bodiless `#builtin` with a `$T: Type` parameter
> silently lowers to `0` (exit 0) instead of erroring. Repro:
> `mystery :: ($T: Type, x: T) -> T #builtin;` then `print("{}\n", mystery(i64, 42))`
> prints `0`. Expected: a loud "unknown #builtin" diagnostic + non-zero exit (the
> non-type-param `#builtin` form already fails loudly at link time, so only the
> type-param/reflection-shaped path is silent).
>
> Suspected area: `src/ir/lower/call.zig` — the reflection-builtin recognition
> (`tryLowerReflectionCall`) declines unknown names, and the call then falls
> through a path that resolves the `$T` arg / folds to a default `0` / `.i64`
> rather than rejecting. Trace where a bodiless `#builtin` call whose name matches
> NO recognizer (reflection / atomic intrinsics / etc.) ends up producing a
> `const_int 0` (or an `else => .i64`-style default in `resolveTypeArg` or the
> builtin-call lowering). The fix: when a call resolves to a bodiless `#builtin`
> decl that no recognizer claimed, emit
> `self.diagnostics.addFmt(.err, callee.span, "unknown #builtin '{s}'", .{name})`
> and return a distinct sentinel — never a silent `0`. Grep for the `#builtin`
> body marker handling + how bodiless-decl calls are lowered. Verification: run the
> repro, expect the new diagnostic + non-zero exit; confirm `zig build test` stays
> green (no legitimate `#builtin` is newly rejected — every shipped builtin IS
> recognized, so only genuinely-unknown names should newly error). Add a `11xx`
> diagnostic example locking the new error.
