about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorCAD97 <cad97@cad97.com>2020-02-18 13:18:33 -0500
committerCAD97 <cad97@cad97.com>2020-04-08 02:24:16 -0400
commit2fcfd233f755c548ddc4d5fda517a7dbb9f04ba3 (patch)
tree655c31fd84477a56252737868e7c65c965dbaa0f /src
parentb70e7fd0db5d23a2e045e89b8bc7e5564acce9b7 (diff)
downloadrust-2fcfd233f755c548ddc4d5fda517a7dbb9f04ba3.tar.gz
rust-2fcfd233f755c548ddc4d5fda517a7dbb9f04ba3.zip
Redesign the Step trait
Diffstat (limited to 'src')
-rw-r--r--src/libcore/iter/range.rs606
-rw-r--r--src/libcore/tests/iter.rs133
-rw-r--r--src/libcore/tests/lib.rs1
-rw-r--r--src/librustc_index/vec.rs32
-rw-r--r--src/test/ui/impl-trait/example-calendar.rs26
5 files changed, 550 insertions, 248 deletions
diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs
index 28fbd00f36b..ae88fb471a0 100644
--- a/src/libcore/iter/range.rs
+++ b/src/libcore/iter/range.rs
@@ -5,47 +5,179 @@ use crate::usize;
 
 use super::{FusedIterator, TrustedLen};
 
-/// Objects that can be stepped over in both directions.
+/// Objects that have a notion of *successor* and *predecessor* operations.
 ///
-/// The `steps_between` function provides a way to efficiently compare
-/// two `Step` objects.
-#[unstable(
-    feature = "step_trait",
-    reason = "likely to be replaced by finer-grained traits",
-    issue = "42168"
-)]
-pub trait Step: Clone + PartialOrd + Sized {
-    /// Returns the number of steps between two step objects. The count is
-    /// inclusive of `start` and exclusive of `end`.
-    ///
-    /// Returns `None` if it is not possible to calculate `steps_between`
-    /// without overflow.
+/// The *successor* operation moves towards values that compare greater.
+/// The *predecessor* operation moves towards values that compare lesser.
+///
+/// # Safety
+///
+/// This trait is `unsafe` because its implementation must be correct for
+/// the safety of `unsafe trait TrustedLen` implementations, and the results
+/// of using this trait can otherwise be trusted by `unsafe` code to be correct
+/// and fulful the listed obligations.
+#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+pub unsafe trait Step: Clone + PartialOrd + Sized {
+    /// Returns the number of *successor* steps required to get from `start` to `end`.
+    ///
+    /// Returns `None` if the number of steps would overflow `usize`
+    /// (or is infinite, or if `end` would never be reached).
+    ///
+    /// # Invariants
+    ///
+    /// For any `a`, `b`, and `n`:
+    ///
+    /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::forward(&a, n) == Some(b)`
+    /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward(&a, n) == Some(a)`
+    /// * `steps_between(&a, &b) == Some(n)` only if `a <= b`
+    ///   * Corollary: `steps_between(&a, &b) == Some(0)` if and only if `a == b`
+    ///   * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`;
+    ///     this is the case wheen it would require more than `usize::MAX` steps to get to `b`
+    /// * `steps_between(&a, &b) == None` if `a > b`
     fn steps_between(start: &Self, end: &Self) -> Option<usize>;
 
-    /// Replaces this step with `1`, returning a clone of itself.
+    /// Returns the value that would be obtained by taking the *successor*
+    /// of `self` `count` times.
+    ///
+    /// If this would overflow the range of values supported by `Self`, returns `None`.
+    ///
+    /// # Invariants
     ///
-    /// The output of this method should always be greater than the output of replace_zero.
-    fn replace_one(&mut self) -> Self;
+    /// For any `a`, `n`, and `m`:
+    ///
+    /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == n.checked_add(m).and_then(|x| Step::forward_checked(a, x))`
+    /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == try { Step::forward_checked(a, n.checked_add(m)?) }`
+    ///
+    /// For any `a` and `n`:
+    ///
+    /// * `Step::forward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::forward_checked(&x, 1))`
+    ///   * Corollary: `Step::forward_checked(&a, 0) == Some(a)`
+    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
+    fn forward_checked(start: Self, count: usize) -> Option<Self>;
 
-    /// Replaces this step with `0`, returning a clone of itself.
+    /// Returns the value that would be obtained by taking the *successor*
+    /// of `self` `count` times.
+    ///
+    /// If this would overflow the range of values supported by `Self`,
+    /// this function is allowed to panic, wrap, or saturate.
+    /// The suggested behavior is to panic when debug assertions are enabled,
+    /// and to wrap or saturate otherwise.
+    ///
+    /// Unsafe code should not rely on the correctness of behavior after overflow.
+    ///
+    /// # Invariants
+    ///
+    /// For any `a`, `n`, and `m`, where no overflow occurs:
     ///
-    /// The output of this method should always be less than the output of replace_one.
-    fn replace_zero(&mut self) -> Self;
+    /// * `Step::forward(Step::forward(a, n), m) == Step::forward(a, n + m)`
+    ///
+    /// For any `a` and `n`, where no overflow occurs:
+    ///
+    /// * `Step::forward_checked(a, n) == Some(Step::forward(a, n))`
+    /// * `Step::forward(a, n) == (0..n).fold(a, |x, _| Step::forward(x, 1))`
+    ///   * Corollary: `Step::forward(a, 0) == a`
+    /// * `Step::forward(a, n) >= a`
+    /// * `Step::backward(Step::forward(a, n), n) == a`
+    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
+    fn forward(start: Self, count: usize) -> Self {
+        Step::forward_checked(start, count).expect("overflow in `Step::forward`")
+    }
 
-    /// Adds one to this step, returning the result.
-    fn add_one(&self) -> Self;
+    /// Returns the value that would be obtained by taking the *successor*
+    /// of `self` `count` times.
+    ///
+    /// # Safety
+    ///
+    /// It is undefined behavior for this operation to overflow the
+    /// range of values supported by `Self`. If you cannot guarantee that this
+    /// will not overflow, use `forward` or `forward_checked` instead.
+    ///
+    /// # Invariants
+    ///
+    /// For any `a`:
+    ///
+    /// * if there exists `b` such that `b > a`, it is safe to call `Step::forward_unchecked(a, 1)`
+    /// * if there exists `b`, `n` such that `steps_between(&a, &b) == Some(n)`,
+    ///   it is safe to call `Step::forward_unchecked(a, m)` for any `m <= n`.
+    ///
+    /// For any `a` and `n`, where no overflow occurs:
+    ///
+    /// * `Step::forward_unchecked(a, n)` is equivalent to `Step::forward(a, n)`
+    #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")]
+    unsafe fn forward_unchecked(start: Self, count: usize) -> Self {
+        Step::forward(start, count)
+    }
 
-    /// Subtracts one to this step, returning the result.
-    fn sub_one(&self) -> Self;
+    /// Returns the value that would be obtained by taking the *successor*
+    /// of `self` `count` times.
+    ///
+    /// If this would overflow the range of values supported by `Self`, returns `None`.
+    ///
+    /// # Invariants
+    ///
+    /// For any `a`, `n`, and `m`:
+    ///
+    /// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == n.checked_add(m).and_then(|x| Step::backward_checked(a, x))`
+    /// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == try { Step::backward_checked(a, n.checked_add(m)?) }`
+    ///
+    /// For any `a` and `n`:
+    ///
+    /// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(&x, 1))`
+    ///   * Corollary: `Step::backward_checked(&a, 0) == Some(a)`
+    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
+    fn backward_checked(start: Self, count: usize) -> Option<Self>;
 
-    /// Adds a `usize`, returning `None` on overflow.
-    fn add_usize(&self, n: usize) -> Option<Self>;
+    /// Returns the value that would be obtained by taking the *predecessor*
+    /// of `self` `count` times.
+    ///
+    /// If this would overflow the range of values supported by `Self`,
+    /// this function is allowed to panic, wrap, or saturate.
+    /// The suggested behavior is to panic when debug assertions are enabled,
+    /// and to wrap or saturate otherwise.
+    ///
+    /// Unsafe code should not rely on the correctness of behavior after overflow.
+    ///
+    /// # Invariants
+    ///
+    /// For any `a`, `n`, and `m`, where no overflow occurs:
+    ///
+    /// * `Step::backward(Step::backward(a, n), m) == Step::backward(a, n + m)`
+    ///
+    /// For any `a` and `n`, where no overflow occurs:
+    ///
+    /// * `Step::backward_checked(a, n) == Some(Step::backward(a, n))`
+    /// * `Step::backward(a, n) == (0..n).fold(a, |x, _| Step::backward(x, 1))`
+    ///   * Corollary: `Step::backward(a, 0) == a`
+    /// * `Step::backward(a, n) <= a`
+    /// * `Step::forward(Step::backward(a, n), n) == a`
+    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
+    fn backward(start: Self, count: usize) -> Self {
+        Step::backward_checked(start, count).expect("overflow in `Step::backward`")
+    }
 
-    /// 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!()
+    /// Returns the value that would be obtained by taking the *predecessor*
+    /// of `self` `count` times.
+    ///
+    /// # Safety
+    ///
+    /// It is undefined behavior for this operation to overflow the
+    /// range of values supported by `Self`. If you cannot guarantee that this
+    /// will not overflow, use `backward` or `backward_checked` instead.
+    ///
+    /// # Invariants
+    ///
+    /// For any `a`:
+    ///
+    /// * if there exists `b` such that `b < a`, it is safe to call `Step::backward_unchecked(a, 1)`
+    /// * if there exists `b`, `n` such that `steps_between(&b, &a) == Some(n)`,
+    ///   it is safe to call `Step::backward_unchecked(a, m)` for any `m <= n`.
+    ///
+    /// For any `a` and `n`, where no overflow occurs:
+    ///
+    /// * `Step::backward_unchecked(a, n)` is equivalent to `Step::backward(a, n)`
+    #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")]
+    unsafe fn backward_unchecked(start: Self, count: usize) -> Self {
+        Step::backward(start, count)
     }
 }
 
@@ -53,127 +185,243 @@ pub trait Step: Clone + PartialOrd + Sized {
 macro_rules! step_identical_methods {
     () => {
         #[inline]
-        fn replace_one(&mut self) -> Self {
-            mem::replace(self, 1)
+        unsafe fn forward_unchecked(start: Self, n: usize) -> Self {
+            start.unchecked_add(n as Self)
         }
 
         #[inline]
-        fn replace_zero(&mut self) -> Self {
-            mem::replace(self, 0)
+        unsafe fn backward_unchecked(start: Self, n: usize) -> Self {
+            start.unchecked_sub(n as Self)
         }
+    };
+    ( [$u:ident $i:ident] ) => {
+        step_identical_methods!();
 
         #[inline]
-        fn add_one(&self) -> Self {
-            Add::add(*self, 1)
+        fn forward(start: Self, n: usize) -> Self {
+            match Self::forward_checked(start, n) {
+                Some(result) => result,
+                None => {
+                    let result = Add::add(start, n as Self);
+                    // add one modular cycle to ensure overflow occurs
+                    Add::add(Add::add(result as $u, $u::MAX), 1) as Self
+                }
+            }
         }
 
         #[inline]
-        fn sub_one(&self) -> Self {
-            Sub::sub(*self, 1)
+        fn backward(start: Self, n: usize) -> Self {
+            match Self::backward_checked(start, n) {
+                Some(result) => result,
+                None => {
+                    let result = Sub::sub(start, n as Self);
+                    // sub one modular cycle to ensure overflow occurs
+                    Sub::sub(Sub::sub(result as $u, $u::MAX), 1) as Self
+                }
+            }
         }
-    }
+    };
 }
 
-macro_rules! step_impl_unsigned {
-    ($($t:ty)*) => ($(
-        #[unstable(feature = "step_trait",
-                   reason = "likely to be replaced by finer-grained traits",
-                   issue = "42168")]
-        impl Step for $t {
-            #[inline]
-            fn steps_between(start: &$t, end: &$t) -> Option<usize> {
-                if *start < *end {
-                    usize::try_from(*end - *start).ok()
-                } else {
-                    Some(0)
+macro_rules! step_integer_impls {
+    {
+        narrower than or same width as usize:
+            $( [ $u_narrower:ident $i_narrower:ident ] ),+;
+        wider than usize:
+            $( [ $u_wider:ident $i_wider:ident ] ),+;
+    } => {
+        $(
+            #[allow(unreachable_patterns)]
+            #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+            unsafe impl Step for $u_narrower {
+                step_identical_methods!( [ $u_narrower $i_narrower ] );
+
+                #[inline]
+                fn steps_between(start: &Self, end: &Self) -> Option<usize> {
+                    if *start <= *end {
+                        // This relies on $u_narrower <= usize
+                        Some((*end - *start) as usize)
+                    } else {
+                        None
+                    }
                 }
-            }
 
-            #[inline]
-            #[allow(unreachable_patterns)]
-            fn add_usize(&self, n: usize) -> Option<Self> {
-                match <$t>::try_from(n) {
-                    Ok(n_as_t) => self.checked_add(n_as_t),
-                    Err(_) => None,
+                #[inline]
+                fn forward_checked(start: Self, n: usize) -> Option<Self> {
+                    match Self::try_from(n) {
+                        Ok(n) => start.checked_add(n),
+                        Err(_) => None, // if n is out of range, `unsigned_start + n` is too
+                    }
+                }
+
+                #[inline]
+                fn backward_checked(start: Self, n: usize) -> Option<Self> {
+                    match Self::try_from(n) {
+                        Ok(n) => start.checked_sub(n),
+                        Err(_) => None, // if n is out of range, `unsigned_start - n` is too
+                    }
                 }
             }
 
-            #[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,
+            #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+            unsafe impl Step for $i_narrower {
+                step_identical_methods!( [ $u_narrower $i_narrower ] );
+
+                #[inline]
+                fn steps_between(start: &Self, end: &Self) -> Option<usize> {
+                    if *start <= *end {
+                        // This relies on $i_narrower <= usize
+                        //
+                        // Casting to isize extends the width but preserves the sign.
+                        // Use wrapping_sub in isize space and cast to usize to compute
+                        // the difference that may not fit inside the range of isize.
+                        Some((*end as isize).wrapping_sub(*start as isize) as usize)
+                    } else {
+                        None
+                    }
                 }
-            }
 
-            step_identical_methods!();
-        }
-    )*)
-}
-macro_rules! step_impl_signed {
-    ($( [$t:ty : $unsigned:ty] )*) => ($(
-        #[unstable(feature = "step_trait",
-                   reason = "likely to be replaced by finer-grained traits",
-                   issue = "42168")]
-        impl Step for $t {
-            #[inline]
-            fn steps_between(start: &$t, end: &$t) -> Option<usize> {
-                if *start < *end {
-                    // Use .wrapping_sub and cast to unsigned to compute the
-                    // difference that may not fit inside the range of $t.
-                    usize::try_from(end.wrapping_sub(*start) as $unsigned).ok()
-                } else {
-                    Some(0)
+                #[inline]
+                fn forward_checked(start: Self, n: usize) -> Option<Self> {
+                    match $u_narrower::try_from(n) {
+                        Ok(n) => {
+                            // Wrapping handles cases like
+                            // `Step::forward(-120_i8, 200) == Some(80_i8)`,
+                            // even though 200 is out of range for i8.
+                            let wrapped = start.wrapping_add(n as Self);
+                            if wrapped >= start {
+                                Some(wrapped)
+                            } else {
+                                None // Addition overflowed
+                            }
+                        }
+                        // If n is out of range of e.g. u8,
+                        // then it is bigger than the entire range for i8 is wide
+                        // so `any_i8 + n` necessarily overflows i8.
+                        Err(_) => None,
+                    }
+                }
+
+                #[inline]
+                fn backward_checked(start: Self, n: usize) -> Option<Self> {
+                    match $u_narrower::try_from(n) {
+                        Ok(n) => {
+                            // Wrapping handles cases like
+                            // `Step::forward(-120_i8, 200) == Some(80_i8)`,
+                            // even though 200 is out of range for i8.
+                            let wrapped = start.wrapping_sub(n as Self);
+                            if wrapped <= start {
+                                Some(wrapped)
+                            } else {
+                                None // Subtraction overflowed
+                            }
+                        }
+                        // If n is out of range of e.g. u8,
+                        // then it is bigger than the entire range for i8 is wide
+                        // so `any_i8 - n` necessarily overflows i8.
+                        Err(_) => None,
+                    }
                 }
             }
+        )+
 
-            #[inline]
+        $(
             #[allow(unreachable_patterns)]
-            fn add_usize(&self, n: usize) -> Option<Self> {
-                match <$unsigned>::try_from(n) {
-                    Ok(n_as_unsigned) => {
-                        // Wrapping in unsigned space handles cases like
-                        // `-120_i8.add_usize(200) == Some(80_i8)`,
-                        // even though 200_usize is out of range for i8.
-                        let wrapped = (*self as $unsigned).wrapping_add(n_as_unsigned) as $t;
-                        if wrapped >= *self {
-                            Some(wrapped)
-                        } else {
-                            None  // Addition overflowed
-                        }
+            #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+            unsafe impl Step for $u_wider {
+                step_identical_methods!();
+
+                #[inline]
+                fn steps_between(start: &Self, end: &Self) -> Option<usize> {
+                    if *start <= *end {
+                        usize::try_from(*end - *start).ok()
+                    } else {
+                        None
                     }
-                    Err(_) => None,
+                }
+
+                #[inline]
+                fn forward_checked(start: Self, n: usize) -> Option<Self> {
+                    start.checked_add(n as Self)
+                }
+
+                #[inline]
+                fn forward(start: Self, n: usize) -> Self {
+                    Add::add(start, n as Self)
+                }
+
+                #[inline]
+                fn backward_checked(start: Self, n: usize) -> Option<Self> {
+                    start.checked_sub(n as Self)
+                }
+
+                #[inline]
+                fn backward(start: Self, n: usize) -> Self {
+                    Sub::sub(start, n as Self)
                 }
             }
 
-            #[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
+            #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+            unsafe impl Step for $i_wider {
+                step_identical_methods!();
+
+                #[inline]
+                fn steps_between(start: &Self, end: &Self) -> Option<usize> {
+                    if *start <= *end {
+                        match end.checked_sub(*start) {
+                            Some(result) => usize::try_from(result).ok(),
+                            // If the difference is too big for e.g. i128,
+                            // it's also gonna be too big for usize with fewer bits.
+                            None => None,
                         }
+                    } else {
+                        None
                     }
-                    Err(_) => None,
+                }
+
+                #[inline]
+                fn forward_checked(start: Self, n: usize) -> Option<Self> {
+                    start.checked_add(n as Self)
+                }
+
+                #[inline]
+                fn forward(start: Self, n: usize) -> Self {
+                    Add::add(start, n as Self)
+                }
+
+                #[inline]
+                fn backward_checked(start: Self, n: usize) -> Option<Self> {
+                    start.checked_sub(n as Self)
+                }
+
+                #[inline]
+                fn backward(start: Self, n: usize) -> Self {
+                    Sub::sub(start, n as Self)
                 }
             }
+        )+
+    };
+}
 
-            step_identical_methods!();
-        }
-    )*)
+#[cfg(target_pointer_width = "64")]
+step_integer_impls! {
+    narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize];
+    wider than usize: [u128 i128];
 }
 
-step_impl_unsigned!(usize u8 u16 u32 u64 u128);
-step_impl_signed!([isize: usize][i8: u8][i16: u16]);
-step_impl_signed!([i32: u32][i64: u64][i128: u128]);
+#[cfg(target_pointer_width = "32")]
+step_integer_impls! {
+    narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [usize isize];
+    wider than usize: [u64 i64], [u128 i128];
+}
+
+#[cfg(target_pointer_width = "16")]
+step_integer_impls! {
+    narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize];
+    wider than usize: [u32 i32], [u64 i64], [u128 i128];
+}
 
 macro_rules! range_exact_iter_impl {
     ($($t:ty)*) => ($(
@@ -189,20 +437,6 @@ macro_rules! range_incl_exact_iter_impl {
     )*)
 }
 
-macro_rules! range_trusted_len_impl {
-    ($($t:ty)*) => ($(
-        #[unstable(feature = "trusted_len", issue = "37572")]
-        unsafe impl TrustedLen for ops::Range<$t> { }
-    )*)
-}
-
-macro_rules! range_incl_trusted_len_impl {
-    ($($t:ty)*) => ($(
-        #[unstable(feature = "trusted_len", issue = "37572")]
-        unsafe impl TrustedLen for ops::RangeInclusive<$t> { }
-    )*)
-}
-
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<A: Step> Iterator for ops::Range<A> {
     type Item = A;
@@ -210,16 +444,12 @@ impl<A: Step> Iterator for ops::Range<A> {
     #[inline]
     fn next(&mut self) -> Option<A> {
         if self.start < self.end {
-            // We check for overflow here, even though it can't actually
-            // happen. Adding this check does however help llvm vectorize loops
-            // for some ranges that don't get vectorized otherwise,
-            // and this won't actually result in an extra check in an optimized build.
-            if let Some(mut n) = self.start.add_usize(1) {
-                mem::swap(&mut n, &mut self.start);
-                Some(n)
-            } else {
-                None
-            }
+            // SAFETY: just checked precondition
+            // We use the unchecked version here, because
+            // this helps LLVM vectorize loops for some ranges
+            // that don't get vectorized otherwise.
+            let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) };
+            Some(mem::replace(&mut self.start, n))
         } else {
             None
         }
@@ -227,17 +457,19 @@ impl<A: Step> Iterator for ops::Range<A> {
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        match Step::steps_between(&self.start, &self.end) {
-            Some(hint) => (hint, Some(hint)),
-            None => (usize::MAX, None),
+        if self.start < self.end {
+            let hint = Step::steps_between(&self.start, &self.end);
+            (hint.unwrap_or(usize::MAX), hint)
+        } else {
+            (0, Some(0))
         }
     }
 
     #[inline]
     fn nth(&mut self, n: usize) -> Option<A> {
-        if let Some(plus_n) = self.start.add_usize(n) {
+        if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) {
             if plus_n < self.end {
-                self.start = plus_n.add_one();
+                self.start = Step::forward(plus_n.clone(), 1);
                 return Some(plus_n);
             }
         }
@@ -263,25 +495,42 @@ impl<A: Step> Iterator for ops::Range<A> {
 }
 
 // These macros generate `ExactSizeIterator` impls for various range types.
-// Range<{u,i}64> and RangeInclusive<{u,i}{32,64,size}> are excluded
-// because they cannot guarantee having a length <= usize::MAX, which is
-// required by ExactSizeIterator.
-range_exact_iter_impl!(usize u8 u16 u32 isize i8 i16 i32);
-range_incl_exact_iter_impl!(u8 u16 i8 i16);
-
-// These macros generate `TrustedLen` impls.
 //
-// They need to guarantee that .size_hint() is either exact, or that
-// the upper bound is None when it does not fit the type limits.
-range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128);
-range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128);
+// * `ExactSizeIterator::len` is required to always return an exact `usize`,
+//   so no range can be longer than `usize::MAX`.
+// * For integer types in `Range<_>` this is the case for types narrower than or as wide as `usize`.
+//   For integer types in `RangeInclusive<_>`
+//   this is the case for types *strictly narrower* than `usize`
+//   since e.g. `(0..=u64::MAX).len()` would be `u64::MAX + 1`.
+range_exact_iter_impl! {
+    usize u8 u16
+    isize i8 i16
+
+    // These are incorect per the reasoning above,
+    // but removing them would be a breaking change as they were stabilized in Rust 1.0.0.
+    // So e.g. `(0..66_000_u32).len()` for example will compile without error or warnings
+    // on 16-bit platforms, but continue to give a wrong result.
+    u32
+    i32
+}
+range_incl_exact_iter_impl! {
+    u8
+    i8
+
+    // These are incorect per the reasoning above,
+    // but removing them would be a breaking change as they were stabilized in Rust 1.26.0.
+    // So e.g. `(0..=u16::MAX).len()` for example will compile without error or warnings
+    // on 16-bit platforms, but continue to give a wrong result.
+    u16
+    i16
+}
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<A: Step> DoubleEndedIterator for ops::Range<A> {
     #[inline]
     fn next_back(&mut self) -> Option<A> {
         if self.start < self.end {
-            self.end = self.end.sub_one();
+            self.end = Step::backward(self.end.clone(), 1);
             Some(self.end.clone())
         } else {
             None
@@ -290,9 +539,9 @@ impl<A: Step> DoubleEndedIterator for ops::Range<A> {
 
     #[inline]
     fn nth_back(&mut self, n: usize) -> Option<A> {
-        if let Some(minus_n) = self.end.sub_usize(n) {
+        if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) {
             if minus_n > self.start {
-                self.end = minus_n.sub_one();
+                self.end = Step::backward(minus_n, 1);
                 return Some(self.end.clone());
             }
         }
@@ -302,6 +551,9 @@ impl<A: Step> DoubleEndedIterator for ops::Range<A> {
     }
 }
 
+#[unstable(feature = "trusted_len", issue = "37572")]
+unsafe impl<A: Step> TrustedLen for ops::Range<A> {}
+
 #[stable(feature = "fused", since = "1.26.0")]
 impl<A: Step> FusedIterator for ops::Range<A> {}
 
@@ -311,9 +563,8 @@ impl<A: Step> Iterator for ops::RangeFrom<A> {
 
     #[inline]
     fn next(&mut self) -> Option<A> {
-        let mut n = self.start.add_one();
-        mem::swap(&mut n, &mut self.start);
-        Some(n)
+        let n = Step::forward(self.start.clone(), 1);
+        Some(mem::replace(&mut self.start, n))
     }
 
     #[inline]
@@ -323,8 +574,16 @@ impl<A: Step> Iterator for ops::RangeFrom<A> {
 
     #[inline]
     fn nth(&mut self, n: usize) -> Option<A> {
-        let plus_n = self.start.add_usize(n).expect("overflow in RangeFrom::nth");
-        self.start = plus_n.add_one();
+        // If we would jump over the maximum value, panic immediately.
+        // This is consistent with behavior before the Step redesign,
+        // even though it's inconsistent with n `next` calls.
+        // To get consistent behavior, change it to use `forward` instead.
+        // This change should go through FCP separately to the redesign, so is for now left as a
+        // FIXME: make this consistent
+        let plus_n =
+            Step::forward_checked(self.start.clone(), n).expect("overflow in RangeFrom::nth");
+        // The final step should always be debug-checked.
+        self.start = Step::forward(plus_n.clone(), 1);
         Some(plus_n)
     }
 }
@@ -346,7 +605,7 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
         }
         let is_iterating = self.start < self.end;
         Some(if is_iterating {
-            let n = self.start.add_one();
+            let n = Step::forward(self.start.clone(), 1);
             mem::replace(&mut self.start, n)
         } else {
             self.exhausted = true;
@@ -372,12 +631,12 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
             return None;
         }
 
-        if let Some(plus_n) = self.start.add_usize(n) {
+        if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) {
             use crate::cmp::Ordering::*;
 
             match plus_n.partial_cmp(&self.end) {
                 Some(Less) => {
-                    self.start = plus_n.add_one();
+                    self.start = Step::forward(plus_n.clone(), 1);
                     return Some(plus_n);
                 }
                 Some(Equal) => {
@@ -408,7 +667,7 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
         let mut accum = init;
 
         while self.start < self.end {
-            let n = self.start.add_one();
+            let n = Step::forward(self.start.clone(), 1);
             let n = mem::replace(&mut self.start, n);
             accum = f(accum, n)?;
         }
@@ -447,7 +706,7 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
         }
         let is_iterating = self.start < self.end;
         Some(if is_iterating {
-            let n = self.end.sub_one();
+            let n = Step::backward(self.end.clone(), 1);
             mem::replace(&mut self.end, n)
         } else {
             self.exhausted = true;
@@ -461,12 +720,12 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
             return None;
         }
 
-        if let Some(minus_n) = self.end.sub_usize(n) {
+        if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) {
             use crate::cmp::Ordering::*;
 
             match minus_n.partial_cmp(&self.start) {
                 Some(Greater) => {
-                    self.end = minus_n.sub_one();
+                    self.end = Step::backward(minus_n.clone(), 1);
                     return Some(minus_n);
                 }
                 Some(Equal) => {
@@ -497,7 +756,7 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
         let mut accum = init;
 
         while self.start < self.end {
-            let n = self.end.sub_one();
+            let n = Step::backward(self.end.clone(), 1);
             let n = mem::replace(&mut self.end, n);
             accum = f(accum, n)?;
         }
@@ -512,5 +771,8 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
     }
 }
 
+#[unstable(feature = "trusted_len", issue = "37572")]
+unsafe impl<A: Step> TrustedLen for ops::RangeInclusive<A> {}
+
 #[stable(feature = "fused", since = "1.26.0")]
 impl<A: Step> FusedIterator for ops::RangeInclusive<A> {}
diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs
index 1a1dbcd7b87..13c05dadbde 100644
--- a/src/libcore/tests/iter.rs
+++ b/src/libcore/tests/iter.rs
@@ -228,7 +228,11 @@ fn test_iterator_chain_size_hint() {
         }
 
         fn size_hint(&self) -> (usize, Option<usize>) {
-            if self.is_empty { (0, Some(0)) } else { (1, Some(1)) }
+            if self.is_empty {
+                (0, Some(0))
+            } else {
+                (1, Some(1))
+            }
         }
     }
 
@@ -1554,7 +1558,11 @@ fn test_find_map() {
     assert_eq!(iter.next(), Some(&7));
 
     fn half_if_even(x: &isize) -> Option<isize> {
-        if x % 2 == 0 { Some(x / 2) } else { None }
+        if x % 2 == 0 {
+            Some(x / 2)
+        } else {
+            None
+        }
     }
 }
 
@@ -2126,6 +2134,24 @@ fn test_range_inclusive_nth_back() {
 }
 
 #[test]
+fn test_range_len() {
+    assert_eq!((0..10_u8).len(), 10);
+    assert_eq!((9..10_u8).len(), 1);
+    assert_eq!((10..10_u8).len(), 0);
+    assert_eq!((11..10_u8).len(), 0);
+    assert_eq!((100..10_u8).len(), 0);
+}
+
+#[test]
+fn test_range_inclusive_len() {
+    assert_eq!((0..=10_u8).len(), 11);
+    assert_eq!((9..=10_u8).len(), 2);
+    assert_eq!((10..=10_u8).len(), 1);
+    assert_eq!((11..=10_u8).len(), 0);
+    assert_eq!((100..=10_u8).len(), 0);
+}
+
+#[test]
 fn test_range_step() {
     #![allow(deprecated)]
 
@@ -2495,42 +2521,91 @@ fn test_chain_fold() {
 }
 
 #[test]
-fn test_step_replace_unsigned() {
-    let mut x = 4u32;
-    let y = x.replace_zero();
-    assert_eq!(x, 0);
-    assert_eq!(y, 4);
+fn test_steps_between() {
+    assert_eq!(Step::steps_between(&20_u8, &200_u8), Some(180_usize));
+    assert_eq!(Step::steps_between(&-20_i8, &80_i8), Some(100_usize));
+    assert_eq!(Step::steps_between(&-120_i8, &80_i8), Some(200_usize));
+    assert_eq!(Step::steps_between(&20_u32, &4_000_100_u32), Some(4_000_080_usize));
+    assert_eq!(Step::steps_between(&-20_i32, &80_i32), Some(100_usize));
+    assert_eq!(Step::steps_between(&-2_000_030_i32, &2_000_050_i32), Some(4_000_080_usize));
+
+    // Skip u64/i64 to avoid differences with 32-bit vs 64-bit platforms
 
-    x = 5;
-    let y = x.replace_one();
-    assert_eq!(x, 1);
-    assert_eq!(y, 5);
+    assert_eq!(Step::steps_between(&20_u128, &200_u128), Some(180_usize));
+    assert_eq!(Step::steps_between(&-20_i128, &80_i128), Some(100_usize));
+    if cfg!(target_pointer_width = "64") {
+        assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_0009_u128), Some(usize::MAX));
+    }
+    assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_000a_u128), None);
+    assert_eq!(Step::steps_between(&10_i128, &0x1_0000_0000_0000_000a_i128), None);
+    assert_eq!(
+        Step::steps_between(&-0x1_0000_0000_0000_0000_i128, &0x1_0000_0000_0000_0000_i128,),
+        None,
+    );
 }
 
 #[test]
-fn test_step_replace_signed() {
-    let mut x = 4i32;
-    let y = x.replace_zero();
-    assert_eq!(x, 0);
-    assert_eq!(y, 4);
+fn test_step_forward() {
+    assert_eq!(Step::forward_checked(55_u8, 200_usize), Some(255_u8));
+    assert_eq!(Step::forward_checked(252_u8, 200_usize), None);
+    assert_eq!(Step::forward_checked(0_u8, 256_usize), None);
+    assert_eq!(Step::forward_checked(-110_i8, 200_usize), Some(90_i8));
+    assert_eq!(Step::forward_checked(-110_i8, 248_usize), None);
+    assert_eq!(Step::forward_checked(-126_i8, 256_usize), None);
 
-    x = 5;
-    let y = x.replace_one();
-    assert_eq!(x, 1);
-    assert_eq!(y, 5);
+    assert_eq!(Step::forward_checked(35_u16, 100_usize), Some(135_u16));
+    assert_eq!(Step::forward_checked(35_u16, 65500_usize), Some(u16::MAX));
+    assert_eq!(Step::forward_checked(36_u16, 65500_usize), None);
+    assert_eq!(Step::forward_checked(-110_i16, 200_usize), Some(90_i16));
+    assert_eq!(Step::forward_checked(-20_030_i16, 50_050_usize), Some(30_020_i16));
+    assert_eq!(Step::forward_checked(-10_i16, 40_000_usize), None);
+    assert_eq!(Step::forward_checked(-10_i16, 70_000_usize), None);
+
+    assert_eq!(Step::forward_checked(10_u128, 70_000_usize), Some(70_010_u128));
+    assert_eq!(Step::forward_checked(10_i128, 70_030_usize), Some(70_040_i128));
+    assert_eq!(
+        Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0xff_usize),
+        Some(u128::MAX),
+    );
+    assert_eq!(
+        Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0x100_usize),
+        None
+    );
+    assert_eq!(
+        Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0xff_usize),
+        Some(i128::MAX),
+    );
+    assert_eq!(
+        Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize),
+        None
+    );
 }
 
 #[test]
-fn test_step_replace_no_between() {
-    let mut x = 4u128;
-    let y = x.replace_zero();
-    assert_eq!(x, 0);
-    assert_eq!(y, 4);
+fn test_step_backward() {
+    assert_eq!(Step::backward_checked(255_u8, 200_usize), Some(55_u8));
+    assert_eq!(Step::backward_checked(100_u8, 200_usize), None);
+    assert_eq!(Step::backward_checked(255_u8, 256_usize), None);
+    assert_eq!(Step::backward_checked(90_i8, 200_usize), Some(-110_i8));
+    assert_eq!(Step::backward_checked(110_i8, 248_usize), None);
+    assert_eq!(Step::backward_checked(127_i8, 256_usize), None);
 
-    x = 5;
-    let y = x.replace_one();
-    assert_eq!(x, 1);
-    assert_eq!(y, 5);
+    assert_eq!(Step::backward_checked(135_u16, 100_usize), Some(35_u16));
+    assert_eq!(Step::backward_checked(u16::MAX, 65500_usize), Some(35_u16));
+    assert_eq!(Step::backward_checked(10_u16, 11_usize), None);
+    assert_eq!(Step::backward_checked(90_i16, 200_usize), Some(-110_i16));
+    assert_eq!(Step::backward_checked(30_020_i16, 50_050_usize), Some(-20_030_i16));
+    assert_eq!(Step::backward_checked(-10_i16, 40_000_usize), None);
+    assert_eq!(Step::backward_checked(-10_i16, 70_000_usize), None);
+
+    assert_eq!(Step::backward_checked(70_010_u128, 70_000_usize), Some(10_u128));
+    assert_eq!(Step::backward_checked(70_020_i128, 70_030_usize), Some(-10_i128));
+    assert_eq!(Step::backward_checked(10_u128, 7_usize), Some(3_u128));
+    assert_eq!(Step::backward_checked(10_u128, 11_usize), None);
+    assert_eq!(
+        Step::backward_checked(-0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize),
+        Some(i128::MIN)
+    );
 }
 
 #[test]
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs
index 05f958cbe81..3d6abb818e4 100644
--- a/src/libcore/tests/lib.rs
+++ b/src/libcore/tests/lib.rs
@@ -22,6 +22,7 @@
 #![feature(slice_partition_at_index)]
 #![feature(specialization)]
 #![feature(step_trait)]
+#![feature(step_trait_ext)]
 #![feature(str_internals)]
 #![feature(test)]
 #![feature(trusted_len)]
diff --git a/src/librustc_index/vec.rs b/src/librustc_index/vec.rs
index a84f89c7cd9..67dcea58cf8 100644
--- a/src/librustc_index/vec.rs
+++ b/src/librustc_index/vec.rs
@@ -65,7 +65,7 @@ impl Idx for u32 {
 /// `u32::MAX`. You can also customize things like the `Debug` impl,
 /// what traits are derived, and so forth via the macro.
 #[macro_export]
-#[allow_internal_unstable(step_trait, rustc_attrs)]
+#[allow_internal_unstable(step_trait, step_trait_ext, rustc_attrs)]
 macro_rules! newtype_index {
     // ---- public rules ----
 
@@ -181,7 +181,7 @@ macro_rules! newtype_index {
             }
         }
 
-        impl ::std::iter::Step for $type {
+        unsafe impl ::std::iter::Step for $type {
             #[inline]
             fn steps_between(start: &Self, end: &Self) -> Option<usize> {
                 <usize as ::std::iter::Step>::steps_between(
@@ -191,33 +191,13 @@ macro_rules! newtype_index {
             }
 
             #[inline]
-            fn replace_one(&mut self) -> Self {
-                ::std::mem::replace(self, Self::from_u32(1))
+            fn forward_checked(start: Self, u: usize) -> Option<Self> {
+                Self::index(start).checked_add(u).map(Self::from_usize)
             }
 
             #[inline]
-            fn replace_zero(&mut self) -> Self {
-                ::std::mem::replace(self, Self::from_u32(0))
-            }
-
-            #[inline]
-            fn add_one(&self) -> Self {
-                Self::from_usize(Self::index(*self) + 1)
-            }
-
-            #[inline]
-            fn sub_one(&self) -> Self {
-                Self::from_usize(Self::index(*self) - 1)
-            }
-
-            #[inline]
-            fn add_usize(&self, u: usize) -> Option<Self> {
-                Self::index(*self).checked_add(u).map(Self::from_usize)
-            }
-
-            #[inline]
-            fn sub_usize(&self, u: usize) -> Option<Self> {
-                Self::index(*self).checked_sub(u).map(Self::from_usize)
+            fn backward_checked(start: Self, u: usize) -> Option<Self> {
+                Self::index(start).checked_sub(u).map(Self::from_usize)
             }
         }
 
diff --git a/src/test/ui/impl-trait/example-calendar.rs b/src/test/ui/impl-trait/example-calendar.rs
index f1b1656745e..fafab8a102a 100644
--- a/src/test/ui/impl-trait/example-calendar.rs
+++ b/src/test/ui/impl-trait/example-calendar.rs
@@ -2,6 +2,7 @@
 
 #![feature(fn_traits,
            step_trait,
+           step_trait_ext,
            unboxed_closures,
 )]
 
@@ -10,7 +11,6 @@
 //! Originally converted to Rust by [Daniel Keep](https://github.com/DanielKeep).
 
 use std::fmt::Write;
-use std::mem;
 
 /// Date representation.
 #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
@@ -156,32 +156,16 @@ impl<'a, 'b> std::ops::Add<&'b NaiveDate> for &'a NaiveDate {
     }
 }
 
-impl std::iter::Step for NaiveDate {
+unsafe impl std::iter::Step for NaiveDate {
     fn steps_between(_: &Self, _: &Self) -> Option<usize> {
         unimplemented!()
     }
 
-    fn replace_one(&mut self) -> Self {
-        mem::replace(self, NaiveDate(0, 0, 1))
+    fn forward_checked(start: Self, n: usize) -> Option<Self> {
+        Some((0..n).fold(start, |x, _| x.succ()))
     }
 
-    fn replace_zero(&mut self) -> Self {
-        mem::replace(self, NaiveDate(0, 0, 0))
-    }
-
-    fn add_one(&self) -> Self {
-        self.succ()
-    }
-
-    fn sub_one(&self) -> Self {
-        unimplemented!()
-    }
-
-    fn add_usize(&self, _: usize) -> Option<Self> {
-        unimplemented!()
-    }
-
-    fn sub_usize(&self, _: usize) -> Option<Self> {
+    fn backward_checked(_: Self, _: usize) -> Option<Self> {
         unimplemented!()
     }
 }