about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTim Vermeulen <tvermeulen@me.com>2019-05-04 12:04:52 +0200
committerTim Vermeulen <tvermeulen@me.com>2019-05-25 02:53:08 +0200
commitf1d0829e20e3ff3ff78a09136968612887544af2 (patch)
treec64d009d967eb8f6ab530d07d4836f5e3382bd7f
parentdec4c5201f88efbc3020b04ba96a5ee2c3b6cfcd (diff)
downloadrust-f1d0829e20e3ff3ff78a09136968612887544af2.tar.gz
rust-f1d0829e20e3ff3ff78a09136968612887544af2.zip
Add Step::sub_usize
-rw-r--r--src/libcore/iter/range.rs35
-rw-r--r--src/librustc_data_structures/indexed_vec.rs5
-rw-r--r--src/test/run-pass/impl-trait/example-calendar.rs4
3 files changed, 44 insertions, 0 deletions
diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs
index f8a975cc8d4..6bbf776fb8f 100644
--- a/src/libcore/iter/range.rs
+++ b/src/libcore/iter/range.rs
@@ -34,6 +34,13 @@ pub trait Step: Clone + PartialOrd + Sized {
 
     /// Adds a `usize`, returning `None` on overflow.
     fn add_usize(&self, n: usize) -> Option<Self>;
+
+    /// Subtracts a `usize`, returning `None` on underflow.
+    fn sub_usize(&self, n: usize) -> Option<Self> {
+        // this default implementation makes the addition of `sub_usize` a non-breaking change
+        let _ = n;
+        unimplemented!()
+    }
 }
 
 // These are still macro-generated because the integer literals resolve to different types.
@@ -85,6 +92,15 @@ macro_rules! step_impl_unsigned {
                 }
             }
 
+            #[inline]
+            #[allow(unreachable_patterns)]
+            fn sub_usize(&self, n: usize) -> Option<Self> {
+                match <$t>::try_from(n) {
+                    Ok(n_as_t) => self.checked_sub(n_as_t),
+                    Err(_) => None,
+                }
+            }
+
             step_identical_methods!();
         }
     )*)
@@ -125,6 +141,25 @@ macro_rules! step_impl_signed {
                 }
             }
 
+            #[inline]
+            #[allow(unreachable_patterns)]
+            fn sub_usize(&self, n: usize) -> Option<Self> {
+                match <$unsigned>::try_from(n) {
+                    Ok(n_as_unsigned) => {
+                        // Wrapping in unsigned space handles cases like
+                        // `80_i8.sub_usize(200) == Some(-120_i8)`,
+                        // even though 200_usize is out of range for i8.
+                        let wrapped = (*self as $unsigned).wrapping_sub(n_as_unsigned) as $t;
+                        if wrapped <= *self {
+                            Some(wrapped)
+                        } else {
+                            None  // Subtraction underflowed
+                        }
+                    }
+                    Err(_) => None,
+                }
+            }
+
             step_identical_methods!();
         }
     )*)
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
index 359b89f683d..c7f6e54c3d5 100644
--- a/src/librustc_data_structures/indexed_vec.rs
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -212,6 +212,11 @@ macro_rules! newtype_index {
             fn add_usize(&self, u: usize) -> Option<Self> {
                 Idx::index(*self).checked_add(u).map(Self::new)
             }
+
+            #[inline]
+            fn sub_usize(&self, u: usize) -> Option<Self> {
+                Idx::index(*self).checked_sub(u).map(Self::new)
+            }
         }
 
         impl From<$type> for u32 {
diff --git a/src/test/run-pass/impl-trait/example-calendar.rs b/src/test/run-pass/impl-trait/example-calendar.rs
index 968e9b7d34a..f1b1656745e 100644
--- a/src/test/run-pass/impl-trait/example-calendar.rs
+++ b/src/test/run-pass/impl-trait/example-calendar.rs
@@ -180,6 +180,10 @@ impl std::iter::Step for NaiveDate {
     fn add_usize(&self, _: usize) -> Option<Self> {
         unimplemented!()
     }
+
+    fn sub_usize(&self, _: usize) -> Option<Self> {
+        unimplemented!()
+    }
 }
 
 #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]