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.
72 lines
3.3 KiB
Markdown
72 lines
3.3 KiB
Markdown
# 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 :: <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.
|