Add Step::forward/backward_overflowing to enable RangeInclusive loop optimizations#155114
Add Step::forward/backward_overflowing to enable RangeInclusive loop optimizations#155114pitaj wants to merge 3 commits into
Step::forward/backward_overflowing to enable RangeInclusive loop optimizations#155114Conversation
This comment has been minimized.
This comment has been minimized.
f26532b to
d0c1f8d
Compare
This comment has been minimized.
This comment has been minimized.
d0c1f8d to
39e74a3
Compare
|
We discussed this in the @rust-lang/libs-api meeting. The If the |
|
Right, I forgot this implements Eq even though I implemented it manually 🤦♂️. @Amanieu I'm not sure it gains much over just going back to the derive, would that be acceptable? |
|
The derive is also acceptable. We explicitly document that |
It implements DoubleEndedIterator but regardless yeah I'll change it back to the derive |
|
@rustbot ready |
|
I found a bug with this PR. fn main() {
let mut range = 255_u8..=255_u8;
let _ = range.next();
println!("{range:?}");
println!("{}", range.contains(&100_u8));
}Output of above code on nightly: Output of above code with this PR: (The desired behavior of the |
|
And here's some strange behavior that's possibly a bug: fn main() {
let mut range = 0_usize..=0_usize;
let _ = range.next_back();
println!("{range:?}");
let data = [0; 1000];
println!("{:?}", &data[range]);
}Output of above code on nightly: Output of above code with this PR: |
Good find on
This is expected behavior from the new overflowing behavior. Given that the values of
I think the I also changed Nominating for libs-api approval of those changes |
|
This was discussed the @rust-lang/libs-api meeting. We're happy with the @bors try |
… r=Mark-Simulacrum Add `Step::forward/backward_overflowing` to enable RangeInclusive loop optimizations ACP: rust-lang/libs-team#767 This adds new required methods to the Step trait: ```rust trait Step: ... { // ... existing functions // New required functions fn forward_overflowing(start: Self, count: usize) -> (Self, bool); fn backward_overflowing(start: Self, count: usize) -> (Self, bool); } ``` It was found that using these to implement RangeInclusive's Iterator impl enabled optimizations previously only applicable to the exclusive-ended `Range`. This required changing how "exhaustion" works for `RangeInclusive`. I've nominated this for libs-api discussion because of one insta-stable change: The new implementations now only set `exhausted` when overflow occurs, and `start` is now advanced past `end` otherwise. I doubt anyone depends on the prior behavior, but it's probably worth a crater run. The exhaustion changes also affect `Debug` but my understanding is that debug formatting is never guaranteed stable. I have now changed the `nth` impls to use the new functions as well. r? libs
… r=Mark-Simulacrum Add `Step::forward/backward_overflowing` to enable RangeInclusive loop optimizations ACP: rust-lang/libs-team#767 This adds new required methods to the Step trait: ```rust trait Step: ... { // ... existing functions // New required functions fn forward_overflowing(start: Self, count: usize) -> (Self, bool); fn backward_overflowing(start: Self, count: usize) -> (Self, bool); } ``` It was found that using these to implement RangeInclusive's Iterator impl enabled optimizations previously only applicable to the exclusive-ended `Range`. This required changing how "exhaustion" works for `RangeInclusive`. I've nominated this for libs-api discussion because of one insta-stable change: The new implementations now only set `exhausted` when overflow occurs, and `start` is now advanced past `end` otherwise. I doubt anyone depends on the prior behavior, but it's probably worth a crater run. The exhaustion changes also affect `Debug` but my understanding is that debug formatting is never guaranteed stable. I have now changed the `nth` impls to use the new functions as well. r? libs
|
@bors r- https://triage.rust-lang.org/gha-logs/rust-lang/rust/83976413749#L2026-06-29T04:53:02.5947801Z |
|
This pull request was unapproved. This PR was contained in a rollup (#158538), which was unapproved. |
|
The failed test was added in #155421 |
|
I believe that guaranteeing (as stated in #155421) that the This is because, with this PR, checking whether a Nominating to decide how to resolve this dilemma, possibly reverting #155421. |
|
This PR changed when RangeInclusive becomes exhausted (now happens only when the bound overflows). let mut exhausted: legacy::RangeInclusive<u8> = 255..=255;
exhausted.next();So I don't think this needs to be looked at by libs-api again, there already okayed the exhausted change. Your concern seems to be confusing the exhausted state with being empty. |
|
@pitaj I am interpreting "exhaust" to mean the operation of "calling .next() until the iterator has produced all elements it could ever produce". I think of it as a publicly visible property, not a private implementation detail stored in the field named At the very least, I think we should let libs-api decide what "exhaust" in the documentation should mean. |
|
We discussed this in the @rust-lang/libs-api meeting. We think that the best solution here is to relax the guarantees around panicking. Instead of guaranteeing a panic if the range is exhausted, we would instead only guarantee that the result is either a panic or an empty range. |
|
Changing this to panic only sometimes feels like a serious footgun. Perhaps it could be changed to always return an empty range instead? |
|
I don't think always returning an empty range is possible in a generic context. I'm not too concerned. Realistically, nobody is going to ever call |
True.
You can construct such a test with |
but RangeInclusive loops do not
and optimize the RangeInclusive iterator implementation with them This changes the `exhausted` field to represent an overflow flag for the bounds, essentially acting as an extra bit for `Idx`. This was found to enable optimizations previously only applicable to the exclusive-ended Range type. - change end_bound to return Excluded(start) when exhausted - add contains to tests - make into_bounds panic when exhausted matches From<legacy::RangeInclusive<T>> for RangeInclusive<T> - add tests for new Step impls
so it is compatible with the new exhaustion behavior. And fix the examples to exercise that behavior.
c83fca5 to
dda08ec
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
@rustbot ready |
I added these - turns out it was a very good idea! Found a few copypaste bugs that slipped past us, which have now been fixed. |
View all comments
ACP: rust-lang/libs-team#767
This adds new required methods to the Step trait:
It was found that using these to implement RangeInclusive's Iterator impl enabled optimizations previously only applicable to the exclusive-ended
Range.This required changing how "exhaustion" works for
RangeInclusive. I've nominated this for libs-api discussion because of one insta-stable change:The new implementations now only set
exhaustedwhen overflow occurs, andstartis now advanced pastendotherwise. I doubt anyone depends on the prior behavior, but it's probably worth a crater run.The exhaustion changes also affect
Debugbut my understanding is that debug formatting is never guaranteed stable.I have now changed the
nthimpls to use the new functions as well.r? libs