lang: const-aggregate comptime folds (PLAN-CONST-AGG step 3)
An array const's '.len' and 'K[<const idx>]' element reads, and a
struct const's field ('LIT.r'), fold as compile-time integer leaves —
usable in array dimensions and other constants' initializers. All
source-aware (the SELECTED author's elements, folded in the author's
context with the cyclic-definition frame); a const out-of-range index
diagnoses at fold time, never wraps.
- evalConstIntExpr gains the three ctx hooks (lookupConstAggLen /
lookupConstArrayElem / lookupConstStructField) + an index_expr arm;
all five ctx implementations extended (stateless tiers fold null).
- Array consts dual-register in module_const_map (value = the literal
node) so the folders see elements; bare reads still hit the GLOBAL
arm first, so no double emission.
- Untyped consts whose RHS is a const-aggregate leaf ('L :: K.len',
'E :: K[1]', 'R :: LIT.r') register in a pass 2b AFTER aggregates,
gated on the receiver naming a const aggregate — a namespaced member
('F :: m.PI_ISH') is never mis-typed by the count placeholder.
Examples: 0179 (folds in dims + const exprs), 1163 (OOB diagnostic).
This commit is contained in:
@@ -898,6 +898,80 @@ pub fn selectModuleConst(self: *Lowering, name: []const u8) ConstAuthor {
|
||||
return .none;
|
||||
}
|
||||
|
||||
/// `<array const>.len` as a compile-time integer — the SELECTED author's
|
||||
/// element count (E2/F2 source-aware, like every const fold).
|
||||
pub fn foldConstAggLen(self: *Lowering, name: []const u8) ?i64 {
|
||||
return switch (self.selectModuleConst(name)) {
|
||||
.resolved => |sel| if (sel.info.value.data == .array_literal)
|
||||
@intCast(sel.info.value.data.array_literal.elements.len)
|
||||
else
|
||||
null,
|
||||
.own_opaque, .ambiguous, .none => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// `K[<const idx>]` over an ARRAY const: the element's compile-time integer
|
||||
/// value, folded in the AUTHOR's context. Out-of-range diagnoses loudly —
|
||||
/// never a wrap or a silent null-into-runtime.
|
||||
pub fn foldConstArrayElem(self: *Lowering, name: []const u8, idx: i64, span: ?ast.Span, frame: ?*const ConstFoldFrame) ?i64 {
|
||||
switch (self.selectModuleConst(name)) {
|
||||
.resolved => |sel| {
|
||||
if (sel.info.value.data != .array_literal) return null;
|
||||
const elems = sel.info.value.data.array_literal.elements;
|
||||
if (idx < 0 or idx >= elems.len) {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, span, "index {d} is out of bounds for constant '{s}' ({d} elements)", .{ idx, name, elems.len });
|
||||
return null;
|
||||
}
|
||||
if (constFoldFrameContains(frame, name, sel.source)) return null;
|
||||
var f = ConstFoldFrame{ .name = name, .source = sel.source, .parent = frame };
|
||||
const restore = self.pinConstAuthorSource(sel.source);
|
||||
defer restore.unpin();
|
||||
return program_index_mod.evalConstIntExpr(elems[@intCast(idx)], SourceConstCtx{ .lowering = self, .frame = &f });
|
||||
},
|
||||
.own_opaque, .ambiguous, .none => return null,
|
||||
}
|
||||
}
|
||||
|
||||
/// `<struct const>.field` as a compile-time integer — the SELECTED author's
|
||||
/// field initializer, matched by name (named inits) or position, folded in
|
||||
/// the author's context.
|
||||
pub fn foldConstStructField(self: *Lowering, name: []const u8, field: []const u8, frame: ?*const ConstFoldFrame) ?i64 {
|
||||
switch (self.selectModuleConst(name)) {
|
||||
.resolved => |sel| {
|
||||
if (sel.info.value.data != .struct_literal) return null;
|
||||
const sl = &sel.info.value.data.struct_literal;
|
||||
const init_expr: ?*const Node = blk: {
|
||||
const has_names = sl.field_inits.len > 0 and sl.field_inits[0].name != null;
|
||||
if (has_names) {
|
||||
for (sl.field_inits) |fi| {
|
||||
if (fi.name) |n| if (std.mem.eql(u8, n, field)) break :blk fi.value;
|
||||
}
|
||||
break :blk null;
|
||||
}
|
||||
// Positional inits: index via the struct type's field order.
|
||||
if (sel.info.ty.isBuiltin()) break :blk null;
|
||||
const ti = self.module.types.get(sel.info.ty);
|
||||
if (ti != .@"struct") break :blk null;
|
||||
for (ti.@"struct".fields, 0..) |sf, i| {
|
||||
if (std.mem.eql(u8, self.module.types.getString(sf.name), field)) {
|
||||
if (i < sl.field_inits.len) break :blk sl.field_inits[i].value;
|
||||
break :blk null;
|
||||
}
|
||||
}
|
||||
break :blk null;
|
||||
};
|
||||
const e = init_expr orelse return null;
|
||||
if (constFoldFrameContains(frame, name, sel.source)) return null;
|
||||
var f = ConstFoldFrame{ .name = name, .source = sel.source, .parent = frame };
|
||||
const restore = self.pinConstAuthorSource(sel.source);
|
||||
defer restore.unpin();
|
||||
return program_index_mod.evalConstIntExpr(e, SourceConstCtx{ .lowering = self, .frame = &f });
|
||||
},
|
||||
.own_opaque, .ambiguous, .none => return null,
|
||||
}
|
||||
}
|
||||
|
||||
/// `source`'s per-source const cache entry for `name` (E0's
|
||||
/// `module_consts_by_source` write side), or null.
|
||||
pub fn sourceModuleConst(self: *Lowering, source: []const u8, name: []const u8) ?ModuleConstInfo {
|
||||
|
||||
Reference in New Issue
Block a user