green(reify): field_type($T, i) -> Type over the type table
REIFY Phase 2.1. fieldTypeOf (lower/generic.zig, re-exported on Lowering) returns the i-th member type of T: struct field / tagged-union + union variant payload (.void for a tagless variant) / tuple element / array + vector element. Out-of-range and memberless types poison to .unresolved with a loud diagnostic (never a silent default). Wired into resolveTypeCallWithBindings (replacing the Phase-2 bail); since it folds to a TypeId at lower time it composes inside type_eq / type_name / any type-arg slot. examples/0616 green: struct fields (name via field_name + type via field_type), type_eq fold, tagged-union payloads incl. quit -> void. Suite green (672 examples, 447 unit). type_info($T) -> TypeInfo (reflect into a value, inverse of reify) is NOT done — still bails loudly; it's the larger Phase 2.2 step (widen the TypeInfo data model + comptime value construction). Plan/checkpoint updated.
This commit is contained in:
@@ -1894,6 +1894,7 @@ pub const Lowering = struct {
|
||||
pub const flatFnAuthorAmbiguous = lower_generic.flatFnAuthorAmbiguous;
|
||||
pub const flatFnAuthorVisible = lower_generic.flatFnAuthorVisible;
|
||||
pub const resolveTypeCallWithBindings = lower_generic.resolveTypeCallWithBindings;
|
||||
pub const fieldTypeOf = lower_generic.fieldTypeOf;
|
||||
pub const resolveParameterizedWithBindings = lower_generic.resolveParameterizedWithBindings;
|
||||
pub const resolveValueParamArg = lower_generic.resolveValueParamArg;
|
||||
pub const canonicalIntConstraintName = lower_generic.canonicalIntConstraintName;
|
||||
|
||||
@@ -1209,6 +1209,39 @@ pub fn flatFnAuthorVisible(self: *Lowering, name: []const u8, from: []const u8)
|
||||
}
|
||||
|
||||
/// Resolve a .call node that represents a type constructor (e.g., List(T), Vector(N, T)).
|
||||
/// The `idx`-th member type of `t` for `field_type($T, i)`: a struct field,
|
||||
/// a tagged-union variant payload (`.void` for a tagless variant), a tuple
|
||||
/// element, a `union` field, or the element type of an array/vector (index
|
||||
/// ignored — every element shares it). Out-of-range or a memberless type
|
||||
/// diagnoses and poisons to `.unresolved` (never a silent default).
|
||||
pub fn fieldTypeOf(self: *Lowering, t: TypeId, idx: usize, span: ?ast.Span) TypeId {
|
||||
const oob = struct {
|
||||
fn err(s: *Lowering, sp: ?ast.Span, i: usize, n: usize) TypeId {
|
||||
if (s.diagnostics) |d|
|
||||
d.addFmt(.err, sp, "field_type index {d} out of range ({d} field{s})", .{ i, n, if (n == 1) @as([]const u8, "") else "s" });
|
||||
return .unresolved;
|
||||
}
|
||||
};
|
||||
if (t.isBuiltin()) {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, span, "field_type: '{s}' has no fields", .{self.formatTypeName(t)});
|
||||
return .unresolved;
|
||||
}
|
||||
return switch (self.module.types.get(t)) {
|
||||
.@"struct" => |s| if (idx < s.fields.len) s.fields[idx].ty else oob.err(self, span, idx, s.fields.len),
|
||||
.tagged_union => |u| if (idx < u.fields.len) u.fields[idx].ty else oob.err(self, span, idx, u.fields.len),
|
||||
.@"union" => |u| if (idx < u.fields.len) u.fields[idx].ty else oob.err(self, span, idx, u.fields.len),
|
||||
.tuple => |tup| if (idx < tup.fields.len) tup.fields[idx] else oob.err(self, span, idx, tup.fields.len),
|
||||
.array => |a| if (idx < a.length) a.element else oob.err(self, span, idx, a.length),
|
||||
.vector => |v| if (idx < v.length) v.element else oob.err(self, span, idx, v.length),
|
||||
else => blk: {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, span, "field_type: '{s}' has no indexable fields", .{self.formatTypeName(t)});
|
||||
break :blk .unresolved;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resolveTypeCallWithBindings(self: *Lowering, cl: *const ast.Call) TypeId {
|
||||
// A namespaced callee (`ns.Box(..)`) is an explicit qualified reach and is
|
||||
// exempt from the bare-head visibility gate; only a plain identifier head
|
||||
@@ -1230,9 +1263,25 @@ pub fn resolveTypeCallWithBindings(self: *Lowering, cl: *const ast.Call) TypeId
|
||||
return .unresolved;
|
||||
}
|
||||
if (std.mem.eql(u8, callee_name, "field_type")) {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, cl.callee.span, "field_type is not yet implemented (REIFY Phase 2)", .{});
|
||||
return .unresolved;
|
||||
// field_type($T, i) -> Type — the i-th field / variant-payload /
|
||||
// element type of `T`. Folds at lower time (it's a `$T: Type` builtin),
|
||||
// so it composes inside `type_eq` / `type_name` / any type-arg slot.
|
||||
if (cl.args.len != 2) {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, cl.callee.span, "field_type takes a type and an index: field_type($T, i)", .{});
|
||||
return .unresolved;
|
||||
}
|
||||
const t = self.resolveTypeArg(cl.args[0]);
|
||||
if (t == .unresolved) return .unresolved;
|
||||
const idx: usize = switch (program_index_mod.foldDimU32(cl.args[1], self, 0)) {
|
||||
.ok => |n| n,
|
||||
else => {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, cl.args[1].span, "field_type index must be a non-negative compile-time integer", .{});
|
||||
return .unresolved;
|
||||
},
|
||||
};
|
||||
return self.fieldTypeOf(t, idx, cl.callee.span);
|
||||
}
|
||||
// Built-in: Vector(N, T)
|
||||
if (std.mem.eql(u8, callee_name, "Vector") and cl.args.len == 2) {
|
||||
|
||||
Reference in New Issue
Block a user