about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2020-03-17 14:11:51 +0100
committerRalf Jung <post@ralfj.de>2020-03-25 15:48:53 +0100
commit5be304b0b411af49e4f0e7196a35c034f430cc85 (patch)
tree975a433572c037afce0d8889feccbdc47644c4f7
parentcdb50c6f2507319f29104a25765bfb79ad53395c (diff)
downloadrust-5be304b0b411af49e4f0e7196a35c034f430cc85.tar.gz
rust-5be304b0b411af49e4f0e7196a35c034f430cc85.zip
miri: simplify shift operator overflow checking
-rw-r--r--src/librustc_mir/interpret/operator.rs25
1 files changed, 13 insertions, 12 deletions
diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs
index 76a5aecb9db..cb0aaa4d40d 100644
--- a/src/librustc_mir/interpret/operator.rs
+++ b/src/librustc_mir/interpret/operator.rs
@@ -1,3 +1,5 @@
+use std::convert::TryFrom;
+
 use rustc::mir;
 use rustc::mir::interpret::{InterpResult, Scalar};
 use rustc::ty::{
@@ -130,28 +132,27 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // Shift ops can have an RHS with a different numeric type.
         if bin_op == Shl || bin_op == Shr {
             let signed = left_layout.abi.is_signed();
-            let mut oflo = (r as u32 as u128) != r;
-            let mut r = r as u32;
-            let size = left_layout.size;
-            oflo |= r >= size.bits() as u32;
-            r %= size.bits() as u32;
+            let size = u128::from(left_layout.size.bits());
+            let overflow = r >= size;
+            let r = r % size; // mask to type size
+            let r = u32::try_from(r).unwrap(); // we masked so this will always fit
             let result = if signed {
                 let l = self.sign_extend(l, left_layout) as i128;
                 let result = match bin_op {
-                    Shl => l << r,
-                    Shr => l >> r,
+                    Shl => l.checked_shl(r).unwrap(),
+                    Shr => l.checked_shr(r).unwrap(),
                     _ => bug!("it has already been checked that this is a shift op"),
                 };
                 result as u128
             } else {
                 match bin_op {
-                    Shl => l << r,
-                    Shr => l >> r,
+                    Shl => l.checked_shl(r).unwrap(),
+                    Shr => l.checked_shr(r).unwrap(),
                     _ => bug!("it has already been checked that this is a shift op"),
                 }
             };
             let truncated = self.truncate(result, left_layout);
-            return Ok((Scalar::from_uint(truncated, size), oflo, left_layout.ty));
+            return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty));
         }
 
         // For the remaining ops, the types must be the same on both sides
@@ -193,7 +194,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 _ => None,
             };
             if let Some(op) = op {
-                let l128 = self.sign_extend(l, left_layout) as i128;
                 let r = self.sign_extend(r, right_layout) as i128;
                 // We need a special check for overflowing remainder:
                 // "int_min % -1" overflows and returns 0, but after casting things to a larger int
@@ -206,8 +206,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     }
                     _ => {}
                 }
+                let l = self.sign_extend(l, left_layout) as i128;
 
-                let (result, oflo) = op(l128, r);
+                let (result, oflo) = op(l, r);
                 // This may be out-of-bounds for the result type, so we have to truncate ourselves.
                 // If that truncation loses any information, we have an overflow.
                 let result = result as u128;