From aeb9a2b72cf94ae15624482abf89f2de7a538aed Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Thu, 14 Jun 2012 15:32:20 -0700 Subject: [PATCH] Add zero check/fail paths on div/mod paths. Close #944. --- src/rustc/middle/trans/base.rs | 65 ++++++++++++++++++++++++----- src/test/run-fail/divide-by-zero.rs | 5 +++ src/test/run-fail/mod-zero.rs | 5 +++ 3 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 src/test/run-fail/divide-by-zero.rs create mode 100644 src/test/run-fail/mod-zero.rs diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 57e0751f3190..21b9549cf822 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1634,11 +1634,38 @@ fn cast_shift_rhs(op: ast::binop, } } +fn fail_if_zero(cx: block, span: span, divmod: ast::binop, + rhs: ValueRef, rhs_t: ty::t) -> block { + let text = if divmod == ast::div { + "divide by zero" + } else { + "modulo zero" + }; + let is_zero = alt ty::get(rhs_t).struct { + ty::ty_int(t) { + let zero = C_integral(T_int_ty(cx.ccx(), t), 0u64, False); + ICmp(cx, lib::llvm::IntEQ, rhs, zero) + } + ty::ty_uint(t) { + let zero = C_integral(T_uint_ty(cx.ccx(), t), 0u64, False); + ICmp(cx, lib::llvm::IntEQ, rhs, zero) + } + _ { + cx.tcx().sess.bug("fail-if-zero on unexpected type: " + + ty_to_str(cx.ccx().tcx, rhs_t)); + } + }; + with_cond(cx, is_zero) {|bcx| + trans_fail(bcx, some(span), text) + } +} + // Important to get types for both lhs and rhs, because one might be _|_ // and the other not. -fn trans_eager_binop(cx: block, op: ast::binop, lhs: ValueRef, +fn trans_eager_binop(cx: block, span: span, op: ast::binop, lhs: ValueRef, lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t, dest: dest) -> block { + let mut cx = cx; let _icx = cx.insn_ctxt("trans_eager_binop"); if dest == ignore { ret cx; } let intype = { @@ -1667,16 +1694,30 @@ fn trans_eager_binop(cx: block, op: ast::binop, lhs: ValueRef, else { Mul(cx, lhs, rhs) } } ast::div { - if is_float { FDiv(cx, lhs, rhs) } - else if ty::type_is_signed(intype) { - SDiv(cx, lhs, rhs) - } else { UDiv(cx, lhs, rhs) } + if is_float { + FDiv(cx, lhs, rhs) + } else { + // Only zero-check integers; fp /0 is NaN + cx = fail_if_zero(cx, span, op, rhs, rhs_t); + if ty::type_is_signed(intype) { + SDiv(cx, lhs, rhs) + } else { + UDiv(cx, lhs, rhs) + } + } } ast::rem { - if is_float { FRem(cx, lhs, rhs) } - else if ty::type_is_signed(intype) { - SRem(cx, lhs, rhs) - } else { URem(cx, lhs, rhs) } + if is_float { + FRem(cx, lhs, rhs) + } else { + // Only zero-check integers; fp %0 is NaN + cx = fail_if_zero(cx, span, op, rhs, rhs_t); + if ty::type_is_signed(intype) { + SRem(cx, lhs, rhs) + } else { + URem(cx, lhs, rhs) + } + } } ast::bitor { Or(cx, lhs, rhs) } ast::bitand { And(cx, lhs, rhs) } @@ -1744,7 +1785,8 @@ fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop, _ { } } } - ret trans_eager_binop(bcx, op, Load(bcx, lhs_res.val), t, rhs_val, t, + ret trans_eager_binop(bcx, ex.span, + op, Load(bcx, lhs_res.val), t, rhs_val, t, save_in(lhs_res.val)); } @@ -1885,7 +1927,8 @@ fn trans_binary(bcx: block, op: ast::binop, lhs: @ast::expr, // Remaining cases are eager: let lhs_res = trans_temp_expr(bcx, lhs); let rhs_res = trans_temp_expr(lhs_res.bcx, rhs); - ret trans_eager_binop(rhs_res.bcx, op, lhs_res.val, + ret trans_eager_binop(rhs_res.bcx, ex.span, + op, lhs_res.val, expr_ty(bcx, lhs), rhs_res.val, expr_ty(bcx, rhs), dest); } diff --git a/src/test/run-fail/divide-by-zero.rs b/src/test/run-fail/divide-by-zero.rs new file mode 100644 index 000000000000..8eb0d427ca43 --- /dev/null +++ b/src/test/run-fail/divide-by-zero.rs @@ -0,0 +1,5 @@ +// error-pattern:divide by zero +fn main() { + let y = 0; + let z = 1 / y; +} diff --git a/src/test/run-fail/mod-zero.rs b/src/test/run-fail/mod-zero.rs new file mode 100644 index 000000000000..7a10ed05aafe --- /dev/null +++ b/src/test/run-fail/mod-zero.rs @@ -0,0 +1,5 @@ +// error-pattern:modulo zero +fn main() { + let y = 0; + let z = 1 % y; +}