]> git.lizzy.rs Git - rust.git/commitdiff
Don't use the undefined bytes of PrimVal::Bytes
authorOliver Schneider <git-no-reply-9879165716479413131@oli-obk.de>
Wed, 21 Feb 2018 21:02:52 +0000 (22:02 +0100)
committerOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Thu, 8 Mar 2018 07:34:18 +0000 (08:34 +0100)
12 files changed:
src/librustc/mir/interpret/mod.rs
src/librustc/mir/interpret/value.rs
src/librustc/ty/mod.rs
src/librustc_mir/build/expr/as_rvalue.rs
src/librustc_mir/hair/cx/mod.rs
src/librustc_mir/interpret/cast.rs
src/librustc_mir/interpret/eval_context.rs
src/librustc_mir/interpret/memory.rs
src/librustc_mir/interpret/mod.rs
src/librustc_mir/interpret/operator.rs
src/librustc_mir/transform/const_prop.rs
src/test/run-pass/const-negation.rs

index 91f7d79108bb8d6a3c6a0f4e643f1d6fced47252..67f30f53a6810fb8feff799ca86cc57b12fbdeed 100644 (file)
@@ -10,7 +10,7 @@ macro_rules! err {
 
 pub use self::error::{EvalError, EvalResult, EvalErrorKind};
 
-pub use self::value::{PrimVal, PrimValKind, Value, Pointer, bytes_to_f32, bytes_to_f64};
+pub use self::value::{PrimVal, PrimValKind, Value, Pointer};
 
 use std::collections::BTreeMap;
 use std::fmt;
index c00956c0a8570445281d519f9be07352a64d6725..7289d74bfbb1bd653fa529cd7951ff7da8a11bb6 100644 (file)
@@ -4,22 +4,6 @@
 use ty;
 
 use super::{EvalResult, MemoryPointer, PointerArithmetic};
-use syntax::ast::FloatTy;
-use rustc_const_math::ConstFloat;
-
-pub fn bytes_to_f32(bits: u128) -> ConstFloat {
-    ConstFloat {
-        bits,
-        ty: FloatTy::F32,
-    }
-}
-
-pub fn bytes_to_f64(bits: u128) -> ConstFloat {
-    ConstFloat {
-        bits,
-        ty: FloatTy::F64,
-    }
-}
 
 /// A `Value` represents a single self-contained Rust value.
 ///
@@ -182,10 +166,6 @@ pub fn from_i128(n: i128) -> Self {
         PrimVal::Bytes(n as u128)
     }
 
-    pub fn from_float(f: ConstFloat) -> Self {
-        PrimVal::Bytes(f.bits)
-    }
-
     pub fn from_bool(b: bool) -> Self {
         PrimVal::Bytes(b as u128)
     }
@@ -260,14 +240,6 @@ pub fn to_i64(self) -> EvalResult<'tcx, i64> {
         })
     }
 
-    pub fn to_f32(self) -> EvalResult<'tcx, ConstFloat> {
-        self.to_bytes().map(bytes_to_f32)
-    }
-
-    pub fn to_f64(self) -> EvalResult<'tcx, ConstFloat> {
-        self.to_bytes().map(bytes_to_f64)
-    }
-
     pub fn to_bool(self) -> EvalResult<'tcx, bool> {
         match self.to_bytes()? {
             0 => Ok(false),
index cb09687a6573cc3e48238dd2985c8988c44da7f4..d730c95f4dd886c03cbbedc988f0d6d5011ac594 100644 (file)
@@ -1845,10 +1845,34 @@ pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
                         ..
                     }) => {
                         trace!("discriminants: {} ({:?})", b, repr_type);
-                        discr = Discr {
-                            val: b,
-                            ty: repr_type.to_ty(tcx),
-                        };
+                        let ty = repr_type.to_ty(tcx);
+                        if ty.is_signed() {
+                            let (ty, param_env) = tcx
+                                .lift_to_global(&(ty, param_env))
+                                .unwrap_or_else(|| {
+                                bug!("MIR: discriminants({:?}, {:?}) got \
+                                    type with inference types/regions",
+                                    ty, param_env);
+                            });
+                            let size = tcx.global_tcx()
+                                .layout_of(param_env.and(ty))
+                                .expect("int layout")
+                                .size
+                                .bits();
+                            let val = b as i128;
+                            // sign extend to i128
+                            let amt = 128 - size;
+                            let val = (val << amt) >> amt;
+                            discr = Discr {
+                                val: val as u128,
+                                ty,
+                            };
+                        } else {
+                            discr = Discr {
+                                val: b,
+                                ty,
+                            };
+                        }
                     }
                     _ => {
                         if !expr_did.is_local() {
index e2cc58b1fb050c78ee910364d2e586306329243e..59f44fa9229a925c2707d3829e06c909f0863450 100644 (file)
@@ -22,7 +22,6 @@
 use rustc::ty::{self, Ty};
 use rustc::mir::*;
 use rustc::mir::interpret::{Value, PrimVal};
-use syntax::ast;
 use syntax_pos::Span;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
@@ -382,9 +381,11 @@ pub fn build_binary_op(&mut self, mut block: BasicBlock,
 
     // Helper to get a `-1` value of the appropriate type
     fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
+        let bits = self.hir.type_bit_size(ty);
+        let n = (!0u128) >> (128 - bits);
         let literal = Literal::Value {
             value: self.hir.tcx().mk_const(ty::Const {
-                val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(-1i128 as u128))),
+                val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))),
                 ty
             })
         };
@@ -394,31 +395,14 @@ fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
 
     // Helper to get the minimum value of the appropriate type
     fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
-        let literal = match ty.sty {
-            ty::TyInt(ity) => {
-                let ity = match ity {
-                    ast::IntTy::Isize => self.hir.tcx().sess.target.isize_ty,
-                    other => other,
-                };
-                let val = match ity {
-                    ast::IntTy::I8  => i8::min_value() as i128,
-                    ast::IntTy::I16 => i16::min_value() as i128,
-                    ast::IntTy::I32 => i32::min_value() as i128,
-                    ast::IntTy::I64 => i64::min_value() as i128,
-                    ast::IntTy::I128 => i128::min_value() as i128,
-                    ast::IntTy::Isize => unreachable!(),
-                };
-
-                Literal::Value {
-                    value: self.hir.tcx().mk_const(ty::Const {
-                        val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))),
-                        ty
-                    })
-                }
-            }
-            _ => {
-                span_bug!(span, "Invalid type for minval_literal: `{:?}`", ty)
-            }
+        assert!(ty.is_signed());
+        let bits = self.hir.type_bit_size(ty);
+        let n = 1 << (bits - 1);
+        let literal = Literal::Value {
+            value: self.hir.tcx().mk_const(ty::Const {
+                val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))),
+                ty
+            })
         };
 
         self.literal_operand(span, ty, literal)
index 0b59ce9e117b8cde6c1f4e5ea40a3346371ae6e2..ec484785ca2f048396cfebf8a249f7334e9d4c56 100644 (file)
@@ -149,6 +149,26 @@ pub fn false_literal(&mut self) -> Literal<'tcx> {
         }
     }
 
+    pub fn type_bit_size(
+        &self,
+        ty: Ty<'tcx>,
+    ) -> u64 {
+        let tcx = self.tcx.global_tcx();
+        let (ty, param_env) = self
+            .tcx
+            .lift_to_global(&(ty, self.param_env))
+            .unwrap_or_else(|| {
+            bug!("MIR: Cx::const_eval_literal({:?}, {:?}) got \
+                type with inference types/regions",
+                ty, self.param_env);
+        });
+        tcx
+            .layout_of(param_env.and(ty))
+            .expect("int layout")
+            .size
+            .bits()
+    }
+
     pub fn const_eval_literal(
         &mut self,
         lit: &'tcx ast::LitKind,
@@ -156,6 +176,7 @@ pub fn const_eval_literal(
         sp: Span,
         neg: bool,
     ) -> Literal<'tcx> {
+        trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg);
         let tcx = self.tcx.global_tcx();
 
         let parse_float = |num: &str, fty| -> ConstFloat {
@@ -165,6 +186,15 @@ pub fn const_eval_literal(
             })
         };
 
+        let clamp = |n| {
+            let size = self.type_bit_size(ty);
+            trace!("clamp {} with size {} and amt {}", n, size, 128 - size);
+            let amt = 128 - size;
+            let result = (n << amt) >> amt;
+            trace!("clamp result: {}", result);
+            result
+        };
+
         use rustc::mir::interpret::*;
         let lit = match *lit {
             LitKind::Str(ref s, _) => {
@@ -185,9 +215,10 @@ pub fn const_eval_literal(
             LitKind::Int(n, _) if neg => {
                 let n = n as i128;
                 let n = n.overflowing_neg().0;
-                Value::ByVal(PrimVal::Bytes(n as u128))
+                let n = clamp(n as u128);
+                Value::ByVal(PrimVal::Bytes(n))
             },
-            LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)),
+            LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(clamp(n))),
             LitKind::Float(n, fty) => {
                 let n = n.as_str();
                 let mut f = parse_float(&n, fty);
index 7d7e6ec9451ed8c288753cafbb03f97721325c77..9b118b7fb78b68b07bea5992e6fca64eb836ac46 100644 (file)
@@ -14,75 +14,37 @@ pub(super) fn cast_primval(
         src_ty: Ty<'tcx>,
         dest_ty: Ty<'tcx>,
     ) -> EvalResult<'tcx, PrimVal> {
+        use rustc::ty::TypeVariants::*;
         trace!("Casting {:?}: {:?} to {:?}", val, src_ty, dest_ty);
-        let src_kind = self.ty_to_primval_kind(src_ty)?;
 
         match val {
             PrimVal::Undef => Ok(PrimVal::Undef),
             PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty),
-            val @ PrimVal::Bytes(_) => {
-                use rustc::mir::interpret::PrimValKind::*;
-                match src_kind {
-                    F32 => self.cast_from_float(val.to_f32()?, dest_ty),
-                    F64 => self.cast_from_float(val.to_f64()?, dest_ty),
-
-                    I8 | I16 | I32 | I64 | I128 => {
-                        self.cast_from_signed_int(val.to_i128()?, dest_ty)
-                    }
-
-                    Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => {
-                        self.cast_from_int(val.to_u128()?, dest_ty, false)
-                    }
+            PrimVal::Bytes(b) => {
+                match src_ty.sty {
+                    TyFloat(fty) => self.cast_from_float(b, fty, dest_ty),
+                    _ => self.cast_from_int(b, src_ty, dest_ty),
                 }
             }
         }
     }
 
-    fn cast_from_signed_int(&self, val: i128, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
-        self.cast_from_int(val as u128, ty, val < 0)
-    }
-
-    fn int_to_int(&self, v: i128, ty: IntTy) -> u128 {
-        match ty {
-            IntTy::I8 => v as i8 as u128,
-            IntTy::I16 => v as i16 as u128,
-            IntTy::I32 => v as i32 as u128,
-            IntTy::I64 => v as i64 as u128,
-            IntTy::I128 => v as u128,
-            IntTy::Isize => {
-                let ty = self.tcx.sess.target.isize_ty;
-                self.int_to_int(v, ty)
-            }
-        }
-    }
-    fn int_to_uint(&self, v: u128, ty: UintTy) -> u128 {
-        match ty {
-            UintTy::U8 => v as u8 as u128,
-            UintTy::U16 => v as u16 as u128,
-            UintTy::U32 => v as u32 as u128,
-            UintTy::U64 => v as u64 as u128,
-            UintTy::U128 => v,
-            UintTy::Usize => {
-                let ty = self.tcx.sess.target.usize_ty;
-                self.int_to_uint(v, ty)
-            }
-        }
-    }
-
     fn cast_from_int(
         &self,
         v: u128,
-        ty: Ty<'tcx>,
-        negative: bool,
+        src_ty: Ty<'tcx>,
+        dest_ty: Ty<'tcx>,
     ) -> EvalResult<'tcx, PrimVal> {
-        trace!("cast_from_int: {}, {}, {}", v, ty, negative);
+        trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty);
         use rustc::ty::TypeVariants::*;
-        match ty.sty {
-            // Casts to bool are not permitted by rustc, no need to handle them here.
-            TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))),
-            TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))),
+        match dest_ty.sty {
+            TyInt(_) | TyUint(_) => {
+                let v = self.sign_extend(v, src_ty)?;
+                let v = self.truncate(v, dest_ty)?;
+                Ok(PrimVal::Bytes(v))
+            }
 
-            TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)),
+            TyFloat(fty) if src_ty.is_signed() => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)),
             TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)),
 
             TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
@@ -91,31 +53,42 @@ fn cast_from_int(
             // No alignment check needed for raw pointers.  But we have to truncate to target ptr size.
             TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)),
 
-            _ => err!(Unimplemented(format!("int to {:?} cast", ty))),
+            // Casts to bool are not permitted by rustc, no need to handle them here.
+            _ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))),
         }
     }
 
-    fn cast_from_float(&self, val: ConstFloat, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
+    fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
         use rustc::ty::TypeVariants::*;
-        match ty.sty {
+        use rustc_apfloat::FloatConvert;
+        match dest_ty.sty {
+            // float -> uint
             TyUint(t) => {
                 let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
-                match val.ty {
-                    FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(val.bits).to_u128(width).value)),
-                    FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(val.bits).to_u128(width).value)),
+                match fty {
+                    FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(bits).to_u128(width).value)),
+                    FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(bits).to_u128(width).value)),
                 }
             },
-
+            // float -> int
             TyInt(t) => {
                 let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
-                match val.ty {
-                    FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(val.bits).to_i128(width).value)),
-                    FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(val.bits).to_i128(width).value)),
+                match fty {
+                    FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(bits).to_i128(width).value)),
+                    FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(bits).to_i128(width).value)),
                 }
             },
-
-            TyFloat(fty) => Ok(PrimVal::from_float(val.convert(fty))),
-            _ => err!(Unimplemented(format!("float to {:?} cast", ty))),
+            // f64 -> f32
+            TyFloat(FloatTy::F32) if fty == FloatTy::F64 => {
+                Ok(PrimVal::Bytes(Single::to_bits(Double::from_bits(bits).convert(&mut false).value)))
+            },
+            // f32 -> f64
+            TyFloat(FloatTy::F64) if fty == FloatTy::F32 => {
+                Ok(PrimVal::Bytes(Double::to_bits(Single::from_bits(bits).convert(&mut false).value)))
+            },
+            // identity cast
+            TyFloat(_) => Ok(PrimVal::Bytes(bits)),
+            _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))),
         }
     }
 
index e0ad306571d249c515f47c5bf16c65d6433d30bf..e38969e45d2fca372310c5296bf82351b32bab37 100644 (file)
@@ -19,7 +19,7 @@
 };
 
 use super::{Place, PlaceExtra, Memory,
-            HasMemory, MemoryKind, operator,
+            HasMemory, MemoryKind,
             Machine};
 
 pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
@@ -536,10 +536,10 @@ pub(super) fn eval_rvalue_into_place(
 
             UnaryOp(un_op, ref operand) => {
                 let val = self.eval_operand_to_primval(operand)?;
-                let kind = self.ty_to_primval_kind(dest_ty)?;
+                let val = self.unary_op(un_op, val, dest_ty)?;
                 self.write_primval(
                     dest,
-                    operator::unary_op(un_op, val, kind)?,
+                    val,
                     dest_ty,
                 )?;
             }
@@ -1677,6 +1677,22 @@ pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option<Span
             self.tcx.sess.err(&e.to_string());
         }
     }
+
+    pub fn sign_extend(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
+        let size = self.layout_of(ty)?.size.bits();
+        // sign extend
+        let amt = 128 - size;
+        // shift the unsigned value to the left
+        // and back to the right as signed (essentially fills with FF on the left)
+        Ok((((value << amt) as i128) >> amt) as u128)
+    }
+
+    pub fn truncate(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
+        let size = self.layout_of(ty)?.size.bits();
+        let amt = 128 - size;
+        // truncate (shift left to drop out leftover values, shift right to fill with zeroes)
+        Ok((value << amt) >> amt)
+    }
 }
 
 impl<'mir, 'tcx> Frame<'mir, 'tcx> {
index 72a966ab38d614f4b38333e3420cb093ce477063..010ec8b9bc03f046b3351457a1f8c6b082e9d8f1 100644 (file)
@@ -666,7 +666,10 @@ pub fn read_primval(&self, ptr: MemoryPointer, ptr_align: Align, size: u64, sign
         }
         // Now we do the actual reading
         let bytes = if signed {
-            read_target_int(endianness, bytes).unwrap() as u128
+            let bytes = read_target_int(endianness, bytes).unwrap() as u128;
+            let amt = 128 - (size * 8);
+            // truncate (shift left to drop out leftover values, shift right to fill with zeroes)
+            (bytes << amt) >> amt
         } else {
             read_target_uint(endianness, bytes).unwrap()
         };
index 8e0158569a86768dbfe6c74adda391aaf5d232be..b1ee3d568fd129c1a02638ba33f8d5076ca9e714 100644 (file)
@@ -30,5 +30,4 @@
 
 pub use self::machine::Machine;
 
-pub use self::operator::unary_op;
 pub use self::memory::{write_target_uint, write_target_int, read_target_uint, read_target_int};
index bad744194d5e35be354bb7ff9ce611862c2aa3a5..13087cfd473bc3d8ae9480d3aec37424d8951964 100644 (file)
@@ -1,12 +1,13 @@
 use rustc::mir;
-use rustc::ty::Ty;
+use rustc::ty::{self, Ty};
 use rustc_const_math::ConstFloat;
 use syntax::ast::FloatTy;
 use std::cmp::Ordering;
+use rustc::ty::layout::LayoutOf;
 
 use super::{EvalContext, Place, Machine, ValTy};
 
-use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64};
+use rustc::mir::interpret::{EvalResult, PrimVal, Value};
 
 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
     fn binop_with_overflow(
@@ -55,74 +56,6 @@ pub fn intrinsic_overflowing(
     }
 }
 
-macro_rules! overflow {
-    (overflowing_div, $l:expr, $r:expr) => ({
-        let (val, overflowed) = if $r == 0 {
-            ($l, true)
-        } else {
-            $l.overflowing_div($r)
-        };
-        let primval = PrimVal::Bytes(val as u128);
-        Ok((primval, overflowed))
-    });
-    (overflowing_rem, $l:expr, $r:expr) => ({
-        let (val, overflowed) = if $r == 0 {
-            ($l, true)
-        } else {
-            $l.overflowing_rem($r)
-        };
-        let primval = PrimVal::Bytes(val as u128);
-        Ok((primval, overflowed))
-    });
-    ($op:ident, $l:expr, $r:expr) => ({
-        let (val, overflowed) = $l.$op($r);
-        let primval = PrimVal::Bytes(val as u128);
-        Ok((primval, overflowed))
-    })
-}
-
-macro_rules! int_arithmetic {
-    ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
-        let l = $l;
-        let r = $r;
-        use rustc::mir::interpret::PrimValKind::*;
-        match $kind {
-            I8  => overflow!($int_op, l as i8,  r as i8),
-            I16 => overflow!($int_op, l as i16, r as i16),
-            I32 => overflow!($int_op, l as i32, r as i32),
-            I64 => overflow!($int_op, l as i64, r as i64),
-            I128 => overflow!($int_op, l as i128, r as i128),
-            U8  => overflow!($int_op, l as u8,  r as u8),
-            U16 => overflow!($int_op, l as u16, r as u16),
-            U32 => overflow!($int_op, l as u32, r as u32),
-            U64 => overflow!($int_op, l as u64, r as u64),
-            U128 => overflow!($int_op, l as u128, r as u128),
-            _ => bug!("int_arithmetic should only be called on int primvals"),
-        }
-    })
-}
-
-macro_rules! int_shift {
-    ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
-        let l = $l;
-        let r = $r;
-        let r_wrapped = r as u32;
-        match $kind {
-            I8  => overflow!($int_op, l as i8,  r_wrapped),
-            I16 => overflow!($int_op, l as i16, r_wrapped),
-            I32 => overflow!($int_op, l as i32, r_wrapped),
-            I64 => overflow!($int_op, l as i64, r_wrapped),
-            I128 => overflow!($int_op, l as i128, r_wrapped),
-            U8  => overflow!($int_op, l as u8,  r_wrapped),
-            U16 => overflow!($int_op, l as u16, r_wrapped),
-            U32 => overflow!($int_op, l as u32, r_wrapped),
-            U64 => overflow!($int_op, l as u64, r_wrapped),
-            U128 => overflow!($int_op, l as u128, r_wrapped),
-            _ => bug!("int_shift should only be called on int primvals"),
-        }.map(|(val, over)| (val, over || r != r_wrapped as u128))
-    })
-}
-
 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
     /// Returns the result of the specified operation and whether it overflowed.
     pub fn binary_op(
@@ -134,11 +67,10 @@ pub fn binary_op(
         right_ty: Ty<'tcx>,
     ) -> EvalResult<'tcx, (PrimVal, bool)> {
         use rustc::mir::BinOp::*;
-        use rustc::mir::interpret::PrimValKind::*;
 
         let left_kind = self.ty_to_primval_kind(left_ty)?;
         let right_kind = self.ty_to_primval_kind(right_ty)?;
-        //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
+        trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
 
         // I: Handle operations that support pointers
         if !left_kind.is_float() && !right_kind.is_float() {
@@ -153,11 +85,19 @@ pub fn binary_op(
 
         // These ops can have an RHS with a different numeric type.
         if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
-            return match bin_op {
-                Shl => int_shift!(left_kind, overflowing_shl, l, r),
-                Shr => int_shift!(left_kind, overflowing_shr, l, r),
+            let op: fn(u128, u32) -> (u128, bool) = match bin_op {
+                Shl => u128::overflowing_shl,
+                Shr => u128::overflowing_shr,
                 _ => bug!("it has already been checked that this is a shift op"),
             };
+            let l = if left_ty.is_signed() {
+                self.sign_extend(l, left_ty)?
+            } else {
+                l
+            };
+            let (result, oflo) = op(l, r as u32);
+            let truncated = self.truncate(result, left_ty)?;
+            return Ok((PrimVal::Bytes(truncated), oflo || truncated != result));
         }
 
         if left_kind != right_kind {
@@ -197,41 +137,95 @@ pub fn binary_op(
             }
         };
 
-        let val = match (bin_op, left_kind) {
-            (_, F32) => float_op(bin_op, l, r, FloatTy::F32),
-            (_, F64) => float_op(bin_op, l, r, FloatTy::F64),
-
-
-            (Eq, _) => PrimVal::from_bool(l == r),
-            (Ne, _) => PrimVal::from_bool(l != r),
-
-            (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
-            (Lt, _) => PrimVal::from_bool(l < r),
-            (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
-            (Le, _) => PrimVal::from_bool(l <= r),
-            (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
-            (Gt, _) => PrimVal::from_bool(l > r),
-            (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
-            (Ge, _) => PrimVal::from_bool(l >= r),
+        if left_ty.is_signed() {
+            let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
+                Lt => Some(i128::lt),
+                Le => Some(i128::le),
+                Gt => Some(i128::gt),
+                Ge => Some(i128::ge),
+                _ => None,
+            };
+            if let Some(op) = op {
+                let l = self.sign_extend(l, left_ty)? as i128;
+                let r = self.sign_extend(r, right_ty)? as i128;
+                return Ok((PrimVal::from_bool(op(&l, &r)), false));
+            }
+            let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
+                Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)),
+                Div => Some(i128::overflowing_div),
+                Rem => Some(i128::overflowing_rem),
+                Add => Some(i128::overflowing_add),
+                Sub => Some(i128::overflowing_sub),
+                Mul => Some(i128::overflowing_mul),
+                _ => None,
+            };
+            if let Some(op) = op {
+                let l128 = self.sign_extend(l, left_ty)? as i128;
+                let r = self.sign_extend(r, right_ty)? as i128;
+                let size = self.layout_of(left_ty)?.size.bits();
+                match bin_op {
+                    Rem | Div => {
+                        // int_min / -1
+                        if r == -1 && l == (1 << (size - 1)) {
+                            return Ok((PrimVal::Bytes(l), true));
+                        }
+                    },
+                    _ => {},
+                }
+                trace!("{}, {}, {}", l, l128, r);
+                let (result, mut oflo) = op(l128, r);
+                trace!("{}, {}", result, oflo);
+                if !oflo && size != 128 {
+                    let max = 1 << (size - 1);
+                    oflo = result >= max || result < -max;
+                }
+                let result = result as u128;
+                let truncated = self.truncate(result, left_ty)?;
+                return Ok((PrimVal::Bytes(truncated), oflo));
+            }
+        }
 
-            (BitOr, _) => PrimVal::Bytes(l | r),
-            (BitAnd, _) => PrimVal::Bytes(l & r),
-            (BitXor, _) => PrimVal::Bytes(l ^ r),
+        if let ty::TyFloat(fty) = left_ty.sty {
+            return Ok((float_op(bin_op, l, r, fty), false));
+        }
 
-            (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
-            (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
-            (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
-            (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
-            (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
+        // only ints left
+        let val = match bin_op {
+            Eq => PrimVal::from_bool(l == r),
+            Ne => PrimVal::from_bool(l != r),
+
+            Lt => PrimVal::from_bool(l < r),
+            Le => PrimVal::from_bool(l <= r),
+            Gt => PrimVal::from_bool(l > r),
+            Ge => PrimVal::from_bool(l >= r),
+
+            BitOr => PrimVal::Bytes(l | r),
+            BitAnd => PrimVal::Bytes(l & r),
+            BitXor => PrimVal::Bytes(l ^ r),
+
+            Add | Sub | Mul | Rem | Div => {
+                let op: fn(u128, u128) -> (u128, bool) = match bin_op {
+                    Add => u128::overflowing_add,
+                    Sub => u128::overflowing_sub,
+                    Mul => u128::overflowing_mul,
+                    Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)),
+                    Div => u128::overflowing_div,
+                    Rem => u128::overflowing_rem,
+                    _ => bug!(),
+                };
+                let (result, oflo) = op(l, r);
+                let truncated = self.truncate(result, left_ty)?;
+                return Ok((PrimVal::Bytes(truncated), oflo || truncated != result));
+            }
 
             _ => {
                 let msg = format!(
                     "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
                     bin_op,
                     left,
-                    left_kind,
+                    left_ty,
                     right,
-                    right_kind
+                    right_ty,
                 );
                 return err!(Unimplemented(msg));
             }
@@ -239,52 +233,33 @@ pub fn binary_op(
 
         Ok((val, false))
     }
-}
 
-pub fn unary_op<'tcx>(
-    un_op: mir::UnOp,
-    val: PrimVal,
-    val_kind: PrimValKind,
-) -> EvalResult<'tcx, PrimVal> {
-    use rustc::mir::UnOp::*;
-    use rustc::mir::interpret::PrimValKind::*;
-
-    let bytes = val.to_bytes()?;
+    pub fn unary_op(
+        &self,
+        un_op: mir::UnOp,
+        val: PrimVal,
+        ty: Ty<'tcx>,
+    ) -> EvalResult<'tcx, PrimVal> {
+        use rustc::mir::UnOp::*;
+        use rustc_apfloat::ieee::{Single, Double};
+        use rustc_apfloat::Float;
 
-    let result_bytes = match (un_op, val_kind) {
-        (Not, Bool) => !val.to_bool()? as u128,
+        let bytes = val.to_bytes()?;
+        let size = self.layout_of(ty)?.size.bits();
 
-        (Not, U8) => !(bytes as u8) as u128,
-        (Not, U16) => !(bytes as u16) as u128,
-        (Not, U32) => !(bytes as u32) as u128,
-        (Not, U64) => !(bytes as u64) as u128,
-        (Not, U128) => !bytes,
+        let result_bytes = match (un_op, &ty.sty) {
 
-        (Not, I8) => !(bytes as i8) as u128,
-        (Not, I16) => !(bytes as i16) as u128,
-        (Not, I32) => !(bytes as i32) as u128,
-        (Not, I64) => !(bytes as i64) as u128,
-        (Not, I128) => !(bytes as i128) as u128,
+            (Not, ty::TyBool) => !val.to_bool()? as u128,
 
-        (Neg, I8) if bytes == i8::min_value() as u128 => return err!(OverflowingMath),
-        (Neg, I8) => -(bytes as i8) as u128,
-        (Neg, I16) if bytes == i16::min_value() as u128 => return err!(OverflowingMath),
-        (Neg, I16) => -(bytes as i16) as u128,
-        (Neg, I32) if bytes == i32::min_value() as u128 => return err!(OverflowingMath),
-        (Neg, I32) => -(bytes as i32) as u128,
-        (Neg, I64) if bytes == i64::min_value() as u128 => return err!(OverflowingMath),
-        (Neg, I64) => -(bytes as i64) as u128,
-        (Neg, I128) if bytes == i128::min_value() as u128 => return err!(OverflowingMath),
-        (Neg, I128) => -(bytes as i128) as u128,
+            (Not, _) => !bytes,
 
-        (Neg, F32) => (-bytes_to_f32(bytes)).bits,
-        (Neg, F64) => (-bytes_to_f64(bytes)).bits,
+            (Neg, ty::TyFloat(FloatTy::F32)) => Single::to_bits(-Single::from_bits(bytes)),
+            (Neg, ty::TyFloat(FloatTy::F64)) => Double::to_bits(-Double::from_bits(bytes)),
 
-        _ => {
-            let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
-            return err!(Unimplemented(msg));
-        }
-    };
+            (Neg, _) if bytes == (1 << (size - 1)) => return err!(OverflowingMath),
+            (Neg, _) => (-(bytes as i128)) as u128,
+        };
 
-    Ok(PrimVal::Bytes(result_bytes))
+        Ok(PrimVal::Bytes(self.truncate(result_bytes, ty)?))
+    }
 }
index de17872e96ff258635dd937c97025c820ef70d90..1b64178477e16f701514f290322fe78e1bbfb57e 100644 (file)
@@ -20,7 +20,7 @@
 use rustc::middle::const_val::ConstVal;
 use rustc::ty::{TyCtxt, self, Instance};
 use rustc::mir::interpret::{Value, PrimVal, GlobalId};
-use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, unary_op, ValTy};
+use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, ValTy};
 use transform::{MirPass, MirSource};
 use syntax::codemap::Span;
 use rustc::ty::subst::Substs;
@@ -205,8 +205,7 @@ fn const_prop(
 
                 let val = self.eval_operand(arg)?;
                 let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?;
-                let kind = ecx.ty_to_primval_kind(val.1).ok()?;
-                match unary_op(op, prim, kind) {
+                match ecx.unary_op(op, prim, val.1) {
                     Ok(val) => Some((Value::ByVal(val), place_ty, span)),
                     Err(mut err) => {
                         ecx.report(&mut err, false, Some(span));
index 012fe0d95ec49c7a554201a1c3c46eba539051d5..5c633eb6112ffdb47264c9ca50c5976137c4cdc1 100644 (file)
@@ -17,11 +17,13 @@ fn main() {
     const I: isize = -9223372036854775808isize;
     assert_eq!(::std::i32::MIN as u64, 0xffffffff80000000);
     assert_eq!(-2147483648isize as u64, 0xffffffff80000000);
+    assert_eq!(-2147483648i32 as u64, 0xffffffff80000000);
     assert_eq!(::std::i64::MIN as u64, 0x8000000000000000);
     #[cfg(target_pointer_width = "64")]
     assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000);
     #[cfg(target_pointer_width = "32")]
     assert_eq!(-9223372036854775808isize as u64, 0);
+    assert_eq!(-9223372036854775808i32 as u64, 0);
     const J: usize = ::std::i32::MAX as usize;
     const K: usize = -1i32 as u32 as usize;
     const L: usize = ::std::i32::MIN as usize;