Plan the `race` async deliverable (roadmap A1): a structured first-wins over the M:1 `Task` layer, returning a comptime-synthesized tagged-union mirroring the input named tuple's labels. Identifies the one net-new compiler primitive needed — `pointee($P: Type) -> Type` (project `*Task(A)` → `A`); everything else (tuple reflection, union synthesis, the suspending runtime) is already in place. Issue 0196: a named-tuple type ALIAS (`NT :: Tuple(a: i64, b: bool)`) loses its structure — field access and reflection both fail on the alias, while the inline and `$T`-type-parameter forms work. Not on the race critical path (race reflects a tuple type parameter), filed for tracking.
3.3 KiB
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
#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:
// 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 : NTthenx.areports "field 'a' not found on type 'NT'", andfield_count(NT)/field_name(NT, i)report "unresolved type: 'NT'". The inline form and a generic$Ttype parameter bound to the sameTuple(...)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-fnNT :: () -> 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 underlyingTupleInfo(hence both "unresolved type" in reflection and "field not found" in member access). Check the type-alias decl lowering path (whereName :: <type-expr>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.