# 0120 — aliasing a GENERIC struct head: silent `.unresolved`, backend panic > **RESOLVED** (2026-06-11, same session — Agra-directed fix). Root > cause: a const alias of a generic struct head was registered nowhere > (`type_alias_map` holds TypeIds, `struct_template_map` only direct > struct decls), and the head selector's miss fell through as > `.not_generic`; the `.call`-node type resolver then returned > `.unresolved` SILENTLY (its parameterized sibling diagnosed; it > didn't). Fix, option 1 (support): `selectGenericStructHead` now > follows const-alias decls (`aliasedStructTemplate` in > `src/ir/lower/nominal.zig`) — own-wins / single-flat author, each hop > resolved from the ALIAS AUTHOR's source (`namespaceAliasVerdictFrom` > for `ns.X` RHS), depth-capped against cycles, checked BEFORE the > template map so a facade's same-name re-export beats an invisible > global template. Plus the missing diagnostic: an unknown `.call` type > head now errors "unknown type 'X'" instead of silently poisoning > (`resolveTypeCallWithBindings`). Alias-vs-alias flat collisions stay > loud (not-visible diagnostic). Still unsupported, by scope: > `ns.AliasName(..)` qualified heads (namespace member that is itself > an alias). Regression test: > `examples/0211-generics-struct-alias-head.sx` (+ `-rich.sx` / > `-facade.sx` companions; pins same-file alias, method, chain, > annotation, and the cross-module facade re-export). Gates: zig build > test 426/426 (incl. fixing the PRE-EXISTING stale > `calls.test.zig` UFCS plan test that predated 0119's opt-in model), > suite 587/587. ## Symptom `Alias :: Box;` where `Box` is a generic struct (`struct ($T: Type)`) lowers without any diagnostic, and instantiating through the alias (`Alias(i64).{ ... }`) reaches LLVM emission with an `.unresolved` type — the backend tripwire panics: ``` panic: unresolved type reached LLVM emission — a type resolution failure was not diagnosed/aborted src/backend/llvm/types.zig:175 toLLVMTypeInfo src/backend/llvm/ops.zig:1204 emitStructInit ``` Observed (one probe family, three manifestations of the same root): - field access through the aliased instantiation → **backend panic** (no front-end diagnostic at all); - method call through the aliased instantiation (`b.get()`) → misleading `unresolved 'get'` (the receiver's type never resolved); - cross-module re-export (`facade.sx`: `Box :: r.Box;`, consumer flat-imports facade) → consumer gets `type 'Box' is not visible; #import the module that declares it` even though the alias is the facade's OWN declaration. Expected: one of the two, decided explicitly — 1. **Support it** (desirable): a const decl whose RHS names a generic struct head (bare `Box` or qualified `r.Box`) binds the alias to the SAME template; instantiation, methods, and one-level flat-import carry behave exactly as the non-generic struct alias already does. 2. **Reject it loudly**: a decl-site diagnostic ("cannot alias a generic struct head" or similar) at `Alias :: Box;`. Silently lowering and panicking in the backend is neither — it is the REJECTED-PATTERNS "silent unresolved" shape. For contrast, both of these alias re-exports already WORK across one flat-import hop (own-decl visibility): `helper :: r.helper;` (plain fn) and `Thing :: r.Thing;` (non-generic struct, including its static `init`). Only the generic head breaks. A fix must not regress these. ## Reproduction Backend panic (primary): ```sx #import "modules/std.sx"; Box :: struct ($T: Type) { item: T; } BoxAlias :: Box; main :: () { b := BoxAlias(i64).{ item = 3 }; print("{}\n", b.item); } ``` Method-call variant (front-end `unresolved 'get'`, same root): ```sx #import "modules/std.sx"; Box :: struct ($T: Type) { item: T; get :: (b: *Box(T)) -> T { b.item } } BoxAlias :: Box; main :: () { b := BoxAlias(i64).{ item = 3 }; print("{}\n", b.get()); } ``` Cross-module variant (`rich.sx` declares `Box`; `facade.sx` has `r :: #import "rich.sx"; Box :: r.Box;`; a consumer flat-importing facade.sx gets `type 'Box' is not visible` at `Box(i64).{ ... }`). ## Investigation prompt Generic structs live as TEMPLATES in `src/ir/program_index.zig` — `struct_template_map` (`StringHashMap(StructTemplate)`, registered by `registerStructDecl`; a parallel `struct_template_by_decl` exists but isn't read for selection yet). Instantiation resolves the head name against that map in `src/ir/lower/nominal.zig` (see the qualified-head comments around nominal.zig:357–382) and monomorphizes via `lower_generic.instantiateGenericStruct` (re-exported at `src/ir/lower.zig:1820`). `BoxAlias :: Box;` is a const decl whose RHS identifier names a template, not a value or a concrete Type — const-decl lowering neither registers `BoxAlias` as a template alias nor rejects the decl. The instantiation head lookup for `BoxAlias` then misses, and the `Name(args).{ ... }` path continues with an `.unresolved` struct type instead of diagnosing the miss — that silent continuation is the bug underneath all three manifestations, and fixing it is step one regardless of the language decision: a struct_init whose head fails to resolve must produce a hard diagnostic, never reach emission. Then the language decision (confirm with Agra if option 2 is ever preferred; the motivating use case wants option 1): when a const decl's RHS resolves to a generic struct head — bare identifier or `ns.X` through a namespace alias — register the alias name in the template registry bound to the same `StructTemplate`, scoped to the declaring module with ordinary own-decl visibility so one-level flat-import carry works (mirror whatever makes `Thing :: r.Thing;` re-export correctly today). Mind collision semantics (own-wins / ambiguity) and that the alias must also work as a plain type head in annotations (`x: BoxAlias(i64)`), nested generics (`List(BoxAlias(i64))` if applicable), and method/UFCS dispatch on instantiations through the alias. Motivating context: the std.sx-as-pure-re-exports restructure wants `List :: list.List;` in `modules/std.sx` (with `list :: #import "modules/std/list.sx";`) so `List` stays bare-visible to std.sx's flat importers. Plain fns and plain structs already re-export this way; generic heads are the missing piece. Verification: 1. Primary repro: prints `3`, exit 0 (option 1) — or a clean decl-site diagnostic, no panic (option 2). 2. Matrix: method-call variant runs (`b.get()` → 3); cross-module variant runs through the facade; `helper :: r.helper;` and `Thing :: r.Thing;` re-exports unchanged; two facades carrying the same alias name still diagnose ambiguity. 3. `bash tests/run_examples.sh` — full suite ok, zero failures. 4. Pin the repro as a regression example per CLAUDE.md.