zig icon indicating copy to clipboard operation
zig copied to clipboard

divExact not panicking for floats at runtime

Open shimamura-sakura opened this issue 1 year ago • 1 comments

Zig Version

0.12.0-dev.3652+a59ad719d

Steps to Reproduce and Observed Behavior

Code:

const std = @import("std");

pub fn main() void {
    var a: f32 = 5.0;
    var b: f32 = 2.0;
    _ = &a;
    _ = &b;
    std.debug.print("{}\n", .{ @divExact(a, b) });
}

Output:

$ zig run a.zig
2e0

But at compile time: Code:

const std = @import("std");

pub fn main() void {
    const a: f32 = 5.0;
    const b: f32 = 2.0;
    _ = &a;
    _ = &b;
    std.debug.print("{}\n", .{ @divExact(a, b) });
}

Output:

$ zig build-exe a.zig 
a.zig:8:32: error: exact division produced remainder
    std.debug.print("{}\n", .{ @divExact(a, b) });
                               ^~~~~~~~~~~~~~~
referenced by:
    callMain: /usr/lib/zig/lib/std/start.zig:501:17
    callMainWithArgs: /usr/lib/zig/lib/std/start.zig:469:12
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

Expected Behavior

The program panic at runtime when "division produced remainder".

shimamura-sakura avatar Apr 14 '24 14:04 shimamura-sakura

The safety check uses .div_trunc and then .floors the result and checks if they match, but for floats .div_trunc doesn't produce remainder so the floored result is always the same: https://github.com/ziglang/zig/blob/e45bdc6bd6e47ec3f7a06dbb48f24e842bf43a0d/src/Sema.zig#L15331-L15348

Using .div_float for floats fixes it:

diff --git a/src/Sema.zig b/src/Sema.zig
index d3989f630c..5044eb1d71 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -15328,7 +15328,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
 
-        const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
+        const result = try block.addBinOp(if (is_int) .div_trunc else .div_float, casted_lhs, casted_rhs);
         const ok = if (!is_int) ok: {
             const floored = try block.addUnOp(.floor, result);
 

Vexu avatar Apr 14 '24 14:04 Vexu