Skip to content

Commit

Permalink
properly short circuit const eval of && and ||
Browse files Browse the repository at this point in the history
As described in rust-lang#29608, the rhs of a logical expression is evaluated
even if evaluation of the lhs means no such evaluation should take
place.
  • Loading branch information
shahn committed Nov 17, 2015
1 parent 9f49ea0 commit fc0f890
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 81 deletions.
181 changes: 100 additions & 81 deletions src/librustc/middle/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,93 +828,112 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
}
_ => ty_hint
};
match (try!(eval_const_expr_partial(tcx, &**a, ty_hint, fn_args)),
try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args))) {
(Float(a), Float(b)) => {
match op.node {
hir::BiAdd => Float(a + b),
hir::BiSub => Float(a - b),
hir::BiMul => Float(a * b),
hir::BiDiv => Float(a / b),
hir::BiRem => Float(a % b),
hir::BiEq => fromb(a == b),
hir::BiLt => fromb(a < b),
hir::BiLe => fromb(a <= b),
hir::BiNe => fromb(a != b),
hir::BiGe => fromb(a >= b),
hir::BiGt => fromb(a > b),
_ => signal!(e, InvalidOpForFloats(op.node))
}
}
(Int(a), Int(b)) => {
match op.node {
hir::BiAdd => try!(const_int_checked_add(a,b,e,expr_int_type)),
hir::BiSub => try!(const_int_checked_sub(a,b,e,expr_int_type)),
hir::BiMul => try!(const_int_checked_mul(a,b,e,expr_int_type)),
hir::BiDiv => try!(const_int_checked_div(a,b,e,expr_int_type)),
hir::BiRem => try!(const_int_checked_rem(a,b,e,expr_int_type)),
hir::BiAnd | hir::BiBitAnd => Int(a & b),
hir::BiOr | hir::BiBitOr => Int(a | b),
hir::BiBitXor => Int(a ^ b),
hir::BiShl => try!(const_int_checked_shl(a,b,e,expr_int_type)),
hir::BiShr => try!(const_int_checked_shr(a,b,e,expr_int_type)),
hir::BiEq => fromb(a == b),
hir::BiLt => fromb(a < b),
hir::BiLe => fromb(a <= b),
hir::BiNe => fromb(a != b),
hir::BiGe => fromb(a >= b),
hir::BiGt => fromb(a > b)
}
}
(Uint(a), Uint(b)) => {
match op.node {
hir::BiAdd => try!(const_uint_checked_add(a,b,e,expr_uint_type)),
hir::BiSub => try!(const_uint_checked_sub(a,b,e,expr_uint_type)),
hir::BiMul => try!(const_uint_checked_mul(a,b,e,expr_uint_type)),
hir::BiDiv => try!(const_uint_checked_div(a,b,e,expr_uint_type)),
hir::BiRem => try!(const_uint_checked_rem(a,b,e,expr_uint_type)),
hir::BiAnd | hir::BiBitAnd => Uint(a & b),
hir::BiOr | hir::BiBitOr => Uint(a | b),
hir::BiBitXor => Uint(a ^ b),
hir::BiShl => try!(const_uint_checked_shl(a,b,e,expr_uint_type)),
hir::BiShr => try!(const_uint_checked_shr(a,b,e,expr_uint_type)),
hir::BiEq => fromb(a == b),
hir::BiLt => fromb(a < b),
hir::BiLe => fromb(a <= b),
hir::BiNe => fromb(a != b),
hir::BiGe => fromb(a >= b),
hir::BiGt => fromb(a > b),
match try!(eval_const_expr_partial(tcx, &**a, ty_hint, fn_args)) {
Bool(a) if op.node == hir::BiAnd => {
if !a {
Bool(a)
} else if let Bool(b) = try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args)) {
Bool(b)
} else {
signal!(e, MiscBinaryOp)
}
}
// shifts can have any integral type as their rhs
(Int(a), Uint(b)) => {
match op.node {
hir::BiShl => try!(const_int_checked_shl_via_uint(a,b,e,expr_int_type)),
hir::BiShr => try!(const_int_checked_shr_via_uint(a,b,e,expr_int_type)),
_ => signal!(e, InvalidOpForIntUint(op.node)),
Bool(a) if op.node == hir::BiOr => {
if a {
Bool(a)
} else if let Bool(b) = try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args)) {
Bool(b)
} else {
signal!(e, MiscBinaryOp)
}
}
(Uint(a), Int(b)) => {
match op.node {
hir::BiShl => try!(const_uint_checked_shl_via_int(a,b,e,expr_uint_type)),
hir::BiShr => try!(const_uint_checked_shr_via_int(a,b,e,expr_uint_type)),
_ => signal!(e, InvalidOpForUintInt(op.node)),
x => {
match (x, try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args))) {
(Float(a), Float(b)) => {
match op.node {
hir::BiAdd => Float(a + b),
hir::BiSub => Float(a - b),
hir::BiMul => Float(a * b),
hir::BiDiv => Float(a / b),
hir::BiRem => Float(a % b),
hir::BiEq => fromb(a == b),
hir::BiLt => fromb(a < b),
hir::BiLe => fromb(a <= b),
hir::BiNe => fromb(a != b),
hir::BiGe => fromb(a >= b),
hir::BiGt => fromb(a > b),
_ => signal!(e, InvalidOpForFloats(op.node))
}
}
(Int(a), Int(b)) => {
match op.node {
hir::BiAdd => try!(const_int_checked_add(a,b,e,expr_int_type)),
hir::BiSub => try!(const_int_checked_sub(a,b,e,expr_int_type)),
hir::BiMul => try!(const_int_checked_mul(a,b,e,expr_int_type)),
hir::BiDiv => try!(const_int_checked_div(a,b,e,expr_int_type)),
hir::BiRem => try!(const_int_checked_rem(a,b,e,expr_int_type)),
hir::BiAnd | hir::BiBitAnd => Int(a & b),
hir::BiOr | hir::BiBitOr => Int(a | b),
hir::BiBitXor => Int(a ^ b),
hir::BiShl => try!(const_int_checked_shl(a,b,e,expr_int_type)),
hir::BiShr => try!(const_int_checked_shr(a,b,e,expr_int_type)),
hir::BiEq => fromb(a == b),
hir::BiLt => fromb(a < b),
hir::BiLe => fromb(a <= b),
hir::BiNe => fromb(a != b),
hir::BiGe => fromb(a >= b),
hir::BiGt => fromb(a > b)
}
}
(Uint(a), Uint(b)) => {
match op.node {
hir::BiAdd => try!(const_uint_checked_add(a,b,e,expr_uint_type)),
hir::BiSub => try!(const_uint_checked_sub(a,b,e,expr_uint_type)),
hir::BiMul => try!(const_uint_checked_mul(a,b,e,expr_uint_type)),
hir::BiDiv => try!(const_uint_checked_div(a,b,e,expr_uint_type)),
hir::BiRem => try!(const_uint_checked_rem(a,b,e,expr_uint_type)),
hir::BiAnd | hir::BiBitAnd => Uint(a & b),
hir::BiOr | hir::BiBitOr => Uint(a | b),
hir::BiBitXor => Uint(a ^ b),
hir::BiShl => try!(const_uint_checked_shl(a,b,e,expr_uint_type)),
hir::BiShr => try!(const_uint_checked_shr(a,b,e,expr_uint_type)),
hir::BiEq => fromb(a == b),
hir::BiLt => fromb(a < b),
hir::BiLe => fromb(a <= b),
hir::BiNe => fromb(a != b),
hir::BiGe => fromb(a >= b),
hir::BiGt => fromb(a > b),
}
}
// shifts can have any integral type as their rhs
(Int(a), Uint(b)) => {
match op.node {
hir::BiShl => try!(const_int_checked_shl_via_uint(a,b,e,expr_int_type)),
hir::BiShr => try!(const_int_checked_shr_via_uint(a,b,e,expr_int_type)),
_ => signal!(e, InvalidOpForIntUint(op.node)),
}
}
(Uint(a), Int(b)) => {
match op.node {
hir::BiShl => try!(const_uint_checked_shl_via_int(a,b,e,expr_uint_type)),
hir::BiShr => try!(const_uint_checked_shr_via_int(a,b,e,expr_uint_type)),
_ => signal!(e, InvalidOpForUintInt(op.node)),
}
}
(Bool(a), Bool(b)) => {
Bool(match op.node {
hir::BiBitXor => a ^ b,
hir::BiBitAnd => a & b,
hir::BiBitOr => a | b,
hir::BiEq => a == b,
hir::BiNe => a != b,
_ => signal!(e, InvalidOpForBools(op.node)),
})
}

_ => signal!(e, MiscBinaryOp),
}
}
(Bool(a), Bool(b)) => {
Bool(match op.node {
hir::BiAnd => a && b,
hir::BiOr => a || b,
hir::BiBitXor => a ^ b,
hir::BiBitAnd => a & b,
hir::BiBitOr => a | b,
hir::BiEq => a == b,
hir::BiNe => a != b,
_ => signal!(e, InvalidOpForBools(op.node)),
})
}

_ => signal!(e, MiscBinaryOp),
}
}
hir::ExprCast(ref base, ref target_ty) => {
Expand Down
15 changes: 15 additions & 0 deletions src/librustc_trans/trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,21 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let is_float = ty.is_fp();
let signed = ty.is_signed();

if ty.is_bool() && (b.node == hir::BiOr || b.node == hir::BiAnd) {
match eval_const_expr_partial(cx.tcx(),
&e1,
ExprTypeChecked,
None) {
Ok(ConstVal::Bool(false)) if b.node == hir::BiAnd => {
return Ok(te1)
}
Ok(ConstVal::Bool(true)) if b.node == hir::BiOr => {
return Ok(te1)
}
_ => ()
}
}

let (te2, _) = try!(const_expr(cx, &**e2, param_substs, fn_args, trueconst));

try!(check_binary_expr_validity(cx, e, ty, te1, te2, trueconst));
Expand Down
17 changes: 17 additions & 0 deletions src/test/run-pass/issue-29608.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Testcase provided by oli-obk

const X: bool = false && ((1u8 - 5u8) == 42);

fn main() {
println!("{}", X);
}

0 comments on commit fc0f890

Please sign in to comment.