make both unary_op and binary_op fully typed, including a return type

This commit is contained in:
Ralf Jung 2019-08-10 19:40:56 +02:00
parent 0a2a517fe6
commit 3edf099266
9 changed files with 90 additions and 69 deletions

View File

@ -11,9 +11,8 @@ use rustc::hir::def::DefKind;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled, ScalarMaybeUndef}; use rustc::mir::interpret::{ConstEvalErr, ErrorHandled, ScalarMaybeUndef};
use rustc::mir; use rustc::mir;
use rustc::ty::{self, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt, subst::Subst};
use rustc::ty::layout::{self, LayoutOf, VariantIdx}; use rustc::ty::layout::{self, LayoutOf, VariantIdx};
use rustc::ty::subst::Subst;
use rustc::traits::Reveal; use rustc::traits::Reveal;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
@ -415,7 +414,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
_bin_op: mir::BinOp, _bin_op: mir::BinOp,
_left: ImmTy<'tcx>, _left: ImmTy<'tcx>,
_right: ImmTy<'tcx>, _right: ImmTy<'tcx>,
) -> InterpResult<'tcx, (Scalar, bool)> { ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
Err( Err(
ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(), ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
) )

View File

@ -137,7 +137,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let l = self.read_immediate(args[0])?; let l = self.read_immediate(args[0])?;
let r = self.read_immediate(args[1])?; let r = self.read_immediate(args[1])?;
let is_add = intrinsic_name == "saturating_add"; let is_add = intrinsic_name == "saturating_add";
let (val, overflowed) = self.binary_op(if is_add { let (val, overflowed, _ty) = self.overflowing_binary_op(if is_add {
BinOp::Add BinOp::Add
} else { } else {
BinOp::Sub BinOp::Sub
@ -184,7 +184,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
"unchecked_shr" => BinOp::Shr, "unchecked_shr" => BinOp::Shr,
_ => bug!("Already checked for int ops") _ => bug!("Already checked for int ops")
}; };
let (val, overflowed) = self.binary_op(bin_op, l, r)?; let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, l, r)?;
if overflowed { if overflowed {
let layout = self.layout_of(substs.type_at(0))?; let layout = self.layout_of(substs.type_at(0))?;
let r_val = r.to_scalar()?.to_bits(layout.size)?; let r_val = r.to_scalar()?.to_bits(layout.size)?;

View File

@ -7,7 +7,7 @@ use std::hash::Hash;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::mir; use rustc::mir;
use rustc::ty::{self, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use super::{ use super::{
Allocation, AllocId, InterpResult, Scalar, AllocationExtra, Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
@ -176,7 +176,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
bin_op: mir::BinOp, bin_op: mir::BinOp,
left: ImmTy<'tcx, Self::PointerTag>, left: ImmTy<'tcx, Self::PointerTag>,
right: ImmTy<'tcx, Self::PointerTag>, right: ImmTy<'tcx, Self::PointerTag>,
) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool)>; ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
/// Heap allocations via the `box` keyword. /// Heap allocations via the `box` keyword.
fn box_alloc( fn box_alloc(

View File

@ -108,7 +108,7 @@ impl<'tcx, Tag> Immediate<Tag> {
// as input for binary and cast operations. // as input for binary and cast operations.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct ImmTy<'tcx, Tag=()> { pub struct ImmTy<'tcx, Tag=()> {
pub imm: Immediate<Tag>, pub(crate) imm: Immediate<Tag>,
pub layout: TyLayout<'tcx>, pub layout: TyLayout<'tcx>,
} }
@ -155,7 +155,7 @@ impl<Tag> Operand<Tag> {
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct OpTy<'tcx, Tag=()> { pub struct OpTy<'tcx, Tag=()> {
op: Operand<Tag>, op: Operand<Tag>, // Keep this private, it helps enforce invariants
pub layout: TyLayout<'tcx>, pub layout: TyLayout<'tcx>,
} }

View File

@ -1,5 +1,5 @@
use rustc::mir; use rustc::mir;
use rustc::ty::{self, layout::TyLayout}; use rustc::ty::{self, Ty, layout::{TyLayout, LayoutOf}};
use syntax::ast::FloatTy; use syntax::ast::FloatTy;
use rustc_apfloat::Float; use rustc_apfloat::Float;
use rustc::mir::interpret::{InterpResult, Scalar}; use rustc::mir::interpret::{InterpResult, Scalar};
@ -17,7 +17,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
right: ImmTy<'tcx, M::PointerTag>, right: ImmTy<'tcx, M::PointerTag>,
dest: PlaceTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let (val, overflowed) = self.binary_op(op, left, right)?; let (val, overflowed, ty) = self.overflowing_binary_op(op, left, right)?;
debug_assert_eq!(
self.tcx.intern_tup(&[ty, self.tcx.types.bool]),
dest.layout.ty,
"type mismatch for result of {:?}", op,
);
let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into()); let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
self.write_immediate(val, dest) self.write_immediate(val, dest)
} }
@ -31,7 +36,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
right: ImmTy<'tcx, M::PointerTag>, right: ImmTy<'tcx, M::PointerTag>,
dest: PlaceTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let (val, _overflowed) = self.binary_op(op, left, right)?; let (val, _overflowed, ty) = self.overflowing_binary_op(op, left, right)?;
assert_eq!(ty, dest.layout.ty, "type mismatch for result of {:?}", op);
self.write_scalar(val, dest) self.write_scalar(val, dest)
} }
} }
@ -42,7 +48,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
bin_op: mir::BinOp, bin_op: mir::BinOp,
l: char, l: char,
r: char, r: char,
) -> (Scalar<M::PointerTag>, bool) { ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
use rustc::mir::BinOp::*; use rustc::mir::BinOp::*;
let res = match bin_op { let res = match bin_op {
@ -54,7 +60,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ge => l >= r, Ge => l >= r,
_ => bug!("Invalid operation on char: {:?}", bin_op), _ => bug!("Invalid operation on char: {:?}", bin_op),
}; };
return (Scalar::from_bool(res), false); return (Scalar::from_bool(res), false, self.tcx.types.bool);
} }
fn binary_bool_op( fn binary_bool_op(
@ -62,7 +68,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
bin_op: mir::BinOp, bin_op: mir::BinOp,
l: bool, l: bool,
r: bool, r: bool,
) -> (Scalar<M::PointerTag>, bool) { ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
use rustc::mir::BinOp::*; use rustc::mir::BinOp::*;
let res = match bin_op { let res = match bin_op {
@ -77,32 +83,33 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
BitXor => l ^ r, BitXor => l ^ r,
_ => bug!("Invalid operation on bool: {:?}", bin_op), _ => bug!("Invalid operation on bool: {:?}", bin_op),
}; };
return (Scalar::from_bool(res), false); return (Scalar::from_bool(res), false, self.tcx.types.bool);
} }
fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>( fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>(
&self, &self,
bin_op: mir::BinOp, bin_op: mir::BinOp,
ty: Ty<'tcx>,
l: F, l: F,
r: F, r: F,
) -> (Scalar<M::PointerTag>, bool) { ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
use rustc::mir::BinOp::*; use rustc::mir::BinOp::*;
let val = match bin_op { let (val, ty) = match bin_op {
Eq => Scalar::from_bool(l == r), Eq => (Scalar::from_bool(l == r), self.tcx.types.bool),
Ne => Scalar::from_bool(l != r), Ne => (Scalar::from_bool(l != r), self.tcx.types.bool),
Lt => Scalar::from_bool(l < r), Lt => (Scalar::from_bool(l < r), self.tcx.types.bool),
Le => Scalar::from_bool(l <= r), Le => (Scalar::from_bool(l <= r), self.tcx.types.bool),
Gt => Scalar::from_bool(l > r), Gt => (Scalar::from_bool(l > r), self.tcx.types.bool),
Ge => Scalar::from_bool(l >= r), Ge => (Scalar::from_bool(l >= r), self.tcx.types.bool),
Add => (l + r).value.into(), Add => ((l + r).value.into(), ty),
Sub => (l - r).value.into(), Sub => ((l - r).value.into(), ty),
Mul => (l * r).value.into(), Mul => ((l * r).value.into(), ty),
Div => (l / r).value.into(), Div => ((l / r).value.into(), ty),
Rem => (l % r).value.into(), Rem => ((l % r).value.into(), ty),
_ => bug!("invalid float op: `{:?}`", bin_op), _ => bug!("invalid float op: `{:?}`", bin_op),
}; };
return (val, false); return (val, false, ty);
} }
fn binary_int_op( fn binary_int_op(
@ -113,7 +120,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
left_layout: TyLayout<'tcx>, left_layout: TyLayout<'tcx>,
r: u128, r: u128,
right_layout: TyLayout<'tcx>, right_layout: TyLayout<'tcx>,
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> { ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
use rustc::mir::BinOp::*; use rustc::mir::BinOp::*;
// Shift ops can have an RHS with a different numeric type. // Shift ops can have an RHS with a different numeric type.
@ -142,7 +149,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
}; };
let truncated = self.truncate(result, left_layout); let truncated = self.truncate(result, left_layout);
return Ok((Scalar::from_uint(truncated, size), oflo)); return Ok((Scalar::from_uint(truncated, size), oflo, left_layout.ty));
} }
// For the remaining ops, the types must be the same on both sides // For the remaining ops, the types must be the same on both sides
@ -167,7 +174,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if let Some(op) = op { if let Some(op) = op {
let l = self.sign_extend(l, left_layout) as i128; let l = self.sign_extend(l, left_layout) as i128;
let r = self.sign_extend(r, right_layout) as i128; let r = self.sign_extend(r, right_layout) as i128;
return Ok((Scalar::from_bool(op(&l, &r)), false)); return Ok((Scalar::from_bool(op(&l, &r)), false, self.tcx.types.bool));
} }
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op { let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
Div if r == 0 => throw_panic!(DivisionByZero), Div if r == 0 => throw_panic!(DivisionByZero),
@ -187,7 +194,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Rem | Div => { Rem | Div => {
// int_min / -1 // int_min / -1
if r == -1 && l == (1 << (size.bits() - 1)) { if r == -1 && l == (1 << (size.bits() - 1)) {
return Ok((Scalar::from_uint(l, size), true)); return Ok((Scalar::from_uint(l, size), true, left_layout.ty));
} }
}, },
_ => {}, _ => {},
@ -202,25 +209,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// this may be out-of-bounds for the result type, so we have to truncate ourselves // this may be out-of-bounds for the result type, so we have to truncate ourselves
let result = result as u128; let result = result as u128;
let truncated = self.truncate(result, left_layout); let truncated = self.truncate(result, left_layout);
return Ok((Scalar::from_uint(truncated, size), oflo)); return Ok((Scalar::from_uint(truncated, size), oflo, left_layout.ty));
} }
} }
let size = left_layout.size; let size = left_layout.size;
// only ints left let (val, ty) = match bin_op {
let val = match bin_op { Eq => (Scalar::from_bool(l == r), self.tcx.types.bool),
Eq => Scalar::from_bool(l == r), Ne => (Scalar::from_bool(l != r), self.tcx.types.bool),
Ne => Scalar::from_bool(l != r),
Lt => Scalar::from_bool(l < r), Lt => (Scalar::from_bool(l < r), self.tcx.types.bool),
Le => Scalar::from_bool(l <= r), Le => (Scalar::from_bool(l <= r), self.tcx.types.bool),
Gt => Scalar::from_bool(l > r), Gt => (Scalar::from_bool(l > r), self.tcx.types.bool),
Ge => Scalar::from_bool(l >= r), Ge => (Scalar::from_bool(l >= r), self.tcx.types.bool),
BitOr => Scalar::from_uint(l | r, size), BitOr => (Scalar::from_uint(l | r, size), left_layout.ty),
BitAnd => Scalar::from_uint(l & r, size), BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty),
BitXor => Scalar::from_uint(l ^ r, size), BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty),
Add | Sub | Mul | Rem | Div => { Add | Sub | Mul | Rem | Div => {
debug_assert!(!left_layout.abi.is_signed()); debug_assert!(!left_layout.abi.is_signed());
@ -236,7 +242,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}; };
let (result, oflo) = op(l, r); let (result, oflo) = op(l, r);
let truncated = self.truncate(result, left_layout); let truncated = self.truncate(result, left_layout);
return Ok((Scalar::from_uint(truncated, size), oflo || truncated != result)); return Ok((
Scalar::from_uint(truncated, size),
oflo || truncated != result,
left_layout.ty,
));
} }
_ => { _ => {
@ -250,17 +260,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
}; };
Ok((val, false)) Ok((val, false, ty))
} }
/// Returns the result of the specified operation and whether it overflowed. /// Returns the result of the specified operation, whether it overflowed, and
#[inline] /// the result type.
pub fn binary_op( pub fn overflowing_binary_op(
&self, &self,
bin_op: mir::BinOp, bin_op: mir::BinOp,
left: ImmTy<'tcx, M::PointerTag>, left: ImmTy<'tcx, M::PointerTag>,
right: ImmTy<'tcx, M::PointerTag>, right: ImmTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> { ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
bin_op, *left, left.layout.ty, *right, right.layout.ty); bin_op, *left, left.layout.ty, *right, right.layout.ty);
@ -279,11 +289,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
ty::Float(fty) => { ty::Float(fty) => {
assert_eq!(left.layout.ty, right.layout.ty); assert_eq!(left.layout.ty, right.layout.ty);
let ty = left.layout.ty;
let left = left.to_scalar()?; let left = left.to_scalar()?;
let right = right.to_scalar()?; let right = right.to_scalar()?;
Ok(match fty { Ok(match fty {
FloatTy::F32 => self.binary_float_op(bin_op, left.to_f32()?, right.to_f32()?), FloatTy::F32 =>
FloatTy::F64 => self.binary_float_op(bin_op, left.to_f64()?, right.to_f64()?), self.binary_float_op(bin_op, ty, left.to_f32()?, right.to_f32()?),
FloatTy::F64 =>
self.binary_float_op(bin_op, ty, left.to_f64()?, right.to_f64()?),
}) })
} }
_ if left.layout.ty.is_integral() => { _ if left.layout.ty.is_integral() => {
@ -312,11 +325,23 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
} }
/// Typed version of `checked_binary_op`, returning an `ImmTy`. Also ignores overflows.
#[inline]
pub fn binary_op(
&self,
bin_op: mir::BinOp,
left: ImmTy<'tcx, M::PointerTag>,
right: ImmTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
let (val, _overflow, ty) = self.overflowing_binary_op(bin_op, left, right)?;
Ok(ImmTy::from_scalar(val, self.layout_of(ty)?))
}
pub fn unary_op( pub fn unary_op(
&self, &self,
un_op: mir::UnOp, un_op: mir::UnOp,
val: ImmTy<'tcx, M::PointerTag>, val: ImmTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, Scalar<M::PointerTag>> { ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
use rustc::mir::UnOp::*; use rustc::mir::UnOp::*;
let layout = val.layout; let layout = val.layout;
@ -330,7 +355,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Not => !val, Not => !val,
_ => bug!("Invalid bool op {:?}", un_op) _ => bug!("Invalid bool op {:?}", un_op)
}; };
Ok(Scalar::from_bool(res)) Ok(ImmTy::from_scalar(Scalar::from_bool(res), self.layout_of(self.tcx.types.bool)?))
} }
ty::Float(fty) => { ty::Float(fty) => {
let res = match (un_op, fty) { let res = match (un_op, fty) {
@ -338,7 +363,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
_ => bug!("Invalid float op {:?}", un_op) _ => bug!("Invalid float op {:?}", un_op)
}; };
Ok(res) Ok(ImmTy::from_scalar(res, layout))
} }
_ => { _ => {
assert!(layout.ty.is_integral()); assert!(layout.ty.is_integral());
@ -351,7 +376,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
}; };
// res needs tuncating // res needs tuncating
Ok(Scalar::from_uint(self.truncate(res, layout), layout.size)) Ok(ImmTy::from_uint(self.truncate(res, layout), layout))
} }
} }
} }

View File

@ -45,7 +45,7 @@ pub enum Place<Tag=(), Id=AllocId> {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct PlaceTy<'tcx, Tag=()> { pub struct PlaceTy<'tcx, Tag=()> {
place: Place<Tag>, place: Place<Tag>, // Keep this private, it helps enforce invariants
pub layout: TyLayout<'tcx>, pub layout: TyLayout<'tcx>,
} }

View File

@ -177,7 +177,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// The operand always has the same type as the result. // The operand always has the same type as the result.
let val = self.read_immediate(self.eval_operand(operand, Some(dest.layout))?)?; let val = self.read_immediate(self.eval_operand(operand, Some(dest.layout))?)?;
let val = self.unary_op(un_op, val)?; let val = self.unary_op(un_op, val)?;
self.write_scalar(val, dest)?; assert_eq!(val.layout, dest.layout, "layout mismatch for result of {:?}", un_op);
self.write_immediate(*val, dest)?;
} }
Aggregate(ref kind, ref operands) => { Aggregate(ref kind, ref operands) => {

View File

@ -50,10 +50,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
for (index, &const_int) in values.iter().enumerate() { for (index, &const_int) in values.iter().enumerate() {
// Compare using binary_op, to also support pointer values // Compare using binary_op, to also support pointer values
let (res, _) = self.binary_op(mir::BinOp::Eq, let res = self.overflowing_binary_op(mir::BinOp::Eq,
discr, discr,
ImmTy::from_uint(const_int, discr.layout), ImmTy::from_uint(const_int, discr.layout),
)?; )?.0;
if res.to_bool()? { if res.to_bool()? {
target_block = targets[index]; target_block = targets[index];
break; break;

View File

@ -452,11 +452,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// Now run the actual operation. // Now run the actual operation.
this.ecx.unary_op(op, prim) this.ecx.unary_op(op, prim)
})?; })?;
let res = ImmTy { Some(val.into())
imm: Immediate::Scalar(val.into()),
layout: place_layout,
};
Some(res.into())
} }
Rvalue::CheckedBinaryOp(op, ref left, ref right) | Rvalue::CheckedBinaryOp(op, ref left, ref right) |
Rvalue::BinaryOp(op, ref left, ref right) => { Rvalue::BinaryOp(op, ref left, ref right) => {
@ -510,8 +506,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
this.ecx.read_immediate(left) this.ecx.read_immediate(left)
})?; })?;
trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
let (val, overflow) = self.use_ecx(source_info, |this| { let (val, overflow, _ty) = self.use_ecx(source_info, |this| {
this.ecx.binary_op(op, l, r) this.ecx.overflowing_binary_op(op, l, r)
})?; })?;
let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
Immediate::ScalarPair( Immediate::ScalarPair(