Rework the std::iter::Step trait
Previous attempts: #43127#62886#68807
Tracking issue: #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).
Minor fixes to comments
* In 'src/librustc_ast_passes/node_count.rs'
* typo fix ('rought' -> 'rough')
* In 'src/librustc_middle/middle/region.rs',
* fixed broken link to 'rustc-dev-guide'
* typo fix ('aluded' -> 'alluded')
Thank you for reviewing this PR :)
Forbid stage arguments to check
Users generally expect that check builds are fast, and that's only true in stage
0 (stages beyond that need us to build a compiler, which is slow).
Closes#69337
r? @alexcrichton
Provide separate option for std debug asserts
On local one-off benchmarking of libcore metadata-only, debug asserts in std are a significant hit (15s to 20s). Provide an option for compiler developers to disable them. A build with a nightly compiler is around 10s, for reference.
Add built in PSP target
This adds a new target, `mipsel-sony-psp`, corresponding to the Sony PSP. The linker script is necessary to handle special sections, which are required by the target. This has been tested with my [rust-psp] crate and I can confirm it works as intended.
The linker script is taken from [here]. It has been slightly adapted to work with rust and LLD.
The `stdarch` submodule was also updated in order for `libcore` to build successfully.
[rust-psp]: https://github.com/overdrivenpotato/rust-psp
[here]: https://github.com/pspdev/pspsdk/blob/master/src/base/linkfile.prx.in
Use min_specialization in liballoc
- Remove a type parameter from `[A]RcFromIter`.
- Remove an implementation of `[A]RcFromIter` that didn't actually
specialize anything.
- Remove unused implementation of `IsZero` for `Option<&mut T>`.
- Change specializations of `[A]RcEqIdent` to use a marker trait version
of `Eq`.
- Remove `BTreeClone`. I couldn't find a way to make this work with
`min_specialization`.
- Add `rustc_unsafe_specialization_marker` to `Copy` and `TrustedLen`.
After this only libcore is the only standard library crate using `feature(specialization)`.
cc #31844
* In 'src/librustc_ast_passes/node_count.rs'
* typo fix ('rought' -> 'rough')
* In 'src/librustc_middle/middle/region.rs',
* fixed broken link to 'rustc-dev-guide'
* typo fix ('aluded' -> 'alluded')
Thank you for reviewing this PR :)
Rollup of 8 pull requests
Successful merges:
- #71910 (Fix unused_parens false positive when using binary operations)
- #72087 (Fix hang in lexical_region_resolve)
- #72126 (Change `WorkProduct::saved_files` to an `Option`.)
- #72127 (add long error explanation for E0228)
- #72141 (Warn against thread::sleep in async fn)
- #72170 (use `require_lang_item` over `unwrap`.)
- #72191 (Clean up E0589 explanation)
- #72194 (Don't ICE on missing `Unsize` impl)
Failed merges:
r? @ghost
Don't ICE on missing `Unsize` impl
Previously code of the form
```rust
#![feature(unsize, dispatch_from_dyn)]
use std::marker::Unsize;
use std::ops::DispatchFromDyn;
pub struct Foo<'a, T: ?Sized> {
_inner: &'a &'a T,
}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T> {}
```
would generate an ICE due to the missing `Unsize` impl being run through the `suggest_change_mut` suggestion. This PR adds an early exit and a pointer to the appropriate docs regarding `Unsize` instead:
```
error[E0277]: the trait bound `&'a T: std::marker::Unsize<&'a U>` is not satisfied
--> src/test/ui/issues/issue-71036.rs:11:1
|
11 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unsize<&'a U>` is not implemented for `&'a T`
|
= note: all implementations of `Unsize` are provided automatically by the compiler, see <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> for more information
= note: required because of the requirements on the impl of `std::ops::DispatchFromDyn<&'a &'a U>` for `&'a &'a T`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
```
r? @estebank
Resolves#71036
use `require_lang_item` over `unwrap`.
Does not yet replace all uses of `lang_items\(\)\.*\.unwrap\(\)`, as there are more
than I expected 😅Fixes#72099
r? @RalfJung
*edit: The goal of this this PR is to change ICE from missing lang items to a fatal error.*
Warn against thread::sleep in async fn
I've seen `thread::sleep` wrecking havoc in async servers. There's already an [issue for clippy](https://github.com/rust-lang/rust-clippy/issues/4377), but the std docs could warn against it too.
add long error explanation for E0228
Add long explanation for the E0228 error code
Part of #61137
Let me know if this is wrong at all (or can be written more clearly), I'm still learning Rust.
Ensure that inliner inserts lifetime markers if they have been emitted during
codegen. Otherwise if allocas from inlined functions are merged together,
lifetime markers from one function might invalidate load & stores performed
by the other one.
Reversed empty ranges
This lint checks range expressions with inverted limits which result in empty ranges. This includes also the ranges used to index slices.
The lint reverse_range_loop was covering iteration of reversed ranges in a for loop, which is a subset of what this new lint covers, so it has been removed. I'm not sure if that's the best choice. It would be doable to check in the new lint that we are not in the arguments of a for loop; I went for removing it because the logic was too similar to keep them separated.
changelog: Added reversed_empty_ranges lint that checks for ranges where the limits have been inverted, resulting in empty ranges. Removed reverse_range_loop which was covering a subset of the new lint.
Closes#4192Closes#96
* Update Javascript to take this change into account
* Update CrateData::aliases field to take a reference instead (it allowed to remove a conversion loop)
Update books
## book
2 commits in e37c0e84e2ef73d3a4ebffda8011db6814a3b02d..6247be15a7f7509559f7981ee2209b9e0cc121df
2020-04-26 09:31:36 -0500 to 2020-05-03 10:55:09 -0500
- Fix guessing game listing explanation (rust-lang/book#2321)
- Update ch01-01-installation.md (rust-lang/book#2325)
## edition-guide
1 commits in 8204c1d123472cd17f0c1c5c77300ae802eb0271..49270740c7a4bff2763e6bc730b191d45b7d5167
2020-04-09 18:55:50 -0700 to 2020-05-11 08:50:29 -0500
- Use rust-lang/rust linkchecker on CI. (rust-lang/edition-guide#197)
## embedded-book
1 commits in 40beccdf1bb8eb9184a2e3b42db8b8c6e394247f..366c50a03bed928589771eba8a6f18e0c0c01d23
2020-04-26 17:44:14 +0000 to 2020-05-07 09:04:42 +0000
- Add HAL patterns/guidelines/recommendations (rust-embedded/book#235)
## nomicon
3 commits in 4d2d275997746d35eabfc4d992dfbdcce2f626ed..d1517d4e3f29264c5c67bce2658516bb5202c800
2020-04-27 10:24:52 -0400 to 2020-05-12 13:47:00 -0400
- Rename Unique::empty to Unique::dangling
- Use simpler link syntax
- Replace catch_panic by catch_unwind
## reference
3 commits in ed22e6fbfcb6ce436e9ea3b4bb4a55b2fb50a57e..892b928b565e35d25b6f9c47faee03b94bc41489
2020-04-24 12:46:22 -0700 to 2020-05-11 11:13:51 -0700
- clarify that str data must still be initialized
- remove language-level UB for non-UTF-8 str
- Replace incorrect term "parent modules" with "ancestor modules". (rust-lang/reference#806)
## rust-by-example
2 commits in ffc99581689fe2455908aaef5f5cf50dd03bb8f5..ab072b14393cbd9e8a1d1d75879bf51e27217bbb
2020-04-24 15:05:04 -0300 to 2020-05-09 08:46:39 -0300
- Fix link of formatting traits (rust-lang/rust-by-example#1346)
- Remove stale footnote (rust-lang/rust-by-example#1345)