// issue-0021: enclosing function's return type bleeds into `xx`'s target // type inside an `if-then-else` expression on the RHS of a struct-field // assignment // // ── Repro ────────────────────────────────────────────────────────────────── // // Foo :: struct { pixel_w: s32; dpi: f32; } // // calc_void :: (self: *Foo, wf: f32) { // self.dpi = if wf > 0.0 then xx self.pixel_w / wf else 1.0; // } // // calc_bool :: (self: *Foo, wf: f32) -> bool { // self.dpi = if wf > 0.0 then xx self.pixel_w / wf else 1.0; // true; // } // // With `f.pixel_w = 2880` and `wf = 1440.0`: // - `calc_void` produces `f.dpi = 2.0` (correct). // - `calc_bool` produces `f.dpi = 0.0` (wrong). // // Only difference: the enclosing function's declared return type. The `xx` // cast appears to take its target type from the enclosing function's return // type rather than from the assignment LHS (which is `f32`). When the return // type is `bool`, the divide is lowered against a non-numeric/zero-valued // operand. // // ── Related, possibly same root cause ─────────────────────────────────────── // // In a struct-returning function, this form makes LLVM verification fail with // `udiv { float, float, i32, i32, float, float }` — the divide is lowered as // integer division over the function's return-struct shape: // // FC :: struct { a: f32; b: f32; c: s32; d: s32; e: f32; f: f32; } // begin :: (self: *Foo) -> FC { // if self.last_perf > 0 { // self.delta_time = xx (current - self.last_perf) / xx freq; // } // FC.{ ... }; // } // // ── Workaround ───────────────────────────────────────────────────────────── // // Hoist the `xx` cast into its own variable so the divide sees two // already-typed f32 values: // // pw : f32 = xx self.pixel_w; // self.dpi = if wf > 0.0 then pw / wf else 1.0; // // ── Real-world impact ────────────────────────────────────────────────────── // // The SX Chess game's dpi_scale calculation took this form inside // `SdlPlatform.init` (which returns `bool`). On a retina display the // dpi_scale silently became 0, so the glyph cache rasterized at scale=0 and // every text label rendered invisibly. #import "modules/std.sx"; Foo :: struct { pixel_w: s32; dpi: f32; } calc_void :: (self: *Foo, wf: f32) { self.dpi = if wf > 0.0 then xx self.pixel_w / wf else 1.0; } calc_bool :: (self: *Foo, wf: f32) -> bool { self.dpi = if wf > 0.0 then xx self.pixel_w / wf else 1.0; true; } main :: () -> void { f : *Foo = xx malloc(size_of(Foo)); f.pixel_w = 2880; f.dpi = 0.0; calc_void(f, 1440.0); out("void-return (expect 200): "); out(int_to_string(xx (f.dpi * 100.0))); out("\n"); f.dpi = 0.0; calc_bool(f, 1440.0); out("bool-return (expect 200): "); out(int_to_string(xx (f.dpi * 100.0))); out("\n"); }