fix(ir): complete const-float evaluator — resolve float-const leaves too [F0.11]
Completes issue 0095: a non-integral float→int narrowing via a FLOAT-const leaf (`F : f64 : 2.5; y : s64 = F + 0.25` = 2.75) silently truncated to 2. `evalConstFloatExpr` delegated only INTEGER leaves to `evalConstIntExpr` and had no float-const leaf arm, so the unified rule never saw the value. - program_index.zig: add `moduleConstFloat`/`moduleConstFloatFramed` — the f64 twin of `moduleConstInt` (same `isCountableConstType` gate, same cyclic- definition frame), recovering a numeric module const's value via `evalConstFloatExpr`. Add `lookupFloatName` to `ModuleConstCtx` and the `.identifier`/`.type_expr` leaf arms to `evalConstFloatExpr` that call it. Integer / integral-float leaves keep resolving through the existing `evalConstIntExpr` delegation, so the unified rule now applies to ANY compile-time-constant float expression — literal, int-const leaf, float-const leaf, and combinations — at every binding site. - lower.zig: add `Lowering.lookupFloatName` delegating to `moduleConstFloat`. Route `typedConstInitFits`' integral-fold check through `evalConstFloatExpr` + `floatToIntExact` (the SAME facility `foldComptimeFloatInit` uses) instead of the int-only `evalComptimeInt`, which folded leaf-by-leaf in i64 and so rejected an integral SUM built from a non-integral float leaf (`K : s64 : F + 1.5` = 4.0 now folds; `K : s64 : F + 0.25` errors). A LOCAL `::` const leaf is a scope ref (not in the const tables) so neither the int nor float evaluator folds it — float now matches int exactly there. Regression: examples/1146 (negative) + 0168 (positive) extended with float-const-leaf cases at local/field/param/const; unit test in program_index.test.zig covers the leaf resolution (F→2.5, F+0.25→2.75, F+1.5→4.0). specs.md + readme.md state the rule covers any compile-time-const float expression incl. float-typed const leaves. issues/0095 banner updated. Gate: zig build + zig build test green; 447 examples pass, 0 failed.
This commit is contained in:
@@ -988,15 +988,22 @@ pub const Lowering = struct {
|
||||
/// `B : s64 : true`.
|
||||
fn typedConstInitFits(self: *Lowering, value: *const Node, dst_ty: TypeId) bool {
|
||||
// An INTEGER-annotated constant accepts a compile-time INTEGRAL float —
|
||||
// a literal (`K : s64 : 4.0`) or an expression that folds to an integer
|
||||
// (`K : s64 : M + 2.0` → 4) — via the SAME `evalConstIntExpr` /
|
||||
// `floatToIntExact` the array-dim path uses. A non-integral float
|
||||
// (`1.5`, `M + 0.5`) folds to null and falls through to the rejecting
|
||||
// checks below, matching the typed-local rule.
|
||||
// a literal (`K : s64 : 4.0`), an int-leaf expression (`K : s64 : M + 2.0`
|
||||
// → 4), or a float-const-leaf expression whose SUM is integral
|
||||
// (`F : f64 : 2.5; K : s64 : F + 1.5` → 4). Integrality is judged on the
|
||||
// FLOAT fold (`evalConstFloatExpr` + `floatToIntExact`) — the SAME facility
|
||||
// the typed-local path (`foldComptimeFloatInit`) uses — not the int-only
|
||||
// folder, which folds leaf-by-leaf in `i64` and so misses an integral SUM
|
||||
// built from a non-integral float leaf. A non-integral fold (`1.5`,
|
||||
// `M + 0.5`, `F + 0.25`) yields null here and falls through to the
|
||||
// rejecting checks below, where `registerTypedModuleConst` emits the
|
||||
// unified narrowing diagnostic.
|
||||
if (self.isIntEx(dst_ty)) {
|
||||
switch (value.data) {
|
||||
.float_literal, .binary_op, .unary_op => {
|
||||
if (self.evalComptimeInt(value) != null) return true;
|
||||
if (program_index_mod.evalConstFloatExpr(value, self)) |fv| {
|
||||
if (program_index_mod.floatToIntExact(fv) != null) return true;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@@ -12113,6 +12120,18 @@ pub const Lowering = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Float-valued leaf for the shared float-expression evaluator: a name bound
|
||||
/// to a NUMERIC module const whose compile-time value is a (non-integral)
|
||||
/// float — the FLOAT counterpart of `lookupDimName`, routed through the SAME
|
||||
/// `module_const_map` so the unified narrowing rule resolves a float-const
|
||||
/// leaf (`F : f64 : 2.5`) exactly as it resolves an int-const leaf. Integer /
|
||||
/// integral-float leaves and comptime int bindings are already resolved by the
|
||||
/// `evalConstIntExpr` delegation inside `evalConstFloatExpr`; this surfaces the
|
||||
/// non-integral float const so the rule can reject it.
|
||||
pub fn lookupFloatName(self: *Lowering, name: []const u8) ?f64 {
|
||||
return program_index_mod.moduleConstFloat(&self.program_index.module_const_map, &self.module.types, name);
|
||||
}
|
||||
|
||||
/// Resolve a name to a compile-time integer across the three const tables.
|
||||
fn comptimeIntNamed(self: *Lowering, name: []const u8) ?i64 {
|
||||
if (self.comptime_constants.get(name)) |cv| switch (cv) {
|
||||
|
||||
Reference in New Issue
Block a user