-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Improve documentation of next_power_of_two #40706
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
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @brson (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
src/libcore/num/mod.rs
Outdated
@@ -2328,7 +2328,7 @@ macro_rules! uint_impl { | |||
} | |||
|
|||
/// Returns the smallest power of two greater than or equal to `self`. | |||
/// Unspecified behavior on overflow. | |||
/// More details about overflow behavior can be found in [RFC 560]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd leave the old sentence and put the new one after it.
Reading through a long RFC just to figure out what happens on overflow may be tedious. :)
It should be clear that on overflow something unspecified will happen, but if the reader wants to find out more - there's a link to the RFC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it actually true that the behavior is unspecified? For normal arithmetic at least, the RFC used to state that behavior was unspecified, but soon afterwards it was specified as "panic in debug, wrapping in release" (slightly simplified).
Edit: The RFC doesn't actually mention next_power_of_two
specifically. I suppose the behavior referred to is just the general overflow behavior, then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way I agree that we should not just link to an RFC (which is not written for end users, and very much a historic document). But the old sentence is plainly wrong AFAIK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it doesn't panic even in debug mode: https://is.gd/pmGoPY
Although by "unspecified behavior" I take it could possibly panic, but doesn't have to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this doesn't match what was discussed in #18604, specifically #18604 (comment) indicates it should panic in debug mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that linking to RFC might not be a good idea.
From what I understand about overflow behavior (correct me if I'm wrong)
- Compiler can detect literal overflow at compile time, this can be disabled using
#[allow(overflowing_literals)]
https://is.gd/M6Oxnf - In debug mode, compiler by default places some checks which panics on overflow. While in release mode it wraps on overflow. https://is.gd/gMmhFo
- Overflow checking can be turned off at compile time using
-Z force-overflow-checks=off
flag.
I could not find any page in doc which documents behavior on overflow so I linked to RFC. Let me know if there is a page which should be linked.
Or we could just remove this section because overflow behavior across the whole program should now be consistent
@irfanhudda thanks for tackling this! In this case, if this function doesn't do anything special with regards to overflow, we should just remove this sentence. If it does, than that's what we should document. From the discussion above, it looks to me like there's nothing special here, so this wording should probably just be removed. |
@steveklabnik The playground @stjepang posted earlier (https://is.gd/pmGoPY) indicates that it does do something special, namely never panic. However, based on #18604 (comment) as well as general principles, I believe this is a bug (but maybe this should be brough up over there and discussed again). |
Let's get libs to decide. Tagging @rust-lang/libs |
@steveklabnik @rkruppe I have no idea why that example (https://is.gd/pmGoPY) should panic when there is no overflow. (in
Can you take a look at https://is.gd/tkY0UX (this has warning for literal overflow) Or for case with panic https://is.gd/EJq9gM I believe that behavior here is consistent (Warn for literal overflow and panic when some operation results in overflow) |
Can you elaborate? There is overflow: The next power of two after 255 is 256, which overflows just as much as 255+1 is (only the implementation does something slightly more sensible, ensuring the result after wrapping is still some power of two). That overflowing literals produce a warning rather than a hard error is mostly a historical accident AFAIK, and not relevant at any rate because 255u8 is in range and |
@rkruppe Your are correct that next power of 255 is 256. But the implementation of
takes modulo Let me know if I'm missing something here EDIT: pub fn next_power_of_two(self) -> Self {
let bits = size_of::<Self>() * 8;
let one: Self = 1;
one << ((bits - self.wrapping_sub(one).leading_zeros() as usize) % bits)
} I don't think there can be overflow in any statement so there is no panic it just wraps around the next power of two |
Yes, that it doesn't panic is an entirely natural consequence of its implementation. I am arguing the implementation is flawed. |
@rkruppe Thanks for clearing that up. |
Discussed during libs triage the other day the conclusion was that |
@alexcrichton I'm interested in implementing this change. Should I create a new pull request for it or continue working on this one? |
Nah it's ok to do it here, no worries! |
Clarify overflow behavior of `next_power_of_two`. Related Issue: rust-lang#18604
3570506
to
f7c641b
Compare
src/libcore/num/mod.rs
Outdated
let npot = self.next_power_of_two(); | ||
let bits = size_of::<Self>() * 8; | ||
let one: Self = 1; | ||
let npot = one << ((bits - self.wrapping_sub(one).leading_zeros() as usize) % bits); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this going to panic if self == 0
?
We need a check for this condition first, the same way you wrote one in fn next_power_of_two
.
Also, I'd personally find the following slightly easier to understand (but I don't mind the current code either):
let shift = bits - self.wrapping_sub(one).leading_zeros() as usize;
if shift < bits {
Some(one << shift)
} else {
None
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, do we have tests for next_power_of_two
and checked_next_power_of_two
?
If not, it'd be a good idea to write some that check for corner cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are tests for next_power_of_two
and checked_next_power_of_two
https://github.com/rust-lang/rust/blob/master/src/libstd/num.rs#L153
src/libcore/num/mod.rs
Outdated
if self == 0 { | ||
1 | ||
} else { | ||
one << (bits - self.wrapping_sub(one).leading_zeros() as usize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, you can replace self.wrapping_sub(one)
with (self - one)
.
I think that'd make the intentions clearer (we just want to subtract one; we don't care about wrapping).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stjepang Thanks for the suggestion.
This is correcting the debug mode behaviour of the method; is fixing the release mode behaviour of the method also in-scope for this change? Like @rkruppe said earlier, the power of two after 255 is 256, so under "overflow panics in debug and wraps in release" it seems to me like Possible approach (based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2). Add the following private (especially with that name) helper:
It's a well-behaved function with no overflow cases; it just sets all bits right of the first bit that's set. Then
with the obvious extension to As a bonus, this approach adds no new branches in the release assembly over the current implementation. (LLVM turns the conditionals into |
Seems reasonable to me to implement @rkruppe's suggestion, @irfanhudda would you be up for including that inline? It may also be worth expanding on the panic semantics a bit in addition to referencing the associated RFC. |
I'm not actually sure if that's a good idea. The current behavior is a special case, yes, but it might be useful that the result is always a power of two even if there's overflow. Or maybe not. I don't know, I just want to clarify I'm not arguing for changing this behavior in release mode.
We agreed early on that linking to the RFC would not be good. I've actually found yet another outdated statement in the RFC text since then, and am now convinced the RFC needs to be either amended to reflect the status quo, or amended with a big fact disclaimer that it's totally inaccurate. (But even if that was done, I wouldn't want to link to it in user facing documentation.) |
Er sorry, mixed up the @-mention |
Maybe it can say something more specific to this method? First stab: "Overflows for |
We’re going to close this for now, to keep our queue clean, but if you get a chance to update this, please reopen! |
@alexcrichton I updated this branch but I'm not sure if I can open this pull request. Can you open this one? |
@irfanhudda i got it 👍 |
Thanks @steveklabnik I've added comments to helper functions to better explain their use. Let me know if any changes are required. |
Hmm, @alexcrichton is right -- I proposed something overly complicated here. @irfanhudda Here's a simplification, if you'd like: scottmcm@e87afa3 (based on your branch) |
Based on @scottmcm 's suggestion
Thanks @scottmcm for suggestion. |
@alexcrichton I believe this is ready for another look. |
@bors: r+ Oops sorry, thanks @irfanhudda! |
📌 Commit 18fadb6 has been approved by |
⌛ Testing commit 18fadb6 with merge 65a2688... |
Improve documentation of next_power_of_two Clarify overflow behavior of `next_power_of_two`. Related Issue: #18604
💔 Test failed - status-travis |
@bors retry
|
⌛ Testing commit 18fadb6 with merge f8f86c1... |
💔 Test failed - status-travis |
Docker hub failures? Working fine locally, let's try again. @bors retry |
⌛ Testing commit 18fadb6 with merge d8d5592... |
Improve documentation of next_power_of_two Clarify overflow behavior of `next_power_of_two`. Related Issue: #18604
☀️ Test successful - status-appveyor, status-travis |
Clarify overflow behavior of
next_power_of_two
.Related Issue: #18604