Commit 0782586b15

Veikka Tuominen <git@vexu.eu>
2022-07-16 01:55:08
Sema: divide by zero safety
1 parent 9f10dfc
src/Sema.zig
@@ -11877,6 +11877,45 @@ fn analyzeArithmetic(
                 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
             }
         }
+        switch (rs.air_tag) {
+            .div_float, .div_exact, .div_trunc, .div_floor => {
+                const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
+                    const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
+                    const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val);
+                    const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type));
+                    break :ok try block.addInst(.{
+                        .tag = .reduce,
+                        .data = .{ .reduce = .{
+                            .operand = ok,
+                            .operation = .And,
+                        } },
+                    });
+                } else ok: {
+                    const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero);
+                    break :ok try block.addBinOp(.cmp_neq, casted_rhs, zero);
+                };
+                try sema.addSafetyCheck(block, ok, .divide_by_zero);
+            },
+            .rem, .mod => {
+                const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
+                    const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
+                    const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val);
+                    const ok = try block.addCmpVector(casted_rhs, zero, if (scalar_tag == .Int) .gt else .neq, try sema.addType(resolved_type));
+                    break :ok try block.addInst(.{
+                        .tag = .reduce,
+                        .data = .{ .reduce = .{
+                            .operand = ok,
+                            .operation = .And,
+                        } },
+                    });
+                } else ok: {
+                    const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero);
+                    break :ok try block.addBinOp(if (scalar_tag == .Int) .cmp_gt else .cmp_neq, casted_rhs, zero);
+                };
+                try sema.addSafetyCheck(block, ok, .remainder_division_zero_negative);
+            },
+            else => {},
+        }
     }
     return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
 }
@@ -18813,6 +18852,8 @@ pub const PanicId = enum {
     integer_overflow,
     shl_overflow,
     shr_overflow,
+    divide_by_zero,
+    remainder_division_zero_negative,
 };
 
 fn addSafetyCheck(
@@ -19031,6 +19072,8 @@ fn safetyPanic(
         .integer_overflow => "integer overflow",
         .shl_overflow => "left shift overflowed bits",
         .shr_overflow => "right shift overflowed bits",
+        .divide_by_zero => "division by zero",
+        .remainder_division_zero_negative => "remainder division by zero or negative value",
     };
 
     const msg_inst = msg_inst: {
test/cases/safety/integer division by zero - vectors.zig
@@ -1,9 +1,11 @@
 const std = @import("std");
 
 pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
-    _ = message;
     _ = stack_trace;
-    std.process.exit(0);
+    if (std.mem.eql(u8, message, "division by zero")) {
+        std.process.exit(0);
+    }
+    std.process.exit(1);
 }
 pub fn main() !void {
     var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444};
@@ -16,5 +18,5 @@ fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
     return @divTrunc(a, b);
 }
 // run
-// backend=stage1
-// target=native
\ No newline at end of file
+// backend=llvm
+// target=native
test/cases/safety/integer division by zero.zig
@@ -1,9 +1,11 @@
 const std = @import("std");
 
 pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
-    _ = message;
     _ = stack_trace;
-    std.process.exit(0);
+    if (std.mem.eql(u8, message, "division by zero")) {
+        std.process.exit(0);
+    }
+    std.process.exit(1);
 }
 pub fn main() !void {
     const x = div0(999, 0);
@@ -14,5 +16,5 @@ fn div0(a: i32, b: i32) i32 {
     return @divTrunc(a, b);
 }
 // run
-// backend=stage1
-// target=native
\ No newline at end of file
+// backend=llvm
+// target=native
test/cases/safety/remainder division by negative number.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+
+pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
+    _ = stack_trace;
+    if (std.mem.eql(u8, message, "remainder division by zero or negative value")) {
+        std.process.exit(0);
+    }
+    std.process.exit(1);
+}
+pub fn main() !void {
+    const x = div0(999, -1);
+    _ = x;
+    return error.TestFailed;
+}
+fn div0(a: i32, b: i32) i32 {
+    return @rem(a, b);
+}
+// run
+// backend=llvm
+// target=native