Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign the std::iter::Step trait #68807

Closed
wants to merge 2 commits into from
Closed

Conversation

CAD97
Copy link
Contributor

@CAD97 CAD97 commented Feb 3, 2020

CC #42168, @SimonSapin, r? @scottmcm. Revival of #43127, re-opened #62886 (with manual rebase and no longer a PR from my master).

This is still blocked on the use of published chalk 0.9.0, unfortunately. cc @rust-lang/wg-traits: is there some way we could get rustc updated to use a version of chalk that no longer implements Step? Should I create a minimal patch for published chalk 0.9.0 that uses the new Step trait? Cherry pick the commit that stops using Step?

The new Step trait is defined as follows:

/// Objects that have a notion of *successor* and *predecessor*.
///
/// 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.
#[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 `a.forward(n) == Some(b)`
    /// * `steps_between(&a, &b) == Some(n)` if and only if `b.backward(n) == Some(a)`
    /// * `steps_between(&a, &b) == Some(n)` only if `a <= b`
    ///   * Corrolary: `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 when 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>;

    /// Returns the value that would be obtained by taking the *successor*
    /// of `self` `count` times.
    ///
    /// Return s`None` if this would overflow the range of values supported by `Self`.
    ///
    /// # Invariants
    ///
    /// For any `a`, `n`, and `m` where `n + m` does not overflow:
    ///
    /// * `a.forward(n).and_then(|x| x.forward(m)) == a.forward(n + m)`
    /// * `a.forward(n)` equals `Step::successor` applied to `a` `n` times
    ///   * Corollary: `a.forward(0) == Some(a)`
    fn forward(&self, count: usize) -> Option<Self>;

    /// Returns the *successor* of `self`.
    ///
    /// If this would overflow the range of values supported by `Self`,
    /// this method is allowed to panic or wrap. Suggested behavior is
    /// to panic when debug assertions are enabled, and wrap otherwise.
    ///
    /// # Invariants
    ///
    /// For any `a` where `a.successor()` does not overflow:
    ///
    /// * `a == a.successor().predecessor()`
    /// * `a.successor() == a.forward(1).unwrap()`
    /// * `a.successor() >= a`
    #[inline]
    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
    fn successor(&self) -> Self {
        self.forward(1).expect("overflow in `Step::successor`")
    }

    /// Returns the *successor* of `self`.
    ///
    /// If this would overflow the range of values supported by `Self`,
    /// this method is defined to return the input value instead.
    ///
    /// # Invaraints
    ///
    /// For any `a` where `a.successor()` does not overflow:
    ///
    /// * `a.successor_saturating() == a.successor()`
    ///
    /// For any `a` where `a.successor()` does overflow:
    ///
    /// * `a.successor_saturating() == a`
    #[inline]
    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
    fn successor_saturating(&self) -> Self {
        self.forward(1).unwrap_or_else(|| self.clone())
    }

    /// Returns the *successor* of `self`.
    ///
    /// # Safety
    ///
    /// It is undefined behavior if this operation exceeds the range of
    /// values supported by `Self`. If you cannot guarantee that this
    /// will not overflow, use `forward` or `successor` instead.
    ///
    /// For any `a`, if there exists `b` such that `b > a`,
    /// it is safe to call `a.successor_unchecked()`.
    #[inline]
    #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")]
    unsafe fn successor_unchecked(&self) -> Self {
        self.successor()
    }

    /// Returns the value that would be obtained by taking the *predecessor*
    /// of `self` `count` times.
    ///
    /// Returns `None` if this would overflow the range of values supported by `Self`.
    ///
    /// # Invariants
    ///
    /// For any `a`, `n`, and `m` where `n + m` does not overflow:
    ///
    /// * `a.backward(n).and_then(|x| x.backward(m)) == a.backward (n + m)`
    /// * `a.backward(n)` equals `Step::predecessor` applied to `a` `n` times
    ///   * Corollary: `a.backward(0) == Some(a)`
    /// * `a.backward(n).unwrap() <= a`
    fn backward(&self, count: usize) -> Option<Self>;

    /// Returns the *predecessor* of `self`.
    ///
    /// If this would underflow the range of values supported by `Self`,
    /// this method is allowed to panic or wrap. Suggested behavior is
    /// to panic when debug assertions are enabled, and wrap otherwise.
    ///
    /// # Invariants
    ///
    /// For any `a` where `a.predecessor()` does not underflow:
    ///
    /// * `a == a.predecessor().successor()`
    /// * `a.predecessor() == a.backward(1).unwrap()`
    /// * `a.predecessor() <= a`
    #[inline]
    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
    fn predecessor(&self) -> Self {
        self.backward(1).expect("overflow in `Step::predecessor`")
    }

    /// Returns the *predecessor* of `self`.
    ///
    /// If this would overflow the range of values supported by `Self`,
    /// this method is defined to return the input value instead.
    ///
    /// # Invariants
    ///
    /// For any `a` where `a.predecessor()` does not overflow:
    ///
    /// * `a.predecessor_saturating() == a.predecessor()`
    ///
    /// For any `a` where `a.predecessor()` does overflow:
    ///
    /// * `a.predecessor_saturating() == a`
    #[inline]
    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
    fn predecessor_saturating(&self) -> Self {
        self.backward(1).unwrap_or_else(|| self.clone())
    }

    /// Returns the *predecessor* of `self`.
    ///
    /// # Safety
    ///
    /// It is undefined behavior if this operation exceeds the range of
    /// values supported by `Self`. If you cannot guarantee that this
    /// will not overflow, use `backward` or `predecessor` instead.
    ///
    /// For any `a`, if there exists `b` such that `b < a`,
    /// it is safe to call `a.predecessor_unchecked()`.
    #[inline]
    #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")]
    unsafe fn predecessor_unchecked(&self) -> Self {
        self.predecessor()
    }
}

Most of the implementation is taken from @SimonSapin's original PR, just updated for current master. Again, arithmetic and overflow handling with multiple integer types of different widths and signedness is tricky, careful review would be appreciated. I had to change a few of of Simon's originally added tests to get them to pass, but I believe that their formulation is correct now and they were incorrect before.

This could use a perf test to see how it impacts performance. Hopefully, due to the fact that this version of the redesign really is pretty much just removing replace_one/replace_zero and renaming the other functions to fit the description as successor/predecessor rather than add/sub, it shouldn't impact much. This also makes a potential impl to make RangeInclusive<char> work much more practical.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 3, 2020
@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
2020-02-03T21:25:27.0673094Z ========================== Starting Command Output ===========================
2020-02-03T21:25:27.0687089Z [command]/bin/bash --noprofile --norc /home/vsts/work/_temp/121d8d7d-d2e6-448e-b726-b406bb45656c.sh
2020-02-03T21:25:27.0863670Z 
2020-02-03T21:25:27.0914107Z ##[section]Finishing: Disable git automatic line ending conversion
2020-02-03T21:25:27.0918772Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/68807/merge to s
2020-02-03T21:25:27.0920132Z Task         : Get sources
2020-02-03T21:25:27.0920161Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-02-03T21:25:27.0920188Z Version      : 1.0.0
2020-02-03T21:25:27.0920216Z Author       : Microsoft
---
2020-02-03T21:25:28.5452529Z ##[command]git remote add origin https://github.com/rust-lang/rust
2020-02-03T21:25:28.5527114Z ##[command]git config gc.auto 0
2020-02-03T21:25:28.5589642Z ##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
2020-02-03T21:25:28.5634061Z ##[command]git config --get-all http.proxy
2020-02-03T21:25:28.5746940Z ##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/68807/merge:refs/remotes/pull/68807/merge
---
2020-02-03T21:30:08.5790365Z     Checking chalk-engine v0.9.0
2020-02-03T21:30:08.6507404Z error[E0407]: method `replace_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6510718Z   --> <::chalk_macros::index::index_struct macros>:18:69
2020-02-03T21:30:08.6510982Z    |
2020-02-03T21:30:08.6511246Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6515890Z 2  |   | {
2020-02-03T21:30:08.6517043Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6517377Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6517600Z ...    |
2020-02-03T21:30:08.6524626Z 18 |   |         { usize :: steps_between (& start . value, & end . value) } fn
2020-02-03T21:30:08.6524999Z    |  _|_____________________________________________________________________^
2020-02-03T21:30:08.6525385Z 19 | | |         replace_one (& mut self) -> Self
2020-02-03T21:30:08.6525690Z 20 | | |         { Self { value : usize :: replace_one (& mut self . value), } } fn
2020-02-03T21:30:08.6526025Z    | |_|_______________________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6526241Z ...    |
2020-02-03T21:30:08.6526506Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6526997Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6527188Z    | 
2020-02-03T21:30:08.6527438Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/stack.rs:15:1
2020-02-03T21:30:08.6527635Z    |
2020-02-03T21:30:08.6527635Z    |
2020-02-03T21:30:08.6527883Z 15 |  /  index_struct! {
2020-02-03T21:30:08.6528162Z 16 |  |      pub(crate) struct StackIndex {
2020-02-03T21:30:08.6528667Z 18 |  |      }
2020-02-03T21:30:08.6528919Z 19 |  |  }
2020-02-03T21:30:08.6529171Z    |  |__- in this macro invocation
2020-02-03T21:30:08.6529202Z 
2020-02-03T21:30:08.6529202Z 
2020-02-03T21:30:08.6529442Z error[E0407]: method `replace_zero` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6529672Z   --> <::chalk_macros::index::index_struct macros>:20:73
2020-02-03T21:30:08.6529850Z    |
2020-02-03T21:30:08.6530141Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6530392Z 2  |   | {
2020-02-03T21:30:08.6530684Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6530974Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6531173Z ...    |
2020-02-03T21:30:08.6531527Z 20 |   |         { Self { value : usize :: replace_one (& mut self . value), } } fn
2020-02-03T21:30:08.6531866Z    |  _|_________________________________________________________________________^
2020-02-03T21:30:08.6532231Z 21 | | |         replace_zero (& mut self) -> Self
2020-02-03T21:30:08.6532562Z 22 | | |         { Self { value : usize :: replace_zero (& mut self . value), } } fn
2020-02-03T21:30:08.6532913Z    | |_|________________________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6533138Z ...    |
2020-02-03T21:30:08.6533416Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6533946Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6534122Z    | 
2020-02-03T21:30:08.6534383Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/stack.rs:15:1
2020-02-03T21:30:08.6534562Z    |
2020-02-03T21:30:08.6534562Z    |
2020-02-03T21:30:08.6534804Z 15 | /   index_struct! {
2020-02-03T21:30:08.6535074Z 16 | |       pub(crate) struct StackIndex {
2020-02-03T21:30:08.6535562Z 18 | |       }
2020-02-03T21:30:08.6535810Z 19 | |   }
2020-02-03T21:30:08.6536049Z    | |___- in this macro invocation
2020-02-03T21:30:08.6536082Z 
2020-02-03T21:30:08.6536082Z 
2020-02-03T21:30:08.6536322Z error[E0407]: method `add_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6536551Z   --> <::chalk_macros::index::index_struct macros>:22:74
2020-02-03T21:30:08.6536725Z    |
2020-02-03T21:30:08.6537015Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6537267Z 2  |   | {
2020-02-03T21:30:08.6537557Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6538024Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6538229Z ...    |
2020-02-03T21:30:08.6538780Z 22 |   |         { Self { value : usize :: replace_zero (& mut self . value), } } fn
2020-02-03T21:30:08.6539107Z    |  _|__________________________________________________________________________^
2020-02-03T21:30:08.6539489Z 23 | | |         add_one (& self) -> Self
2020-02-03T21:30:08.6561158Z 24 | | |         { Self { value : usize :: add_one (& self . value), } } fn sub_one
2020-02-03T21:30:08.6561612Z    | |_|_______________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6561841Z ...    |
2020-02-03T21:30:08.6562105Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6562599Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6562802Z    | 
2020-02-03T21:30:08.6563052Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/stack.rs:15:1
2020-02-03T21:30:08.6563230Z    |
2020-02-03T21:30:08.6563230Z    |
2020-02-03T21:30:08.6563490Z 15 | /   index_struct! {
2020-02-03T21:30:08.6563916Z 16 | |       pub(crate) struct StackIndex {
2020-02-03T21:30:08.6564783Z 18 | |       }
2020-02-03T21:30:08.6565024Z 19 | |   }
2020-02-03T21:30:08.6565282Z    | |___- in this macro invocation
2020-02-03T21:30:08.6565333Z 
2020-02-03T21:30:08.6565333Z 
2020-02-03T21:30:08.6565637Z error[E0407]: method `sub_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6565884Z   --> <::chalk_macros::index::index_struct macros>:24:65
2020-02-03T21:30:08.6566093Z    |
2020-02-03T21:30:08.6566390Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6566643Z 2  |   | {
2020-02-03T21:30:08.6566991Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6567279Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6567513Z ...    |
2020-02-03T21:30:08.6568204Z 24 |   |         { Self { value : usize :: add_one (& self . value), } } fn sub_one
2020-02-03T21:30:08.6568591Z    |  _|_________________________________________________________________^
2020-02-03T21:30:08.6568896Z 25 | | |         (& self) -> Self
2020-02-03T21:30:08.6569337Z 26 | | |         { Self { value : usize :: sub_one (& self . value), } } fn add_usize
2020-02-03T21:30:08.6569922Z    | |_|_______________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6570167Z ...    |
2020-02-03T21:30:08.6570512Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6571313Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6571522Z    | 
2020-02-03T21:30:08.6571974Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/stack.rs:15:1
2020-02-03T21:30:08.6572193Z    |
2020-02-03T21:30:08.6572193Z    |
2020-02-03T21:30:08.6572460Z 15 | /   index_struct! {
2020-02-03T21:30:08.6572756Z 16 | |       pub(crate) struct StackIndex {
2020-02-03T21:30:08.6573659Z 18 | |       }
2020-02-03T21:30:08.6573911Z 19 | |   }
2020-02-03T21:30:08.6574706Z    | |___- in this macro invocation
2020-02-03T21:30:08.6574738Z 
2020-02-03T21:30:08.6574738Z 
2020-02-03T21:30:08.6665295Z error[E0407]: method `add_usize` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6665566Z   --> <::chalk_macros::index::index_struct macros>:26:65
2020-02-03T21:30:08.6665743Z    |
2020-02-03T21:30:08.6666023Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6666246Z 2  |   | {
2020-02-03T21:30:08.6666530Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6666805Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6666993Z ...    |
2020-02-03T21:30:08.6667399Z 26 |   |         { Self { value : usize :: sub_one (& self . value), } } fn add_usize
2020-02-03T21:30:08.6667713Z    |  _|_________________________________________________________________^
2020-02-03T21:30:08.6668002Z 27 | | |         (& self, n : usize) -> Option < Self >
2020-02-03T21:30:08.6668336Z 28 | | |         {
2020-02-03T21:30:08.6668610Z 29 | | |             usize :: add_usize (& self . value, n) . map
2020-02-03T21:30:08.6668894Z 30 | | |             (| value | Self { value })
2020-02-03T21:30:08.6669433Z    | |_|_________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6669433Z    | |_|_________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6669691Z 32 |   |     } impl From < usize > for $ n
2020-02-03T21:30:08.6669955Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6670438Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6670632Z    | 
2020-02-03T21:30:08.6670904Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/stack.rs:15:1
2020-02-03T21:30:08.6671083Z    |
2020-02-03T21:30:08.6671083Z    |
2020-02-03T21:30:08.6671319Z 15 | /   index_struct! {
2020-02-03T21:30:08.6671601Z 16 | |       pub(crate) struct StackIndex {
2020-02-03T21:30:08.6672094Z 18 | |       }
2020-02-03T21:30:08.6672336Z 19 | |   }
2020-02-03T21:30:08.6672577Z    | |___- in this macro invocation
2020-02-03T21:30:08.6689171Z 
2020-02-03T21:30:08.6689171Z 
2020-02-03T21:30:08.6689587Z error[E0407]: method `replace_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6689808Z   --> <::chalk_macros::index::index_struct macros>:18:69
2020-02-03T21:30:08.6690009Z    |
2020-02-03T21:30:08.6690274Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6690494Z 2  |   | {
2020-02-03T21:30:08.6690893Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6691178Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6691364Z ...    |
2020-02-03T21:30:08.6691651Z 18 |   |         { usize :: steps_between (& start . value, & end . value) } fn
2020-02-03T21:30:08.6692008Z    |  _|_____________________________________________________________________^
2020-02-03T21:30:08.6692293Z 19 | | |         replace_one (& mut self) -> Self
2020-02-03T21:30:08.6692591Z 20 | | |         { Self { value : usize :: replace_one (& mut self . value), } } fn
2020-02-03T21:30:08.6692914Z    | |_|_______________________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6693142Z ...    |
2020-02-03T21:30:08.6693408Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6693896Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6694284Z    | 
2020-02-03T21:30:08.6694539Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/table.rs:34:1
2020-02-03T21:30:08.6694891Z    |
2020-02-03T21:30:08.6694891Z    |
2020-02-03T21:30:08.6695144Z 34 | /   index_struct! {
2020-02-03T21:30:08.6695586Z 35 | |       pub(crate) struct AnswerIndex {
2020-02-03T21:30:08.6696287Z 37 | |       }
2020-02-03T21:30:08.6696531Z 38 | |   }
2020-02-03T21:30:08.6696795Z    | |___- in this macro invocation
2020-02-03T21:30:08.6696846Z 
2020-02-03T21:30:08.6696846Z 
2020-02-03T21:30:08.6784551Z error[E0407]: method `replace_zero` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6784790Z   --> <::chalk_macros::index::index_struct macros>:20:73
2020-02-03T21:30:08.6784995Z    |
2020-02-03T21:30:08.6785256Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6785477Z 2  |   | {
2020-02-03T21:30:08.6785773Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6786137Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6786385Z ...    |
2020-02-03T21:30:08.6786661Z 20 |   |         { Self { value : usize :: replace_one (& mut self . value), } } fn
2020-02-03T21:30:08.6787044Z    |  _|_________________________________________________________________________^
2020-02-03T21:30:08.6787318Z 21 | | |         replace_zero (& mut self) -> Self
2020-02-03T21:30:08.6787634Z 22 | | |         { Self { value : usize :: replace_zero (& mut self . value), } } fn
2020-02-03T21:30:08.6787960Z    | |_|________________________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6788164Z ...    |
2020-02-03T21:30:08.6788453Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6788941Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6789138Z    | 
2020-02-03T21:30:08.6789391Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/table.rs:34:1
2020-02-03T21:30:08.6789588Z    |
2020-02-03T21:30:08.6789588Z    |
2020-02-03T21:30:08.6789826Z 34 | /   index_struct! {
2020-02-03T21:30:08.6790092Z 35 | |       pub(crate) struct AnswerIndex {
2020-02-03T21:30:08.6790592Z 37 | |       }
2020-02-03T21:30:08.6790839Z 38 | |   }
2020-02-03T21:30:08.6791092Z    | |___- in this macro invocation
2020-02-03T21:30:08.6794351Z 
2020-02-03T21:30:08.6794351Z 
2020-02-03T21:30:08.6811239Z error[E0407]: method `add_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6811482Z   --> <::chalk_macros::index::index_struct macros>:22:74
2020-02-03T21:30:08.6811663Z    |
2020-02-03T21:30:08.6811939Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6812164Z 2  |   | {
2020-02-03T21:30:08.6812436Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6812817Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6813042Z ...    |
2020-02-03T21:30:08.6813337Z 22 |   |         { Self { value : usize :: replace_zero (& mut self . value), } } fn
2020-02-03T21:30:08.6813723Z    |  _|__________________________________________________________________________^
2020-02-03T21:30:08.6814010Z 23 | | |         add_one (& self) -> Self
2020-02-03T21:30:08.6814307Z 24 | | |         { Self { value : usize :: add_one (& self . value), } } fn sub_one
2020-02-03T21:30:08.6814641Z    | |_|_______________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6814837Z ...    |
2020-02-03T21:30:08.6815110Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6815597Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6815805Z    | 
2020-02-03T21:30:08.6816065Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/table.rs:34:1
2020-02-03T21:30:08.6816244Z    |
2020-02-03T21:30:08.6816244Z    |
2020-02-03T21:30:08.6816496Z 34 | /   index_struct! {
2020-02-03T21:30:08.6816759Z 35 | |       pub(crate) struct AnswerIndex {
2020-02-03T21:30:08.6817271Z 37 | |       }
2020-02-03T21:30:08.6817499Z 38 | |   }
2020-02-03T21:30:08.6817742Z    | |___- in this macro invocation
2020-02-03T21:30:08.6817789Z 
2020-02-03T21:30:08.6817789Z 
2020-02-03T21:30:08.6818017Z error[E0407]: method `sub_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6818239Z   --> <::chalk_macros::index::index_struct macros>:24:65
2020-02-03T21:30:08.6818438Z    |
2020-02-03T21:30:08.6818726Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6818964Z 2  |   | {
2020-02-03T21:30:08.6819273Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6819610Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6819990Z ...    |
2020-02-03T21:30:08.6820290Z 24 |   |         { Self { value : usize :: add_one (& self . value), } } fn sub_one
2020-02-03T21:30:08.6820710Z    |  _|_________________________________________________________________^
2020-02-03T21:30:08.6820986Z 25 | | |         (& self) -> Self
2020-02-03T21:30:08.6821293Z 26 | | |         { Self { value : usize :: sub_one (& self . value), } } fn add_usize
2020-02-03T21:30:08.6821654Z    | |_|_______________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6821863Z ...    |
2020-02-03T21:30:08.6822176Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6822694Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6822876Z    | 
2020-02-03T21:30:08.6823130Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/table.rs:34:1
2020-02-03T21:30:08.6823325Z    |
2020-02-03T21:30:08.6823325Z    |
2020-02-03T21:30:08.6823562Z 34 | /   index_struct! {
2020-02-03T21:30:08.6823820Z 35 | |       pub(crate) struct AnswerIndex {
2020-02-03T21:30:08.6824333Z 37 | |       }
2020-02-03T21:30:08.6824561Z 38 | |   }
2020-02-03T21:30:08.6824821Z    | |___- in this macro invocation
2020-02-03T21:30:08.6824852Z 
2020-02-03T21:30:08.6824852Z 
2020-02-03T21:30:08.6944782Z error[E0407]: method `add_usize` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6945053Z   --> <::chalk_macros::index::index_struct macros>:26:65
2020-02-03T21:30:08.6945221Z    |
2020-02-03T21:30:08.6945481Z 1  |   / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6945734Z 2  |   | {
2020-02-03T21:30:08.6946010Z 3  |   |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6946414Z 4  |   |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6946645Z ...    |
2020-02-03T21:30:08.6946919Z 26 |   |         { Self { value : usize :: sub_one (& self . value), } } fn add_usize
2020-02-03T21:30:08.6947211Z    |  _|_________________________________________________________________^
2020-02-03T21:30:08.6947619Z 27 | | |         (& self, n : usize) -> Option < Self >
2020-02-03T21:30:08.6947881Z 28 | | |         {
2020-02-03T21:30:08.6948160Z 29 | | |             usize :: add_usize (& self . value, n) . map
2020-02-03T21:30:08.6948447Z 30 | | |             (| value | Self { value })
2020-02-03T21:30:08.6948966Z    | |_|_________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6948966Z    | |_|_________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6949228Z 32 |   |     } impl From < usize > for $ n
2020-02-03T21:30:08.6949500Z 33 |   |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6949979Z    |   |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6950143Z    | 
2020-02-03T21:30:08.6950405Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/table.rs:34:1
2020-02-03T21:30:08.6950568Z    |
2020-02-03T21:30:08.6950568Z    |
2020-02-03T21:30:08.6950784Z 34 | /   index_struct! {
2020-02-03T21:30:08.6951039Z 35 | |       pub(crate) struct AnswerIndex {
2020-02-03T21:30:08.6951489Z 37 | |       }
2020-02-03T21:30:08.6951720Z 38 | |   }
2020-02-03T21:30:08.6951940Z    | |___- in this macro invocation
2020-02-03T21:30:08.6954846Z 
2020-02-03T21:30:08.6954846Z 
2020-02-03T21:30:08.6960166Z error[E0407]: method `replace_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6960417Z   --> <::chalk_macros::index::index_struct macros>:18:69
2020-02-03T21:30:08.6960612Z    |
2020-02-03T21:30:08.6960891Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6961235Z 2  |  | {
2020-02-03T21:30:08.6961583Z 3  |  |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6961846Z 4  |  |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6962129Z ...   |
2020-02-03T21:30:08.6962427Z 18 |  |         { usize :: steps_between (& start . value, & end . value) } fn
2020-02-03T21:30:08.6962872Z    |  |_____________________________________________________________________^
2020-02-03T21:30:08.6963150Z 19 | ||         replace_one (& mut self) -> Self
2020-02-03T21:30:08.6963437Z 20 | ||         { Self { value : usize :: replace_one (& mut self . value), } } fn
2020-02-03T21:30:08.6963765Z    | ||_______________________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6963977Z ...   |
2020-02-03T21:30:08.6964239Z 33 |  |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6964731Z    |  |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6964911Z    | 
2020-02-03T21:30:08.6965148Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/lib.rs:81:1
2020-02-03T21:30:08.6965320Z    |
2020-02-03T21:30:08.6965320Z    |
2020-02-03T21:30:08.6965551Z 81 | /  index_struct! {
2020-02-03T21:30:08.6965802Z 82 | |      pub struct TableIndex { // FIXME: pub b/c Fold
2020-02-03T21:30:08.6966272Z 84 | |      }
2020-02-03T21:30:08.6966483Z 85 | |  }
2020-02-03T21:30:08.6966705Z    | |__- in this macro invocation
2020-02-03T21:30:08.6980385Z 
2020-02-03T21:30:08.6980385Z 
2020-02-03T21:30:08.6980794Z error[E0407]: method `replace_zero` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6981015Z   --> <::chalk_macros::index::index_struct macros>:20:73
2020-02-03T21:30:08.6981204Z    |
2020-02-03T21:30:08.6981465Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.6981974Z 2  |  | {
2020-02-03T21:30:08.6982324Z 3  |  |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.6982590Z 4  |  |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.6982900Z ...   |
2020-02-03T21:30:08.6983188Z 20 |  |         { Self { value : usize :: replace_one (& mut self . value), } } fn
2020-02-03T21:30:08.6983480Z    |  |_________________________________________________________________________^
2020-02-03T21:30:08.6983770Z 21 | ||         replace_zero (& mut self) -> Self
2020-02-03T21:30:08.6984068Z 22 | ||         { Self { value : usize :: replace_zero (& mut self . value), } } fn
2020-02-03T21:30:08.6984426Z    | ||________________________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.6984640Z ...   |
2020-02-03T21:30:08.6985101Z 33 |  |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.6985588Z    |  |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.6985759Z    | 
2020-02-03T21:30:08.6985989Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/lib.rs:81:1
2020-02-03T21:30:08.6986181Z    |
2020-02-03T21:30:08.6986181Z    |
2020-02-03T21:30:08.6986398Z 81 | /  index_struct! {
2020-02-03T21:30:08.6986644Z 82 | |      pub struct TableIndex { // FIXME: pub b/c Fold
2020-02-03T21:30:08.6987115Z 84 | |      }
2020-02-03T21:30:08.6987325Z 85 | |  }
2020-02-03T21:30:08.6987564Z    | |__- in this macro invocation
2020-02-03T21:30:08.6987593Z 
2020-02-03T21:30:08.6987593Z 
2020-02-03T21:30:08.7104495Z error[E0407]: method `add_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7104776Z   --> <::chalk_macros::index::index_struct macros>:22:74
2020-02-03T21:30:08.7104941Z    |
2020-02-03T21:30:08.7105202Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.7105440Z 2  |  | {
2020-02-03T21:30:08.7105839Z 3  |  |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.7106155Z 4  |  |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.7106436Z ...   |
2020-02-03T21:30:08.7106724Z 22 |  |         { Self { value : usize :: replace_zero (& mut self . value), } } fn
2020-02-03T21:30:08.7106990Z    |  |__________________________________________________________________________^
2020-02-03T21:30:08.7107245Z 23 | ||         add_one (& self) -> Self
2020-02-03T21:30:08.7107554Z 24 | ||         { Self { value : usize :: add_one (& self . value), } } fn sub_one
2020-02-03T21:30:08.7107870Z    | ||_______________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7108093Z ...   |
2020-02-03T21:30:08.7108357Z 33 |  |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.7108851Z    |  |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.7109017Z    | 
2020-02-03T21:30:08.7109267Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/lib.rs:81:1
2020-02-03T21:30:08.7109431Z    |
2020-02-03T21:30:08.7109431Z    |
2020-02-03T21:30:08.7109648Z 81 | /  index_struct! {
2020-02-03T21:30:08.7109924Z 82 | |      pub struct TableIndex { // FIXME: pub b/c Fold
2020-02-03T21:30:08.7110369Z 84 | |      }
2020-02-03T21:30:08.7110607Z 85 | |  }
2020-02-03T21:30:08.7110832Z    | |__- in this macro invocation
2020-02-03T21:30:08.7113654Z 
2020-02-03T21:30:08.7113654Z 
2020-02-03T21:30:08.7118718Z error[E0407]: method `sub_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7118968Z   --> <::chalk_macros::index::index_struct macros>:24:65
2020-02-03T21:30:08.7119153Z    |
2020-02-03T21:30:08.7119413Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.7119633Z 2  |  | {
2020-02-03T21:30:08.7120027Z 3  |  |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.7120315Z 4  |  |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.7120522Z ...   |
2020-02-03T21:30:08.7120796Z 24 |  |         { Self { value : usize :: add_one (& self . value), } } fn sub_one
2020-02-03T21:30:08.7121405Z 25 | ||         (& self) -> Self
2020-02-03T21:30:08.7121405Z 25 | ||         (& self) -> Self
2020-02-03T21:30:08.7121692Z 26 | ||         { Self { value : usize :: sub_one (& self . value), } } fn add_usize
2020-02-03T21:30:08.7122026Z    | ||_______________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7122227Z ...   |
2020-02-03T21:30:08.7122503Z 33 |  |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.7122971Z    |  |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.7123153Z    | 
2020-02-03T21:30:08.7123391Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/lib.rs:81:1
2020-02-03T21:30:08.7123574Z    |
2020-02-03T21:30:08.7123574Z    |
2020-02-03T21:30:08.7123795Z 81 | /  index_struct! {
2020-02-03T21:30:08.7124051Z 82 | |      pub struct TableIndex { // FIXME: pub b/c Fold
2020-02-03T21:30:08.7124516Z 84 | |      }
2020-02-03T21:30:08.7124726Z 85 | |  }
2020-02-03T21:30:08.7124971Z    | |__- in this macro invocation
2020-02-03T21:30:08.7128154Z 
2020-02-03T21:30:08.7128154Z 
2020-02-03T21:30:08.7136836Z error[E0407]: method `add_usize` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7137106Z   --> <::chalk_macros::index::index_struct macros>:26:65
2020-02-03T21:30:08.7137287Z    |
2020-02-03T21:30:08.7137546Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.7137787Z 2  |  | {
2020-02-03T21:30:08.7138059Z 3  |  |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.7138442Z 4  |  |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.7138674Z ...   |
2020-02-03T21:30:08.7138948Z 26 |  |         { Self { value : usize :: sub_one (& self . value), } } fn add_usize
2020-02-03T21:30:08.7139313Z    |  |_________________________________________________________________^
2020-02-03T21:30:08.7139579Z 27 | ||         (& self, n : usize) -> Option < Self >
2020-02-03T21:30:08.7139960Z 28 | ||         {
2020-02-03T21:30:08.7140248Z 29 | ||             usize :: add_usize (& self . value, n) . map
2020-02-03T21:30:08.7140526Z 30 | ||             (| value | Self { value })
2020-02-03T21:30:08.7140765Z 31 | ||         }
2020-02-03T21:30:08.7141046Z    | ||_________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7141310Z 32 |  |     } impl From < usize > for $ n
2020-02-03T21:30:08.7141583Z 33 |  |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.7142063Z    |  |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.7142237Z    | 
2020-02-03T21:30:08.7142482Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/lib.rs:81:1
2020-02-03T21:30:08.7142650Z    |
2020-02-03T21:30:08.7142650Z    |
2020-02-03T21:30:08.7142868Z 81 | /  index_struct! {
2020-02-03T21:30:08.7143130Z 82 | |      pub struct TableIndex { // FIXME: pub b/c Fold
2020-02-03T21:30:08.7143586Z 84 | |      }
2020-02-03T21:30:08.7143818Z 85 | |  }
2020-02-03T21:30:08.7144052Z    | |__- in this macro invocation
2020-02-03T21:30:08.7144081Z 
2020-02-03T21:30:08.7144081Z 
2020-02-03T21:30:08.7307256Z error[E0407]: method `replace_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7307534Z   --> <::chalk_macros::index::index_struct macros>:18:69
2020-02-03T21:30:08.7307725Z    |
2020-02-03T21:30:08.7308174Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.7308669Z 2  |  | {
2020-02-03T21:30:08.7309195Z 3  |  |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.7309857Z 4  |  |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.7310080Z ...   |
2020-02-03T21:30:08.7310412Z 18 |  |         { usize :: steps_between (& start . value, & end . value) } fn
2020-02-03T21:30:08.7310731Z    |  |_____________________________________________________________________^
2020-02-03T21:30:08.7311202Z 19 | ||         replace_one (& mut self) -> Self
2020-02-03T21:30:08.7311728Z 20 | ||         { Self { value : usize :: replace_one (& mut self . value), } } fn
2020-02-03T21:30:08.7312119Z    | ||_______________________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7312548Z ...   |
2020-02-03T21:30:08.7313095Z 33 |  |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.7314378Z    |  |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.7314758Z    | 
2020-02-03T21:30:08.7315219Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/lib.rs:91:1
2020-02-03T21:30:08.7317015Z    |
2020-02-03T21:30:08.7317015Z    |
2020-02-03T21:30:08.7317284Z 91 | /  index_struct! {
2020-02-03T21:30:08.7317832Z 93 | |          value: usize,
2020-02-03T21:30:08.7318423Z 94 | |      }
2020-02-03T21:30:08.7318862Z 95 | |  }
2020-02-03T21:30:08.7319281Z    | |__- in this macro invocation
2020-02-03T21:30:08.7319281Z    | |__- in this macro invocation
2020-02-03T21:30:08.7324050Z 
2020-02-03T21:30:08.7330284Z error[E0407]: method `replace_zero` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7330541Z   --> <::chalk_macros::index::index_struct macros>:20:73
2020-02-03T21:30:08.7330741Z    |
2020-02-03T21:30:08.7331028Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.7331716Z 2  |  | {
2020-02-03T21:30:08.7332249Z 3  |  |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.7332536Z 4  |  |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.7332875Z ...   |
2020-02-03T21:30:08.7333183Z 20 |  |         { Self { value : usize :: replace_one (& mut self . value), } } fn
2020-02-03T21:30:08.7333478Z    |  |_________________________________________________________________________^
2020-02-03T21:30:08.7333800Z 21 | ||         replace_zero (& mut self) -> Self
2020-02-03T21:30:08.7334118Z 22 | ||         { Self { value : usize :: replace_zero (& mut self . value), } } fn
2020-02-03T21:30:08.7334497Z    | ||________________________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7334722Z ...   |
2020-02-03T21:30:08.7335181Z 33 |  |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.7335695Z    |  |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.7335898Z    | 
2020-02-03T21:30:08.7336148Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/lib.rs:91:1
2020-02-03T21:30:08.7336338Z    |
2020-02-03T21:30:08.7336338Z    |
2020-02-03T21:30:08.7336594Z 91 | /  index_struct! {
2020-02-03T21:30:08.7337101Z 93 | |          value: usize,
2020-02-03T21:30:08.7337342Z 94 | |      }
2020-02-03T21:30:08.7337578Z 95 | |  }
2020-02-03T21:30:08.7337840Z    | |__- in this macro invocation
2020-02-03T21:30:08.7337840Z    | |__- in this macro invocation
2020-02-03T21:30:08.7349058Z 
2020-02-03T21:30:08.7349831Z error[E0407]: method `add_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7350296Z   --> <::chalk_macros::index::index_struct macros>:22:74
2020-02-03T21:30:08.7350490Z    |
2020-02-03T21:30:08.7350789Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
2020-02-03T21:30:08.7351061Z 2  |  | {
2020-02-03T21:30:08.7351532Z 3  |  |     # [derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $ v struct
2020-02-03T21:30:08.7351881Z 4  |  |     $ n { $ vf value : usize, } impl $ n
2020-02-03T21:30:08.7352102Z ...   |
2020-02-03T21:30:08.7352666Z 22 |  |         { Self { value : usize :: replace_zero (& mut self . value), } } fn
2020-02-03T21:30:08.7353159Z    |  |__________________________________________________________________________^
2020-02-03T21:30:08.7353451Z 23 | ||         add_one (& self) -> Self
2020-02-03T21:30:08.7353797Z 24 | ||         { Self { value : usize :: add_one (& self . value), } } fn sub_one
2020-02-03T21:30:08.7354321Z    | ||_______________________________________________________________^ not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7354564Z ...   |
2020-02-03T21:30:08.7355019Z 33 |  |     { fn from (value : usize) -> Self { Self { value : value } } }
2020-02-03T21:30:08.7355723Z    |  |_- in this expansion of `index_struct!`
2020-02-03T21:30:08.7355898Z    | 
2020-02-03T21:30:08.7356157Z   ::: /cargo/registry/src/github.com-1ecc6299db9ec823/chalk-engine-0.9.0/src/lib.rs:91:1
2020-02-03T21:30:08.7356334Z    |
2020-02-03T21:30:08.7356334Z    |
2020-02-03T21:30:08.7356563Z 91 | /  index_struct! {
2020-02-03T21:30:08.7357070Z 93 | |          value: usize,
2020-02-03T21:30:08.7357296Z 94 | |      }
2020-02-03T21:30:08.7357531Z 95 | |  }
2020-02-03T21:30:08.7357777Z    | |__- in this macro invocation
2020-02-03T21:30:08.7357777Z    | |__- in this macro invocation
2020-02-03T21:30:08.7357807Z 
2020-02-03T21:30:08.7393758Z error[E0407]: method `sub_one` is not a member of trait `std::iter::Step`
2020-02-03T21:30:08.7394014Z   --> <::chalk_macros::index::index_struct macros>:24:65
2020-02-03T21:30:08.7394211Z    |
2020-02-03T21:30:08.7394689Z 1  |  / ($ v : vis struct $ n : ident { $ vf : vis value : usize, }) =>
---
2020-02-03T21:30:08.8199635Z For more information about an error, try `rustc --explain E0200`.
2020-02-03T21:30:08.8233341Z error: could not compile `chalk-engine`.
2020-02-03T21:30:08.8251868Z warning: build failed, waiting for other jobs to finish...
2020-02-03T21:30:09.4114185Z error: build failed
2020-02-03T21:30:09.4114788Z command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "check" "-Zconfig-profile" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-j" "2" "--release" "--color" "always" "--features" " llvm" "--manifest-path" "/checkout/src/rustc/Cargo.toml" "--message-format" "json-render-diagnostics"
2020-02-03T21:30:09.4114956Z failed to run: /checkout/obj/build/bootstrap/debug/bootstrap check
2020-02-03T21:30:09.4115015Z Build completed unsuccessfully in 0:02:27
2020-02-03T21:30:09.4115051Z == clock drift check ==
2020-02-03T21:30:09.4115084Z   local time: Mon Feb  3 21:30:09 UTC 2020
2020-02-03T21:30:09.4115084Z   local time: Mon Feb  3 21:30:09 UTC 2020
2020-02-03T21:30:09.5415498Z   network time: Mon, 03 Feb 2020 21:30:09 GMT
2020-02-03T21:30:09.5415578Z == end clock drift check ==
2020-02-03T21:30:11.3177889Z 
2020-02-03T21:30:11.3262174Z ##[error]Bash exited with code '1'.
2020-02-03T21:30:11.3273069Z ##[section]Finishing: Run build
2020-02-03T21:30:11.3284324Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/68807/merge to s
2020-02-03T21:30:11.3286281Z Task         : Get sources
2020-02-03T21:30:11.3286339Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-02-03T21:30:11.3286376Z Version      : 1.0.0
2020-02-03T21:30:11.3286409Z Author       : Microsoft
2020-02-03T21:30:11.3286409Z Author       : Microsoft
2020-02-03T21:30:11.3286462Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
2020-02-03T21:30:11.3286502Z ==============================================================================
2020-02-03T21:30:11.6686161Z Cleaning any cached credential from repository: rust-lang/rust (GitHub)
2020-02-03T21:30:11.6718086Z ##[section]Finishing: Checkout rust-lang/rust@refs/pull/68807/merge to s
2020-02-03T21:30:11.6811141Z Cleaning up task key
2020-02-03T21:30:11.6811737Z Start cleaning up orphan processes.
2020-02-03T21:30:11.6934838Z Terminate orphan process: pid (4316) (python)
2020-02-03T21:30:11.7080203Z ##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@@ -311,7 +549,13 @@ impl<A: Step> Iterator for ops::RangeFrom<A> {

#[inline]
fn next(&mut self) -> Option<A> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RangeFrom is a very tricky case (cc #25708, @ollie27, #62886 (comment)).

Because of the stable declaration of the type, it is physically impossible to store the e.g. 257 states for RangeFrom to yield 0, 1, 2, ..., 254, 255, panic, .... The current implementation (and the one here) in debug mode yield 253, 254, panic, ... and in release mode yield 254, 255, 0, 1, .... Mixing in nth with next calls makes it even more interesting: nth always panics if it would overflow u8, and when it should return 255, in release mode sets the range to 0.., and in debug mode panics. So nth is not consistent with n calls to next due to this behavior of only sometimes panicking when overflowing.

This PR shouldn't change the observable behavior of RangeFrom, but it's worth discussing where it is desired to end up with RangeFrom, as that will shape what behaviors Step needs to provide. But no matter what we choose, n.. must behave differently from n..MAX.

The four consistent potential solutions are:

  • Unconditional saturation: 0, 1, ..., 254, 255, 255, ...,
  • Unconditional wrapping: 0, 1, ..., 254, 255, 0, 1, ...,
  • Unconditional panic "off-by-one": 0, 1, ..., 254, panic, ...
  • Debug-conditional panic "off-by-one" or wrapping, applied identically to nth as to next, or
  • Through some amazing hack, add an extra bit of state to RangeFrom without breaking public API.

The last is impossible without drastically changing how struct initialization works in Rust, so is a no-go.

The unconditional "off-by-one" panic is consistent and simple, and is consistent with interpreting n.. as n..MAX (not n..=MAX). Still, this feels like an incorrect choice semantically to me. This also makes e.g. (0u8..).take(256) not work, even though iteration is capped before overflow, requiring instead use of 0u8..=255u8 (which to be fair is actually less state, 2*u8 rather than u8+usize).

Unconditional saturation, unconditional wrapping, and debug-conditional are "just" a choice of semantics, though the former two would break the debug-checked custom of Rust's integer types. However, it's actually a bit more complicated than that, because of nth. successor_wrapping, forward_wrapping, and forward_saturating aren't (efficiently) implementable by this minimum required API surface (of forward_checked(n) and backward_checked(n)). If we want either of these semantics, we'd have to add a batch operation for it to Step in order to keep RangeFrom::nth O(1).

Writing all of this out, I think I'm now leaning towards renaming forward to forward_checked, and adding a forward that has the same "panic or wrap" semantics as successor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, equivalently, giving successor (and friends) a step parameter as well.

src/libcore/iter/range.rs Outdated Show resolved Hide resolved
Remove the unused replace_one and replace_zero,
and rename methods to make Step about succ/pred operations.
The primary api is still forward (succ_n) and backward (pred_n),
but we provide stepwise methods for optimization purposes.
@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
2020-02-04T19:39:39.8028009Z ========================== Starting Command Output ===========================
2020-02-04T19:39:39.8029411Z [command]/bin/bash --noprofile --norc /home/vsts/work/_temp/dbbd57b7-dd81-4713-af8c-61a534573530.sh
2020-02-04T19:39:39.8029448Z 
2020-02-04T19:39:39.8032953Z ##[section]Finishing: Disable git automatic line ending conversion
2020-02-04T19:39:39.8038959Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/68807/merge to s
2020-02-04T19:39:39.8040526Z Task         : Get sources
2020-02-04T19:39:39.8040558Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-02-04T19:39:39.8040606Z Version      : 1.0.0
2020-02-04T19:39:39.8040640Z Author       : Microsoft
---
2020-02-04T19:39:40.6506289Z ##[command]git remote add origin https://github.com/rust-lang/rust
2020-02-04T19:39:40.6602484Z ##[command]git config gc.auto 0
2020-02-04T19:39:40.6680018Z ##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
2020-02-04T19:39:40.6749232Z ##[command]git config --get-all http.proxy
2020-02-04T19:39:40.6900760Z ##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/68807/merge:refs/remotes/pull/68807/merge
---
2020-02-04T19:44:18.1712223Z     Checking core v0.0.0 (/checkout/src/libcore)
2020-02-04T19:44:25.3585652Z error[E0599]: no method named `predeccessor_unchecked` found for type parameter `A` in the current scope
2020-02-04T19:44:25.3586244Z    --> src/libcore/iter/range.rs:760:39
2020-02-04T19:44:25.3586537Z     |
2020-02-04T19:44:25.3586848Z 760 |             let n = unsafe { self.end.predeccessor_unchecked() };
2020-02-04T19:44:25.3587239Z     |                                       ^^^^^^^^^^^^^^^^^^^^^^ help: there is a method with a similar name: `predecessor_unchecked`
2020-02-04T19:44:28.0210052Z    Compiling libc v0.2.66
2020-02-04T19:44:28.2368416Z error: aborting due to previous error
2020-02-04T19:44:28.2369315Z 
2020-02-04T19:44:28.2372405Z For more information about this error, try `rustc --explain E0599`.
---
2020-02-04T19:44:29.8248173Z   local time: Tue Feb  4 19:44:29 UTC 2020
2020-02-04T19:44:29.9852723Z   network time: Tue, 04 Feb 2020 19:44:29 GMT
2020-02-04T19:44:29.9854935Z == end clock drift check ==
2020-02-04T19:44:32.8923253Z 
2020-02-04T19:44:32.9013532Z ##[error]Bash exited with code '1'.
2020-02-04T19:44:32.9025811Z ##[section]Finishing: Run build
2020-02-04T19:44:32.9044950Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/68807/merge to s
2020-02-04T19:44:32.9046905Z Task         : Get sources
2020-02-04T19:44:32.9046950Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-02-04T19:44:32.9046994Z Version      : 1.0.0
2020-02-04T19:44:32.9047052Z Author       : Microsoft
2020-02-04T19:44:32.9047052Z Author       : Microsoft
2020-02-04T19:44:32.9047097Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
2020-02-04T19:44:32.9047144Z ==============================================================================
2020-02-04T19:44:33.3294660Z Cleaning any cached credential from repository: rust-lang/rust (GitHub)
2020-02-04T19:44:33.3334848Z ##[section]Finishing: Checkout rust-lang/rust@refs/pull/68807/merge to s
2020-02-04T19:44:33.3452034Z Cleaning up task key
2020-02-04T19:44:33.3453043Z Start cleaning up orphan processes.
2020-02-04T19:44:33.3563658Z Terminate orphan process: pid (4309) (python)
2020-02-04T19:44:33.3754099Z ##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@nikomatsakis
Copy link
Contributor

Regarding the use of chalk -- we are planning on upgrading to newer versions but there is some work required for that. I have been debating if we should simply remove the existing integration in the meantime, maybe this is a motivator.

@bors
Copy link
Contributor

bors commented Feb 10, 2020

☔ The latest upstream changes (presumably #68835) made this pull request unmergeable. Please resolve the merge conflicts.

@JohnCSimon JohnCSimon added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 16, 2020
@JohnCSimon
Copy link
Member

Ping from triage: @CAD97 - can you address the merge conflicts?

@CAD97
Copy link
Contributor Author

CAD97 commented Feb 16, 2020

I think I'm just going to close this for now. It's still blocked on chalk being upgraded or removed, even if I do keep up with rebasing.

I think once the irlo discussion of RangeFrom died down I'll come back with a fresh proposal that makes sure to support whatever consensus is there.

@CAD97 CAD97 closed this Feb 16, 2020
bors added a commit that referenced this pull request Mar 3, 2020
Remove experimental chalk option

As suggested by @nikomatsakis [here](#68807 (comment)).

The current version of chalk used by the experimental `-Zchalk` flag is [v0.9.0, which is over a year old](https://crates.io/crates/chalk-engine). Since v0.9.0, chalk has seen [a lot of further development](rust-lang/chalk@41dfe13...master), and the intent is to eventually upgrade rustc to use a more recent chalk.

However, it will take a decent chunk of effort to upgrade the current experimental chalk support, and it is currently [blocking at least some PRs](#68807) due to chalk:0.9.0's use of unstable features. So for the interim until the next chalk release and experimental rustc integration, we remove the chalk-specific code from rustc.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 14, 2020
Rework the std::iter::Step trait

Previous attempts: rust-lang#43127 rust-lang#62886 rust-lang#68807
Tracking issue: rust-lang#42168

This PR reworks the `Step` trait to be phrased in terms of the *successor* and *predecessor* operations. With this, `Step` hopefully has a consistent identity that can have a path towards stabilization. The proposed trait:

```rust
/// Objects that have a notion of *successor* and *predecessor* operations.
///
/// 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.
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>;

    /// 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::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))`
    ///
    /// For any `a`, `n`, and `m` where `n + m` does not overflow:
    ///
    /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + 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)`
    fn forward_checked(start: Self, count: usize) -> Option<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`,
    /// 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::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`
    fn forward(start: Self, count: usize) -> Self {
        Step::forward_checked(start, count).expect("overflow in `Step::forward`")
    }

    /// 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)
    }

    /// 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)`
    fn backward_checked(start: Self, count: 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`
    fn backward(start: Self, count: usize) -> Self {
        Step::backward_checked(start, count).expect("overflow in `Step::backward`")
    }

    /// 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)
    }
}
```

Note that all of these are associated functions and not callable via method syntax; the calling syntax is always `Step::forward(start, n)`. This version of the trait additionally changes the stepping functions to talk their arguments by value.

As opposed to previous attempts which provided a "step by one" method directly, this version of the trait only exposes "step by n". There are a few reasons for this:

- `Range*`, the primary consumer of `Step`, assumes that the "step by n" operation is cheap. If a single step function is provided, it will be a lot more enticing to implement "step by n" as n repeated calls to "step by one". While this is not strictly incorrect, this behavior would be surprising for anyone used to using `Range<{primitive integer}>`.
- With a trivial default impl, this can be easily added backwards-compatibly later.
- The debug-wrapping "step by n" needs to exist for `RangeFrom` to be consistent between "step by n" and "step by one" operation. (Note: the behavior is not changed by this PR, but making the behavior consistent is made tenable by this PR.)

Three "kinds" of step are provided: `_checked`, which returns an `Option` indicating attempted overflow; (unsuffixed), which provides "safe overflow" behavior (is allowed to panic, wrap, or saturate, depending on what is most convenient for a given type); and `_unchecked`, which is a version which assumes overflow does not happen.

Review is appreciated to check that:

- The invariants as described on the `Step` functions are enough to specify the "common sense" consistency for successor/predecessor.
- Implementation of `Step` functions is correct in the face of overflow and the edges of representable integers.
- Added tests of `Step` functions are asserting the correct behavior (and not just the implemented behavior).
RalfJung added a commit to RalfJung/rust that referenced this pull request May 15, 2020
Rework the std::iter::Step trait

Previous attempts: rust-lang#43127 rust-lang#62886 rust-lang#68807
Tracking issue: rust-lang#42168

This PR reworks the `Step` trait to be phrased in terms of the *successor* and *predecessor* operations. With this, `Step` hopefully has a consistent identity that can have a path towards stabilization. The proposed trait:

```rust
/// Objects that have a notion of *successor* and *predecessor* operations.
///
/// 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.
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>;

    /// 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::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))`
    ///
    /// For any `a`, `n`, and `m` where `n + m` does not overflow:
    ///
    /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + 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)`
    fn forward_checked(start: Self, count: usize) -> Option<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`,
    /// 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::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`
    fn forward(start: Self, count: usize) -> Self {
        Step::forward_checked(start, count).expect("overflow in `Step::forward`")
    }

    /// 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)
    }

    /// 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)`
    fn backward_checked(start: Self, count: 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`
    fn backward(start: Self, count: usize) -> Self {
        Step::backward_checked(start, count).expect("overflow in `Step::backward`")
    }

    /// 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)
    }
}
```

Note that all of these are associated functions and not callable via method syntax; the calling syntax is always `Step::forward(start, n)`. This version of the trait additionally changes the stepping functions to talk their arguments by value.

As opposed to previous attempts which provided a "step by one" method directly, this version of the trait only exposes "step by n". There are a few reasons for this:

- `Range*`, the primary consumer of `Step`, assumes that the "step by n" operation is cheap. If a single step function is provided, it will be a lot more enticing to implement "step by n" as n repeated calls to "step by one". While this is not strictly incorrect, this behavior would be surprising for anyone used to using `Range<{primitive integer}>`.
- With a trivial default impl, this can be easily added backwards-compatibly later.
- The debug-wrapping "step by n" needs to exist for `RangeFrom` to be consistent between "step by n" and "step by one" operation. (Note: the behavior is not changed by this PR, but making the behavior consistent is made tenable by this PR.)

Three "kinds" of step are provided: `_checked`, which returns an `Option` indicating attempted overflow; (unsuffixed), which provides "safe overflow" behavior (is allowed to panic, wrap, or saturate, depending on what is most convenient for a given type); and `_unchecked`, which is a version which assumes overflow does not happen.

Review is appreciated to check that:

- The invariants as described on the `Step` functions are enough to specify the "common sense" consistency for successor/predecessor.
- Implementation of `Step` functions is correct in the face of overflow and the edges of representable integers.
- Added tests of `Step` functions are asserting the correct behavior (and not just the implemented behavior).
RalfJung added a commit to RalfJung/rust that referenced this pull request May 15, 2020
Rework the std::iter::Step trait

Previous attempts: rust-lang#43127 rust-lang#62886 rust-lang#68807
Tracking issue: rust-lang#42168

This PR reworks the `Step` trait to be phrased in terms of the *successor* and *predecessor* operations. With this, `Step` hopefully has a consistent identity that can have a path towards stabilization. The proposed trait:

```rust
/// Objects that have a notion of *successor* and *predecessor* operations.
///
/// 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.
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>;

    /// 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::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))`
    ///
    /// For any `a`, `n`, and `m` where `n + m` does not overflow:
    ///
    /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + 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)`
    fn forward_checked(start: Self, count: usize) -> Option<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`,
    /// 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::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`
    fn forward(start: Self, count: usize) -> Self {
        Step::forward_checked(start, count).expect("overflow in `Step::forward`")
    }

    /// 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)
    }

    /// 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)`
    fn backward_checked(start: Self, count: 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`
    fn backward(start: Self, count: usize) -> Self {
        Step::backward_checked(start, count).expect("overflow in `Step::backward`")
    }

    /// 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)
    }
}
```

Note that all of these are associated functions and not callable via method syntax; the calling syntax is always `Step::forward(start, n)`. This version of the trait additionally changes the stepping functions to talk their arguments by value.

As opposed to previous attempts which provided a "step by one" method directly, this version of the trait only exposes "step by n". There are a few reasons for this:

- `Range*`, the primary consumer of `Step`, assumes that the "step by n" operation is cheap. If a single step function is provided, it will be a lot more enticing to implement "step by n" as n repeated calls to "step by one". While this is not strictly incorrect, this behavior would be surprising for anyone used to using `Range<{primitive integer}>`.
- With a trivial default impl, this can be easily added backwards-compatibly later.
- The debug-wrapping "step by n" needs to exist for `RangeFrom` to be consistent between "step by n" and "step by one" operation. (Note: the behavior is not changed by this PR, but making the behavior consistent is made tenable by this PR.)

Three "kinds" of step are provided: `_checked`, which returns an `Option` indicating attempted overflow; (unsuffixed), which provides "safe overflow" behavior (is allowed to panic, wrap, or saturate, depending on what is most convenient for a given type); and `_unchecked`, which is a version which assumes overflow does not happen.

Review is appreciated to check that:

- The invariants as described on the `Step` functions are enough to specify the "common sense" consistency for successor/predecessor.
- Implementation of `Step` functions is correct in the face of overflow and the edges of representable integers.
- Added tests of `Step` functions are asserting the correct behavior (and not just the implemented behavior).
bors added a commit to rust-lang-ci/rust that referenced this pull request May 15, 2020
Rework the std::iter::Step trait

Previous attempts: rust-lang#43127 rust-lang#62886 rust-lang#68807
Tracking issue: rust-lang#42168

This PR reworks the `Step` trait to be phrased in terms of the *successor* and *predecessor* operations. With this, `Step` hopefully has a consistent identity that can have a path towards stabilization. The proposed trait:

```rust
/// Objects that have a notion of *successor* and *predecessor* operations.
///
/// 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.
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>;

    /// 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::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))`
    ///
    /// For any `a`, `n`, and `m` where `n + m` does not overflow:
    ///
    /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + 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)`
    fn forward_checked(start: Self, count: usize) -> Option<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`,
    /// 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::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`
    fn forward(start: Self, count: usize) -> Self {
        Step::forward_checked(start, count).expect("overflow in `Step::forward`")
    }

    /// 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)
    }

    /// 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)`
    fn backward_checked(start: Self, count: 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`
    fn backward(start: Self, count: usize) -> Self {
        Step::backward_checked(start, count).expect("overflow in `Step::backward`")
    }

    /// 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)
    }
}
```

Note that all of these are associated functions and not callable via method syntax; the calling syntax is always `Step::forward(start, n)`. This version of the trait additionally changes the stepping functions to talk their arguments by value.

As opposed to previous attempts which provided a "step by one" method directly, this version of the trait only exposes "step by n". There are a few reasons for this:

- `Range*`, the primary consumer of `Step`, assumes that the "step by n" operation is cheap. If a single step function is provided, it will be a lot more enticing to implement "step by n" as n repeated calls to "step by one". While this is not strictly incorrect, this behavior would be surprising for anyone used to using `Range<{primitive integer}>`.
- With a trivial default impl, this can be easily added backwards-compatibly later.
- The debug-wrapping "step by n" needs to exist for `RangeFrom` to be consistent between "step by n" and "step by one" operation. (Note: the behavior is not changed by this PR, but making the behavior consistent is made tenable by this PR.)

Three "kinds" of step are provided: `_checked`, which returns an `Option` indicating attempted overflow; (unsuffixed), which provides "safe overflow" behavior (is allowed to panic, wrap, or saturate, depending on what is most convenient for a given type); and `_unchecked`, which is a version which assumes overflow does not happen.

Review is appreciated to check that:

- The invariants as described on the `Step` functions are enough to specify the "common sense" consistency for successor/predecessor.
- Implementation of `Step` functions is correct in the face of overflow and the edges of representable integers.
- Added tests of `Step` functions are asserting the correct behavior (and not just the implemented behavior).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants