# issue-0042 — Const-decl type aliases (`MyInt :: s32;`) silently return `.s64` from `size_of` / `align_of`

## Symptom

A type alias declared via `Foo :: SomeType;` is registered in the
lowering's `type_alias_map` but is **never consulted** when the alias
name is later used as a type argument to `size_of` / `align_of`. The
fallback returns `.s64` (8 bytes) — which coincidentally produces a
correct result for any alias whose underlying type is 8 bytes
(`*T`, `f64`, function pointers, `s64`, `u64`), silently masking the
bug for years.

Observed:
```
size_of(s32)    = 4    ← direct, correct
size_of(MyInt)  = 8    ← via alias, WRONG (expected 4)
```

Where `MyInt :: s32;`.

## Reproduction

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

MyInt :: s32;

main :: () -> s32 {
    print("direct: {}\n", size_of(s32));    // 4
    print("alias:  {}\n", size_of(MyInt));  // 8 — should be 4
    0;
}
```

`./zig-out/bin/sx run` against unmodified master prints:

```
direct: 4
alias:  8
```

## Why this surfaces now

issue-0041 work extends the const-decl alias path to register
pointer, optional, array, slice, many-pointer, and function-type
aliases (`Ptr :: *u8;`, `Maybe :: ?u8;`, `Arr :: [3]u8;`,
`Cb :: (s32) -> s32;`). Every one of those aliases ends up in
`type_alias_map`, then `size_of(<alias>)` falls through the same
`.identifier` branch that ignores the map — returning `.s64` (8).
For pointer and function-type aliases this is coincidentally right
(8 bytes). For optional, array, etc. it produces silently-wrong
sizes (`size_of(Maybe) = 8` instead of 2;
`size_of(Arr) = 8` instead of 3).

The issue-0041 work cannot land without this being fixed — the
test snapshots would pin in the wrong values and the new feature
would ship subtly broken.

## Investigation prompt

> The bug lives in `src/ir/lower.zig`, in `resolveTypeArg`
> ([line ~7132](src/ir/lower.zig#L7132)). The `.identifier`
> branch looks like:
>
> ```zig
> .identifier => |id| {
>     if (self.type_bindings) |tb| {
>         if (tb.get(id.name)) |ty| return ty;
>     }
>     const name_id = self.module.types.internString(id.name);
>     return self.module.types.findByName(name_id) orelse .s64;
> },
> ```
>
> It checks `type_bindings` (generic-monomorphization) and
> `findByName` (registered named types), but never consults
> `self.type_alias_map` — which is where the const-decl alias
> registration in `lower.zig:425` puts entries. The neighbouring
> `.type_expr` branch (line ~7143) DOES check `type_alias_map`:
>
> ```zig
> .type_expr => |te| {
>     if (self.type_alias_map.get(te.name)) |alias_ty| return alias_ty;
>     return type_bridge.resolveAstType(node, &self.module.types);
> },
> ```
>
> Why two branches: an `.identifier` AST node is what parsePrimary
> emits for non-keyword names; `.type_expr` is what it emits for
> built-in primitive names recognised by `Type.fromName` (`s32`,
> `u8`, etc.) and for the `f32`/`f64`/`Type` keywords. User-defined
> alias names like `MyInt` and `Ptr` flow through `.identifier`.
>
> **Likely fix:** mirror the `type_alias_map.get` lookup in the
> `.identifier` branch — try alias map first (or before/after
> findByName, whichever is the established precedence elsewhere).
>
> ```zig
> .identifier => |id| {
>     if (self.type_bindings) |tb| {
>         if (tb.get(id.name)) |ty| return ty;
>     }
>     if (self.type_alias_map.get(id.name)) |alias_ty| return alias_ty;
>     const name_id = self.module.types.internString(id.name);
>     return self.module.types.findByName(name_id) orelse .s64;
> },
> ```
>
> **Verification:**
> 1. Add the repro above as `examples/issue-0042.sx`.
> 2. `bash tests/run_examples.sh --update` to capture expected
>    output (`alias: 4`, not `alias: 8`).
> 3. Make sure existing snapshots that test type aliases (search
>    `examples/` for `::` patterns followed by `size_of`) don't
>    change in unexpected ways.
>
> **Possible adjacency:** the issue may extend to `align_of`
> (likely same call path) and to type-alias chains
> (`A :: s32; B :: A;` — does B resolve through A's alias entry?).
> Worth pinning down with a test once the primary fix lands.

## Plan-level impact

Blocks issue-0041 (compound-type-as-expression). Once 0042 is
fixed, 0041 work can resume from the testing phase (the parser and
lowering edits for 0041 are already in place; only the alias
lookup is broken).

## Suggested fix order

1. Land 0042's `.identifier` alias-map lookup.
2. Resume 0041 from the test step — re-run `examples/issue-0041.sx`
   and verify `size_of(Maybe) = 2`, `size_of(Arr) = 3`, etc.
3. Regenerate snapshots and proceed with the 0041 finishing
   steps (50-smoke, rename, etc.).
