-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Implement arithmetic overflow changes #20795
Conversation
Adds overflow checking to integer addition, multiplication, and subtraction when `-Z force-overflow-checks` is true, or if `--cfg ndebug` is not passed to the compiler. On overflow, it panics with `arithmatic operation overflowed`. Also adds `overflowing_add`, `overflowing_sub`, and `overflowing_mul` intrinsics for doing unchecked arithmatic. [breaking-change]
Adds a Wrapping<T> wrapper type and a WrappingOps trait for allowing people to explictly opt-in to wrapping integer arithmetic. This commit currently disables overflow checking in the build due to cases of expected overflow triggering the panic.
Many of the core rust libraries have places that rely on integer wrapping behaviour. These places have been altered to use the wrapping_* methods: * core::hash::sip - A number of macros * core::str - The `maximal_suffix` method in `TwoWaySearcher` * rustc::util::nodemap - Implementation of FnvHash * rustc_back::sha2 - A number of macros and other places * rand::isaac - Isaac64Rng, changed to use the Wrapping helper type Some places had "benign" underflow. This is when underflow or overflow occurs, but the unspecified value is not used due to other conditions. * collections::bit::Bitv - underflow when `self.nbits` is zero. * collections::hash::{map,table} - Underflow when searching an empty table. Did cause undefined behaviour in this case due to an out-of-bounds ptr::offset based on the underflowed index. However the resulting pointers would never be read from. * syntax::ext::deriving::encodable - Underflow when calculating the index of the last field in a variant with no fields. These cases were altered to avoid the underflow, often by moving the underflowing operation to a place where underflow could not happen. There was one case that relied on the fact that unsigned arithmetic and two's complement arithmetic are identical with wrapping semantics. This was changed to use the wrapping_* methods. Finally, the calculation of variant discriminants could overflow if the preceeding discriminant was `U64_MAX`. The logic in `rustc::middle::ty` for this was altered to avoid the overflow completely, while the remaining places were changed to use wrapping methods. This is because `rustc::middle::ty::enum_variants` now throws an error when the calculated discriminant value overflows a `u64`. This behaviour can be triggered by the following code: ``` enum Foo { A = U64_MAX, B } ``` This commit also implements the remaining integer operators for Wrapped<T>.
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @nick29581 (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. 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 CONTRIBUTING.md for more information. |
During my clean-up of rebase errors, I took the opportunity to implement parse_opt_bool so that it isn't identical to parse_bool wrapped in `Some`. parse_opt_bool considers no value to be true, a value of 'y', 'yes' or 'on' to be true and 'n', 'no' or 'off' to be false. All other values are an error.
#[cfg(not(stage0))] | ||
extern "rust-intrinsic" { | ||
/// Returns (a + b) mod 2^N, where N is the width of N in bits. | ||
pub fn overflowing_add<T>(a: T, b: T) -> T; |
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.
Any reason these are overflowing_foo while the methods are wrapping_foo? I think wrapping_foo is a bit more descriptive, fwiw.
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.
Yes, we ought to give them consistent names.
// error-pattern:thread '<main>' panicked at 'arithmatic operation overflowed' | ||
|
||
fn main() { | ||
let x = 200u8 + 4u8; |
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.
Should this be multiplication?
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.
Probably, yes. Blame copy-paste. (And @cmr, since it's his code :P)
This looks overall really nice. I'll give it a more detailed look soon. |
Argh, slipped off my radar! I'm sorry about that. Tomorrow. (Please feel free to ping me.) |
pub trait WrappingOps { | ||
fn wrapping_add(self, rhs: Self) -> Self; | ||
fn wrapping_sub(self, rhs: Self) -> Self; | ||
fn wrapping_mul(self, rhs: Self) -> Self; |
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.
What about wrapping_div
and wrapping_mod
? (same with the intrinsics)
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.
These two methods only make sense for f32
and f64
(since they will never overflow and already panic for division by 0)… which are not part of the checking scheme.
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.
floats don't panic on div by zero. they return inf.
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.
Err, I surely meant that integers’ division panics and never overflows when dividing.
This doesn't currently merge. |
Now that the RFC has been accepted, we can rebase, updated, and land this PR. @Aatch are you stll interested in pursuing that? Can you talk to me on IRC (I've looked for you a few times but not found you, bad timing I guess)? |
This patch doesn't address atomic operations. |
More precisely: If overflow caused by fetch_add or fetch_sub is detected the process must be aborted. Panic is not possible because other threads might already have consumed the value. |
@mahkoh and I talked briefly about atomics on IRC. The options seem to be:
Each has its own downsides. The first option is obviously prone to programmer error, particularly if someone is naively using an atomic to achieve some kind of global counter. That said, atomics are fairly specialized, and perhaps it should be expected. (Note: If we don't wrap by default, then clearly we need explicit wrapping options whatever we do.) The second process is heavy-handed. I am generally reluctant to abort an entire process as it is irrecoverable. However, this is only in debug mode -- but it impacts users who want to use checked overflowing even in optimized code, as aborting the process may not be an option for them. Aborting the thread is insufficient in general since others may observe the values. Another option is to wrap but return an enum that indicates if wrapping occurred. This seems to add some overhead and mainly serves to warn users that wrapping can occur, thus encouraging them to design a recovery strategy. Probably not very appealing. Falling back to a CAS loop gives irregular performance similarly may impact users who wish to turn on checked overflow by default (even outside debug mode). |
Having given the question of atomics some more thought (and talked it over with @aturon), my feeling is that the best option is simply to clearly document the semantics of |
@nikomatsakis maybe we should at least name them |
closing in favor of rebased version here; #22532 |
Rebase and follow-through on work done by @cmr and @Aatch. Implements most of rust-lang/rfcs#560. Errors encountered from the checks during building were fixed. The checks for division, remainder and bit-shifting have not been implemented yet. See also PR #20795 cc @Aatch ; cc @nikomatsakis
Implements most of rust-lang/rfcs#560. Errors that I encountered from the checks during building were fixed.
The checks for division, remainder and bit-shifting have not been implemented yet.
cc @nikomatsakis