feat: comptime type-call composition (field_type/pointee/field_name in value position)

A comptime-type-call's `Type` result (`field_type(T, i)`, `pointee(P)`) could only
be used in a type-arg slot — not as a `Type`-typed struct-field value, a generic
`$P: Type` argument, or a nested type-call arg — when the index was an `inline for`
loop variable. It routed through value / generic-fn lowering ("cannot infer generic
type parameter" / "unknown #builtin field_type") instead of the type-call fold. This
is what blocked the variable-arity `race` result synthesis: a `($T) -> Type` builder
looping `field_type(pointee(field_type(T, i)), 0)` to mint a tagged-union.

Three coordinated changes route these through the SAME type-call fold (which folds
the index, including a loop var), so type-arg and value positions never disagree:

- `isTypeShapedAstNode` (type_bridge.zig): a `.call` to a type-returning builtin
  (`field_type`/`pointee`/`type_of`, via new `isTypeReturningBuiltinName`) is
  type-shaped, so generic-arg inference (buildTypeBindings Strategy 1) resolves it
  via `resolveTypeArg` rather than failing value inference.
- `tryLowerReflectionCall` (call.zig): value-position `field_type`/`pointee` fold
  to `constType(resolveTypeCallWithBindings(c))` — the value twin of the existing
  `type_of` fold (every failure path already diagnoses before `.unresolved`).
- `field_name` (call.zig): folds to a const STRING via `memberName` when the type
  resolves and the index is a compile-time constant (matching the runtime
  `field_name_get` array exactly — same `memberName`, same "" for nameless
  members); a dynamic index still emits the `field_name_get` instruction.

Adversarially reviewed (SHIP): no over-broadening (only type-demanding slots consult
isTypeShapedAstNode; only `$T: Type` slots are affected), no silent defaults (every
fold failure is preceded by a diagnostic; "" is the runtime-matching value for a
nameless member). Locked by examples/comptime/0649-comptime-typecall-composition.sx
(reflect a named tuple of `*Box(..)` handles → mint a tagged-union with the tuple's
labels, projecting `*Box(A)` -> `A`). Suite green (821/0). Unblocks PLAN-RACE step 2.
This commit is contained in:
agra
2026-06-26 13:40:52 +03:00
parent 18443ea2e9
commit eb18bbc6fd
6 changed files with 105 additions and 1 deletions

View File

@@ -0,0 +1,48 @@
// Comptime type-call COMPOSITION: a `($T) -> Type` builder reflects a named
// tuple, projects each element type through `pointee` + `field_type`, and mints a
// tagged-union whose variant labels mirror the tuple's labels — the shape the
// `race` result synthesis needs (`(a: *Task(A), b: *Task(B))` -> `{ a: A; b: B }`).
//
// Exercises three things that previously failed when the index was an `inline for`
// loop var: a type-call RESULT used as (1) a `Type`-typed struct field value
// (`payload = field_type(...)`), (2) a nested type-call arg
// (`field_type(pointee(field_type(T, i)), 0)`), and a `field_name(T, i)` folded to
// a comptime string for a minted variant NAME. All resolve through the same
// type-call fold as a literal index would.
#import "modules/std.sx";
#import "modules/std/meta.sx";
// Stand-in for a task handle: a pointer to a generic box carrying the result.
Box :: struct ($R: Type) { value: R; }
// Mint a tagged-union mirroring a named tuple of `*Box(..)` handles:
// variant name = tuple label, payload = the box's value type (`*Box(A)` -> `A`).
ResultOf :: ($T: Type) -> Type {
vs : [field_count(T)]EnumVariant = ---;
inline for 0..field_count(T) (i) {
vs[i] = EnumVariant.{
name = field_name(T, i), // folded to a const string
payload = field_type(pointee(field_type(T, i)), 0), // *Box(A) -> Box(A) -> A
};
}
return make_enum("ResultOf", vs[0..field_count(T)]);
}
R :: ResultOf(Tuple(a: *Box(i64), b: *Box(bool), c: *Box(f64)));
use :: (r: R) {
if r == {
case .a: (v) { print("a (i64) = {}\n", v); }
case .b: (v) { print("b (bool) = {}\n", v); }
case .c: (v) { print("c (f64) = {}\n", v); }
}
}
main :: () -> i32 {
use(.a(42));
use(.b(true));
use(.c(2.5));
print("R: variants={} names=({},{},{})\n",
field_count(R), field_name(R, 0), field_name(R, 1), field_name(R, 2));
return 0;
}

View File

@@ -0,0 +1,4 @@
a (i64) = 42
b (bool) = true
c (f64) = 2.500000
R: variants=3 names=(a,b,c)