// Unified float→int narrowing rule (F0.11), POSITIVE side: an INTEGRAL float // flowing into an integer-typed binding FOLDS to its integer — the same // `floatToIntExact` rule an array dimension / `$K: Count` already uses — across // a typed LOCAL, a struct FIELD default, a typed module CONST, and a function // PARAM default. The escape hatch (`xx` / `cast`) still TRUNCATES any float, // integral or not. // // Companion to the negative example 1146 (non-integral floats error). // Regression (issue 0095): a typed local/param/field silently truncated a float // initializer (`y : s64 = 1.5` → 1) with no diagnostic; the rule now folds an // integral float and rejects a non-integral one. #import "modules/std.sx"; Box :: struct { n : s64 = 4.0; // integral float field default → folds to 4 } withDefault :: (x : s64 = 6.0) -> s64 { return x; } // param default → 6 K : s64 : 8.0; // integral float module const → folds to 8 main :: () { // Typed local: integral float folds. z : s64 = 4.0; print("local={}\n", z); // Negative integral float folds to its (negative) integer. neg : s64 = -2.0; print("neg={}\n", neg); // Struct field default folds. b := Box.{}; print("field={}\n", b.n); // Param default folds. print("param={}\n", withDefault()); // Module const folds (and can drive an array dimension: len 8). a : [K]s64 = ---; print("const={} len={}\n", K, a.len); // Explicit escape: `xx` / `cast` always truncate, integral or not. e : s64 = xx 4.9; c : s64 = cast(s64) 1.5; print("xx={} cast={}\n", e, c); }