/// We cannot do self.read_value(self.eval_operand) due to eval_operand taking &mut self,
/// so this helps avoid unnecessary let.
- pub fn eval_operand_and_read_valty(
+ #[inline]
+ pub fn eval_operand_and_read_value(
&mut self,
op: &mir::Operand<'tcx>,
+ layout: Option<TyLayout<'tcx>>,
) -> EvalResult<'tcx, ValTy<'tcx>> {
- let op = self.eval_operand(op, None)?;
+ let op = self.eval_operand(op, layout)?;
self.read_value(op)
}
- pub fn eval_operand_and_read_scalar(
- &mut self,
- op: &mir::Operand<'tcx>,
- ) -> EvalResult<'tcx, ScalarMaybeUndef> {
- Ok(self.eval_operand_and_read_valty(op)?.to_scalar_or_undef())
- }
/// reads a tag and produces the corresponding variant index
pub fn read_discriminant_as_variant_index(
use super::{EvalContext, Machine};
+/// Classify whether an operator is "left-homogeneous", i.e. the LHS has the
+/// same type as the result.
+#[inline]
+fn binop_left_homogeneous(op: mir::BinOp) -> bool {
+ use rustc::mir::BinOp::*;
+ match op {
+ Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr |
+ Offset | Shl | Shr =>
+ true,
+ Eq | Ne | Lt | Le | Gt | Ge =>
+ false,
+ }
+}
+/// Classify whether an operator is "right-homogeneous", i.e. the RHS has the
+/// same type as the LHS.
+#[inline]
+fn binop_right_homogeneous(op: mir::BinOp) -> bool {
+ use rustc::mir::BinOp::*;
+ match op {
+ Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr |
+ Eq | Ne | Lt | Le | Gt | Ge =>
+ true,
+ Offset | Shl | Shr =>
+ false,
+ }
+}
+
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
pub fn inc_step_counter_and_detect_loops(&mut self) -> EvalResult<'tcx, ()> {
/// The number of steps between loop detector snapshots.
}
BinaryOp(bin_op, ref left, ref right) => {
- let left = self.eval_operand_and_read_valty(left)?;
- let right = self.eval_operand_and_read_valty(right)?;
+ let layout = if binop_left_homogeneous(bin_op) { Some(dest.layout) } else { None };
+ let left = self.eval_operand_and_read_value(left, layout)?;
+ let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None };
+ let right = self.eval_operand_and_read_value(right, layout)?;
self.binop_ignore_overflow(
bin_op,
left,
}
CheckedBinaryOp(bin_op, ref left, ref right) => {
- let left = self.eval_operand_and_read_valty(left)?;
- let right = self.eval_operand_and_read_valty(right)?;
+ // Due to the extra boolean in the result, we can never reuse the `dest.layout`.
+ let left = self.eval_operand_and_read_value(left, None)?;
+ let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None };
+ let right = self.eval_operand_and_read_value(right, layout)?;
self.binop_with_overflow(
bin_op,
left,
}
UnaryOp(un_op, ref operand) => {
- let val = self.eval_operand_and_read_scalar(operand)?;
- let val = self.unary_op(un_op, val.not_undef()?, dest.layout)?;
+ // The operand always has the same type as the result.
+ let val = self.eval_operand_and_read_value(operand, Some(dest.layout))?;
+ let val = self.unary_op(un_op, val.to_scalar()?, dest.layout)?;
self.write_scalar(val, dest)?;
}
target,
..
} => {
- let cond_val = self.eval_operand_and_read_scalar(cond)?.not_undef()?.to_bool()?;
+ let cond_val = self.eval_operand_and_read_value(cond, None)?.to_scalar()?.to_bool()?;
if expected == cond_val {
self.goto_block(target);
} else {
use rustc::mir::interpret::EvalErrorKind::*;
return match *msg {
BoundsCheck { ref len, ref index } => {
- let len = self.eval_operand_and_read_scalar(len)
- .expect("can't eval len")
+ let len = self.eval_operand_and_read_value(len, None)
+ .expect("can't eval len").to_scalar()?
.to_bits(self.memory().pointer_size())? as u64;
- let index = self.eval_operand_and_read_scalar(index)
- .expect("can't eval index")
+ let index = self.eval_operand_and_read_value(index, None)
+ .expect("can't eval index").to_scalar()?
.to_bits(self.memory().pointer_size())? as u64;
err!(BoundsCheck { len, index })
}