# Issue 0196 — a named-tuple type alias (`NT :: Tuple(a: i64, b: bool)`) loses its structure Status: **OPEN.** Found while building `race` (does NOT block it — `race` reflects a tuple type PARAMETER `$T`, which works; this is the narrower *named alias* path). Filed so the inconsistency is tracked. ## Symptom Binding a named tuple type to a `::` alias and then using the alias drops the tuple's field structure. Both field access and comptime reflection fail on the alias, even though the identical **inline** type and a **type-parameter** `$T` bound to the same type both work. | form | `field_count` / `field_name` | field access `x.a` | |---|---|---| | inline `Tuple(a: i64, b: bool)` | ✓ works (`fc=2`, `name0=a`) | n/a | | type param `($T: Type)` ← `Tuple(a: i64, b: bool)` | ✓ works (`fc=2`, `name0=a`) | n/a | | **alias `NT :: Tuple(a: i64, b: bool)`** | ✗ `error: unresolved type: 'NT'` | ✗ `error: field 'a' not found on type 'NT'` | ## Reproduction ```sx #import "modules/std.sx"; NT :: Tuple(a: i64, b: bool); main :: () -> i32 { // (1) field access through the alias fails: x : NT = .(a = 1, b = true); print("x.a={} x.b={}\n", x.a, x.b); // error: field 'a' not found on type 'NT' // (2) reflection through the alias fails: print("fc={}\n", field_count(NT)); // error: unresolved type: 'NT' return 0; } ``` Contrast — both of these work today: ```sx // inline (see examples/comptime/0646-comptime-field-reflect-tuple-array.sx): field_count(Tuple(a: i64, b: bool)); // 2 field_name(Tuple(a: i64, b: bool), 0); // "a" // type parameter: count :: ($T: Type) -> i64 { return field_count(T); } count(Tuple(a: i64, b: bool)); // 2 ``` ## Notes / investigation prompt > A `::` alias of a named tuple type (`NT :: Tuple(a: i64, b: bool)`) doesn't behave like the inline > named tuple: `x : NT` then `x.a` reports "field 'a' not found on type 'NT'", and `field_count(NT)` / > `field_name(NT, i)` report "unresolved type: 'NT'". The inline form and a generic `$T` type > parameter bound to the same `Tuple(...)` both work, so the named-tuple TypeId is correct — the alias > binding is where the structure (or the resolvability) is lost. > > First determine whether this is intended (tuples are structural / non-nominal per specs.md §3.5, so > perhaps a `::` alias is meant to be spelled differently, e.g. a type-fn `NT :: () -> Type { ... }`) > or a genuine bug in how a `::` type-alias binds a structural tuple type. If it's a bug: the alias > likely registers a forward/opaque type entry that never resolves to the underlying `TupleInfo` > (hence both "unresolved type" in reflection and "field not found" in member access). Check the > type-alias decl lowering path (where `Name :: ` binds) and whether a structural tuple > type-expr is resolved + carried vs. left as an unresolved nominal placeholder. > > Also check the POSITIONAL case (`PT :: Tuple(i64, bool)` → `x : PT = .(1, true)`, `x.0`, > `field_count(PT)`) to see whether the breakage is named-tuple-specific or all tuple aliases. ## Why it doesn't block `race` `race` is `RaceResult :: ($T: Type) -> Type` over a tuple type **parameter** (the named tuple arrives as the inferred `$T` of the `race(tasks)` call), and reflection on a `$T` tuple parameter works (verified). The alias form is a convenience that `race` does not depend on.