feat: comptime-cursor indexing of a named-tuple VALUE (GAP 1)
`tup[i]` where `i` folds to a compile-time integer (an `inline for` cursor or a literal) now reads the i-th tuple field with its CONCRETE type instead of failing with "cannot index a value of type '(…)'". A tuple's elements are heterogeneous, so there is no runtime element-indexing op — a comptime index lowers exactly like the `.N` field-access path (a `structGet` of the i-th field). A genuinely runtime index into a tuple value still falls through to the existing "cannot index a value of type" error (no single element type). A comptime index out of range gets a dedicated loud diagnostic. This is the read side `race` needs: pull the i-th `*Task(T_i)` handle out of a named-tuple param keeping its real type so field/method access on it resolves. Locked by examples/comptime/0652-comptime-tuple-cursor-index.sx.
This commit is contained in:
40
examples/comptime/0652-comptime-tuple-cursor-index.sx
Normal file
40
examples/comptime/0652-comptime-tuple-cursor-index.sx
Normal file
@@ -0,0 +1,40 @@
|
||||
// Comptime-cursor indexing of a named-tuple VALUE: `tup[i]` where `i` is an
|
||||
// `inline for` cursor (or a literal) reads the i-th tuple field with its
|
||||
// CONCRETE type — not a type-erased `Any`. This is the read side `race` needs:
|
||||
// pull the i-th `*Task(T_i)` handle out of a named-tuple param keeping its real
|
||||
// type, so `field`/method access on it resolves. A tuple's elements are
|
||||
// heterogeneous, so there is no runtime element-indexing op — a comptime index
|
||||
// lowers exactly like the `.N` field-access path (a `structGet`). A runtime
|
||||
// index into a tuple value remains an error (no single element type).
|
||||
//
|
||||
// (GAP 1 of PLAN-RACE.)
|
||||
#import "modules/std.sx";
|
||||
|
||||
Box :: struct ($R: Type) { value: R; }
|
||||
|
||||
// Read each handle with its concrete type via the `inline for` cursor.
|
||||
show_all :: (tup: $T) {
|
||||
inline for 0..field_count(T) (i) {
|
||||
h := tup[i]; // concrete `*Box(T_i)`, not `Any`
|
||||
print("[{}] = {}\n", i, h.value); // field access resolves
|
||||
}
|
||||
}
|
||||
|
||||
// Literal-index form, positional tuple.
|
||||
first_two :: (tup: $T) -> i64 {
|
||||
a := tup[0];
|
||||
b := tup[1];
|
||||
return a.value + b.value;
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
ba : Box(i64) = .{ value = 7 };
|
||||
bb : Box(bool) = .{ value = true };
|
||||
bc : Box(f64) = .{ value = 2.5 };
|
||||
show_all(.(a = @ba, b = @bb, c = @bc)); // named tuple of *Box(..)
|
||||
|
||||
p0 : Box(i64) = .{ value = 10 };
|
||||
p1 : Box(i64) = .{ value = 32 };
|
||||
print("sum = {}\n", first_two(.(@p0, @p1))); // positional, literal index
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[0] = 7
|
||||
[1] = true
|
||||
[2] = 2.500000
|
||||
sum = 42
|
||||
@@ -1798,6 +1798,38 @@ pub fn lowerIndexExpr(self: *Lowering, ie: *const ast.IndexExpr) Ref {
|
||||
}
|
||||
// Infer element type from the object's slice/array type
|
||||
const obj_ty = self.inferExprType(ie.object);
|
||||
// Comptime-constant index into a tuple VALUE — `tup[i]` where `i` folds
|
||||
// to a compile-time integer (an `inline for` cursor or a literal). A tuple
|
||||
// has heterogeneous element types, so there is no runtime element-indexing
|
||||
// op; treat it exactly like the `.N` field-access path (a `structGet` of
|
||||
// the i-th field), yielding the field's CONCRETE type. This is what the
|
||||
// `race`/reflection loops need: read the i-th `*Task(T_i)` from a
|
||||
// named-tuple param with its real type, not a type-erased `Any`. A
|
||||
// *runtime* index into a tuple value falls through to the generic guard
|
||||
// below ("cannot index a value of type '(...)'") — there is no single
|
||||
// element type to index by at runtime.
|
||||
if (!obj_ty.isBuiltin() and self.module.types.get(obj_ty) == .tuple) {
|
||||
const tinfo = self.module.types.get(obj_ty).tuple;
|
||||
if (self.comptimeIndexOf(ie.index)) |ci| {
|
||||
if (ci >= 0 and @as(usize, @intCast(ci)) < tinfo.fields.len) {
|
||||
const fi: u32 = @intCast(ci);
|
||||
const obj = self.lowerExpr(ie.object);
|
||||
return self.builder.structGet(obj, fi, tinfo.fields[fi]);
|
||||
}
|
||||
// Comptime index is out of range — diagnose loudly rather than
|
||||
// letting it fall through to the generic "cannot index" message,
|
||||
// which would obscure the real cause (a bad constant index).
|
||||
if (self.diagnostics) |d| {
|
||||
d.addFmt(.err, ie.index.span, "tuple index {} out of bounds — tuple '{s}' has {} field{s}", .{
|
||||
ci,
|
||||
self.formatTypeName(obj_ty),
|
||||
tinfo.fields.len,
|
||||
if (tinfo.fields.len == 1) "" else "s",
|
||||
});
|
||||
}
|
||||
return self.builder.constInt(0, .i64); // placeholder — hasErrors() aborts before codegen
|
||||
}
|
||||
}
|
||||
// Optional-chain index: `opt?.xs[i]`. The `?.` makes the object an
|
||||
// optional whose child is the (array/slice/many-ptr) field — so the index
|
||||
// applies inside the chain's some-branch and the whole expression is
|
||||
|
||||
Reference in New Issue
Block a user