]> git.lizzy.rs Git - rust.git/commitdiff
Fix sign conversion arithmetic errors
authorvarkor <github@varkor.com>
Wed, 23 May 2018 09:22:45 +0000 (10:22 +0100)
committervarkor <github@varkor.com>
Thu, 16 Aug 2018 19:09:05 +0000 (20:09 +0100)
src/librustc_mir/hair/pattern/_match.rs
src/test/ui/exhaustive_integer_patterns.stderr

index 281dc624a62aabeacecb89f80d9ed3c2b90f80c9..ceb065cc0c8772dce3b46c7e34f2befbb3666ee9 100644 (file)
@@ -471,9 +471,9 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
         }
         ty::TyInt(_) if exhaustive_integer_patterns => {
             let size = cx.tcx.layout_of(ty::ParamEnv::reveal_all().and(pcx.ty))
-                             .unwrap().size.bits() as i128;
-            let min = (1i128 << (size - 1)).wrapping_neg();
-            let max = (1i128 << (size - 1)).wrapping_sub(1);
+                             .unwrap().size.bits() as u128;
+            let min = (1u128 << (size - 1)).wrapping_neg();
+            let max = (1u128 << (size - 1)).wrapping_sub(1);
             value_constructors = true;
             vec![ConstantRange(ty::Const::from_bits(cx.tcx, min as u128, pcx.ty),
                                ty::Const::from_bits(cx.tcx, max as u128, pcx.ty),
@@ -603,13 +603,17 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
 }
 
 /// An inclusive interval, used for precise integer exhaustiveness checking.
+/// `Interval`s always store a contiguous range of integers. That means that
+/// signed integers are offset (see `offset_sign`) by their minimum value.
 struct Interval<'tcx> {
     pub range: RangeInclusive<u128>,
     pub ty: Ty<'tcx>,
 }
 
 impl<'tcx> Interval<'tcx> {
-    fn from_ctor(ctor: &Constructor<'tcx>) -> Option<Interval<'tcx>> {
+    fn from_ctor(tcx: TyCtxt<'_, 'tcx, 'tcx>,
+                 ctor: &Constructor<'tcx>)
+                 -> Option<Interval<'tcx>> {
         match ctor {
             ConstantRange(lo, hi, end) => {
                 assert_eq!(lo.ty, hi.ty);
@@ -618,7 +622,7 @@ fn from_ctor(ctor: &Constructor<'tcx>) -> Option<Interval<'tcx>> {
                     if let Some(hi) = hi.assert_bits(ty) {
                         // Perform a shift if the underlying types are signed,
                         // which makes the interval arithmetic simpler.
-                        let (lo, hi) = Interval::offset_sign(ty, lo..=hi, true);
+                        let (lo, hi) = Self::offset_sign(tcx, ty, lo..=hi, true);
                         // Make sure the interval is well-formed.
                         return if lo > hi || lo == hi && *end == RangeEnd::Excluded {
                             None
@@ -632,7 +636,12 @@ fn from_ctor(ctor: &Constructor<'tcx>) -> Option<Interval<'tcx>> {
             }
             ConstantValue(val) => {
                 let ty = val.ty;
-                val.assert_bits(ty).map(|val| Interval { range: val..=val, ty })
+                if let Some(val) = val.assert_bits(ty) {
+                    let (lo, hi) = Self::offset_sign(tcx, ty, val..=val, true);
+                    Some(Interval { range: lo..=hi, ty })
+                } else {
+                    None
+                }
             }
             Single | Variant(_) | Slice(_) => {
                 None
@@ -640,30 +649,32 @@ fn from_ctor(ctor: &Constructor<'tcx>) -> Option<Interval<'tcx>> {
         }
     }
 
-    fn offset_sign(ty: Ty<'tcx>, range: RangeInclusive<u128>, forwards: bool) -> (u128, u128) {
+    fn offset_sign(tcx: TyCtxt<'_, 'tcx, 'tcx>,
+                   ty: Ty<'tcx>,
+                   range: RangeInclusive<u128>,
+                   encode: bool)
+                   -> (u128, u128) {
+        // We ensure that all integer values are contiguous: that is, that their
+        // minimum value is represented by 0, so that comparisons and increments/
+        // decrements on interval endpoints work consistently whether the endpoints
+        // are signed or unsigned.
         let (lo, hi) = range.into_inner();
-        use syntax::ast::IntTy::*;
         match ty.sty {
-            ty::TyInt(int_ty) => {
-                macro_rules! offset_sign_for_ty {
-                    ($ity:ident, $uty:ty) => {{
-                        let min = Wrapping($ity::MIN as $uty);
-                        if forwards {
-                            ((Wrapping(lo as $uty) + min).0 as u128,
-                             (Wrapping(hi as $uty) + min).0 as u128)
-                        } else {
-                            ((Wrapping(lo as $uty) + min).0 as $ity as u128,
-                             (Wrapping(hi as $uty) + min).0 as $ity as u128)
-                        }
-                    }}
-                }
-                match int_ty {
-                    Isize => offset_sign_for_ty!(isize, usize),
-                    I8    => offset_sign_for_ty!(i8, u8),
-                    I16   => offset_sign_for_ty!(i16, u16),
-                    I32   => offset_sign_for_ty!(i32, u32),
-                    I64   => offset_sign_for_ty!(i64, u64),
-                    I128  => offset_sign_for_ty!(i128, u128),
+            ty::TyInt(_) => {
+                let size = tcx.layout_of(ty::ParamEnv::reveal_all().and(ty))
+                                 .unwrap().size.bits() as u128;
+                let min = (1u128 << (size - 1)).wrapping_neg();
+                let shift = 1u128.overflowing_shl(size as u32);
+                let mask = shift.0.wrapping_sub(1 + (shift.1 as u128));
+                if encode {
+                    let offset = |x: u128| x.wrapping_sub(min) & mask;
+                    (offset(lo), offset(hi))
+                } else {
+                    let offset = |x: u128| {
+                        interpret::sign_extend(tcx, x.wrapping_add(min) & mask, ty)
+                                  .expect("layout error for TyInt")
+                    };
+                    (offset(lo), offset(hi))
                 }
             }
             ty::TyUint(_) | ty::TyChar => {
@@ -685,10 +696,10 @@ fn ranges_subtract_pattern<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
                                      pat_ctor: &Constructor<'tcx>,
                                      ranges: Vec<Constructor<'tcx>>)
                                      -> Vec<Constructor<'tcx>> {
-    if let Some(pat_interval) = Interval::from_ctor(pat_ctor) {
+    if let Some(pat_interval) = Interval::from_ctor(cx.tcx, pat_ctor) {
         let mut remaining_ranges = vec![];
         let mut ranges: Vec<_> = ranges.into_iter().filter_map(|r| {
-            Interval::from_ctor(&r).map(|i| i.into_inner())
+            Interval::from_ctor(cx.tcx, &r).map(|i| i.into_inner())
         }).collect();
         let ty = pat_interval.ty;
         let (pat_interval_lo, pat_interval_hi) = pat_interval.into_inner();
@@ -712,7 +723,7 @@ fn ranges_subtract_pattern<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
         }
         // Convert the remaining ranges from pairs to inclusive `ConstantRange`s.
         remaining_ranges.into_iter().map(|r| {
-            let (lo, hi) = Interval::offset_sign(ty, r, false);
+            let (lo, hi) = Interval::offset_sign(cx.tcx, ty, r, false);
             ConstantRange(ty::Const::from_bits(cx.tcx, lo, ty),
                           ty::Const::from_bits(cx.tcx, hi, ty),
                           RangeEnd::Included)
index 243ba05251a48f7112749aa7127ec31c84d1cecd..3d308a9e5d1e6b1cb0332d6814b04a4fd2c48d35 100644 (file)
@@ -28,11 +28,11 @@ error: unreachable pattern
 LL |         -2..=20 => {} //~ ERROR unreachable pattern
    |         ^^^^^^^
 
-error[E0004]: non-exhaustive patterns: `-128i8...-6i8` and `122i8...127i8` not covered
+error[E0004]: non-exhaustive patterns: `-128i8...-8i8`, `-6i8`, `121i8...124i8` and 1 more not covered
   --> $DIR/exhaustive_integer_patterns.rs:50:11
    |
 LL |     match x { //~ ERROR non-exhaustive patterns
-   |           ^ patterns `-128i8...-6i8` and `122i8...127i8` not covered
+   |           ^ patterns `-128i8...-8i8`, `-6i8`, `121i8...124i8` and 1 more not covered
 
 error[E0004]: non-exhaustive patterns: `-128i8` not covered
   --> $DIR/exhaustive_integer_patterns.rs:99:11