fix: resolve qualified-import-member const as a compile-time constant (issue 0192)
A namespaced import's const (`m :: #import "lib.sx"; … m.CAP`) only ever resolved as a runtime value — the const folders in program_index.zig had no namespace-member arm, so a qualified const was rejected as an array dimension / Vector lane / generic value-param and could not seed another const, while the flat-import form worked everywhere. Add a `lookupQualifiedConst` (+ float / float-typed twins) ctx hook: resolve the alias via `namespaceAliasVerdictFrom` to its target module, then fold the member from that module's per-source const cache (`foldQualifiedConstInt` in lower/comptime.zig), pinned to the target source so nested const RHSs fold there. Wire it into evalConstIntExpr / evalConstFloatExpr / isFloatValuedExpr — both the expression-position field_access arm (`[m.CAP]T`) and the type-argument dotted-name arm (`Vector(m.LANES, …)`, generic value-params). Implemented on the source-aware ctxs (Lowering / SourceConstCtx); the namespace-blind ModuleConstCtx / StatelessInner return null, so a qualified-const dim reached only via the stateless type-alias path stays a clean unresolved-dim diagnostic, never a fabricated length. Resolves correctly for array dims, arithmetic, integral-float dims, Vector lanes, generic value-params, inline-for bounds, and struct fields. Regression: examples/modules/0842-modules-qualified-import-const-comptime.sx.
This commit is contained in:
@@ -1293,6 +1293,72 @@ pub fn foldSourceConstInt(self: *Lowering, name: []const u8, frame: ?*const Cons
|
||||
};
|
||||
}
|
||||
|
||||
/// Resolve a QUALIFIED module const `ns.field` (a namespaced-import member —
|
||||
/// `m :: #import "lib.sx"; … m.CAP`) to its authoring source + info (issue
|
||||
/// 0192). The alias `ns` is resolved in the CURRENT source context — the file
|
||||
/// that wrote `ns.field`, since an alias binds in its declaring file, not the
|
||||
/// use site — then `field` is read from that target module's per-source const
|
||||
/// cache (`module_consts_by_source`). Null when `ns` is not a visible namespace
|
||||
/// alias, is ambiguous, or names no such const there. Diagnostic-free: a
|
||||
/// speculative const fold must not emit (a real "name resolves nowhere" error is
|
||||
/// the reference site's job), so the ambiguous-alias case folds to null here.
|
||||
pub fn selectQualifiedConst(self: *Lowering, ns: []const u8, field: []const u8) ?SelectedConst {
|
||||
// Resolve the alias from the use-site source — the file that wrote `ns.field`
|
||||
// (where `ns` binds). A main-file body carries a null `current_source_file`
|
||||
// (it IS the root), so fall back to `main_file`, matching `selectModuleConst`.
|
||||
const from = self.current_source_file orelse self.main_file orelse return null;
|
||||
const target = switch (self.namespaceAliasVerdictFrom(ns, from)) {
|
||||
.target => |t| t,
|
||||
.ambiguous, .none => return null,
|
||||
};
|
||||
const src = target.target_module_path;
|
||||
const ci = self.sourceModuleConst(src, field) orelse return null;
|
||||
return .{ .info = ci, .source = src };
|
||||
}
|
||||
|
||||
/// Source-aware INTEGER fold of a qualified const `ns.field` (issue 0192): the
|
||||
/// qualified twin of `foldSourceConstInt`. Resolve the namespace member to its
|
||||
/// authoring source, then fold ITS RHS PINNED to that source so nested const
|
||||
/// leaves (`CAP :: BASE + 1`, `BASE` authored in the target module) re-select
|
||||
/// against the target module, not the use site. `frame` (keyed by name +
|
||||
/// author-source) cycle-guards a const whose value references another const.
|
||||
pub fn foldQualifiedConstInt(self: *Lowering, ns: []const u8, field: []const u8, frame: ?*const ConstFoldFrame) ?i64 {
|
||||
const sel = self.selectQualifiedConst(ns, field) orelse return null;
|
||||
if (constFoldFrameContains(frame, field, sel.source)) return null;
|
||||
if (!program_index_mod.isCountableConstType(&self.module.types, sel.info.ty)) return null;
|
||||
var f = ConstFoldFrame{ .name = field, .source = sel.source, .parent = frame };
|
||||
const restore = self.pinConstAuthorSource(sel.source);
|
||||
defer restore.unpin();
|
||||
return program_index_mod.evalConstIntExpr(sel.info.value, SourceConstCtx{ .lowering = self, .frame = &f });
|
||||
}
|
||||
|
||||
/// FLOAT counterpart of `foldQualifiedConstInt` (issue 0192) — the qualified
|
||||
/// twin of `foldSourceConstFloat`, so a qualified non-integral float const
|
||||
/// (`m.PI`) folds the same way its bare-name sibling does.
|
||||
pub fn foldQualifiedConstFloat(self: *Lowering, ns: []const u8, field: []const u8, frame: ?*const ConstFoldFrame) ?f64 {
|
||||
const sel = self.selectQualifiedConst(ns, field) orelse return null;
|
||||
if (constFoldFrameContains(frame, field, sel.source)) return null;
|
||||
if (!program_index_mod.isCountableConstType(&self.module.types, sel.info.ty)) return null;
|
||||
var f = ConstFoldFrame{ .name = field, .source = sel.source, .parent = frame };
|
||||
const restore = self.pinConstAuthorSource(sel.source);
|
||||
defer restore.unpin();
|
||||
return program_index_mod.evalConstFloatExpr(sel.info.value, SourceConstCtx{ .lowering = self, .frame = &f });
|
||||
}
|
||||
|
||||
/// "Is the qualified const `ns.field` FLOAT-valued" (issue 0192) — the
|
||||
/// qualified twin of `sourceConstIsFloatTyped`, consulted by the int folder's
|
||||
/// division guard so `m.K / 3` (with `m.K : f64`) is recognised as float
|
||||
/// division exactly as a bare `K / 3` is.
|
||||
pub fn qualifiedConstIsFloatTyped(self: *Lowering, ns: []const u8, field: []const u8, frame: ?*const ConstFoldFrame) bool {
|
||||
const sel = self.selectQualifiedConst(ns, field) orelse return false;
|
||||
if (constFoldFrameContains(frame, field, sel.source)) return false;
|
||||
if (program_index_mod.isFloatConstType(sel.info.ty)) return true;
|
||||
var f = ConstFoldFrame{ .name = field, .source = sel.source, .parent = frame };
|
||||
const restore = self.pinConstAuthorSource(sel.source);
|
||||
defer restore.unpin();
|
||||
return program_index_mod.isFloatValuedExpr(sel.info.value, SourceConstCtx{ .lowering = self, .frame = &f });
|
||||
}
|
||||
|
||||
/// Float counterpart of `foldSourceConstInt` (E2/F2/R1).
|
||||
pub fn foldSourceConstFloat(self: *Lowering, name: []const u8, frame: ?*const ConstFoldFrame) ?f64 {
|
||||
return switch (self.selectModuleConst(name)) {
|
||||
|
||||
Reference in New Issue
Block a user