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

Add operations for easy arithmetic that panics on overflow #270

Closed
RalfJung opened this issue Sep 14, 2023 · 1 comment
Closed

Add operations for easy arithmetic that panics on overflow #270

RalfJung opened this issue Sep 14, 2023 · 1 comment
Labels
ACP-accepted API Change Proposal is accepted (seconded with no objections) api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api

Comments

@RalfJung
Copy link
Member

RalfJung commented Sep 14, 2023

Proposal

Problem statement

Rust provides convenient access to several different ways of doing arithmetic:

  • a + b: shouldn't overflow but this is not checked in all configurations
  • a.wrapping_add(b): overflow is explicitly fine
  • a.saturating_add(b): saturate the result on overflow

There are also a.checked_add(b) and a.overflowing_add(b) but those are significantly different: they are not of the shape fn(T< T) -> T, so one cannot easily use them in larger arithmetic expressions.

What is missing is the natural dual to a.wrapping_add(b): arithmetic that is guaranteed to panic on overflow. One can write a.checked_add(b).unwrap(), but the extra unwrap makes this very verbose, in particular for larger operations:

a
  .checked_add(b.checked_mul(2).unwrap())
  .unwrap()
  .checked_add(c)
  .unwrap()
  .checked_mul(3)
  .unwrap()

or

a
  .checked_add(b.checked_mul(2).unwrap())
  .and_then(|x| x.checked_add(c))
  .and_then(|x| x.checked_mul(3))
  .unwrap()

Motivating examples or use cases

There are parts of Miri where I want all arithmetic to be definitely overflow-checked, and currently we have code like in the example above, and it is an eyesore.

I find it surprising that we make it easier to write "guaranteed to wrap" arithmetic than "guaranteed to panic on overflow" arithmetic. That seems to be the exact opposite of where we actually want to steer people towards! (We have a flag to globally make all regular arithmetic panic-on-overflow, of course. But that's global for the entire binary [not even just a crate!] and e.g. not an option for Miri for performance reasons.)

Solution sketch

I propose we give "panic on overflow"-style arithmetic the same status as "wrap on overflow"-style arithmetic, and provide a nice operation with the type fn(T, T) -> T for that. I propose we call this strict_add, but the name is obviously open to bikeshedding. This applies not just to add but to all arithmetic operations that have a checked_ variant.

a
  .strict_add(b.strict_mul(2))
  .strict_add(c)
  .strict_mul(3)

We could furthermore also add a Strict<i32> type that is similar to Wrapping<i32> and Saturating<i32>, but uses guaranteed overflow-checked arithmetic. But this is an optional extension of the ACP; having the methods as a first step would already be useful.

((Strict(a) + (Strict(b) * 2) + c) * 3).0

(This assumes that we have impl Add<i32> for Strict<i32> etc, which we currently do not seem to have for Wrapping, to my surprise.)

Alternatives

Other possible names: panicking_add, and a Panicking<i32> type. Or maybe something like unwrap_add or assert_add?

An alternative to Strict<T> as a newtype around T would be

enum Checked<T> {
    Value(T),
    Overflow,
}

and then having operator overloading for Checked<i32> etc. That wouldn't provide strict_* methods but (with enough operator overloading) one could write

((Checked::new(a) + (Checked::new(b) * 2) + c) * 3).unwrap()

We could do nothing, and people have to keep writing tons of unwrap everywhere. We could leave it to a library crate to provide this feature.

Links and related work

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
@RalfJung RalfJung added api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api labels Sep 14, 2023
@BurntSushi BurntSushi added the ACP-accepted API Change Proposal is accepted (seconded with no objections) label Sep 22, 2023
@BurntSushi
Copy link
Member

Seconded.

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Jan 20, 2024
…u-se

Implement strict integer operations that panic on overflow

This PR implements the first part of the ACP for adding panic on overflow style arithmetic operations (rust-lang/libs-team#270), mentioned in rust-lang#116064.

It adds the following operations on both signed and unsigned integers:

- `strict_add`
- `strict_sub`
- `strict_mul`
- `strict_div`
- `strict_div_euclid`
- `strict_rem`
- `strict_rem_euclid`
- `strict_neg`
- `strict_shl`
- `strict_shr`
- `strict_pow`

Additionally, signed integers have:

- `strict_add_unsigned`
- `strict_sub_unsigned`
- `strict_abs`

And unsigned integers have:

- `strict_add_signed`

The `div` and `rem` operations are the same as normal division and remainder but are added for completeness similar to the corresponding `wrapping_*` operations.

I'm not sure if I missed any operations, I basically found them from the `wrapping_*` and `checked_*` operations on both integer types.
Nadrieril added a commit to Nadrieril/rust that referenced this issue Jan 21, 2024
…u-se

Implement strict integer operations that panic on overflow

This PR implements the first part of the ACP for adding panic on overflow style arithmetic operations (rust-lang/libs-team#270), mentioned in rust-lang#116064.

It adds the following operations on both signed and unsigned integers:

- `strict_add`
- `strict_sub`
- `strict_mul`
- `strict_div`
- `strict_div_euclid`
- `strict_rem`
- `strict_rem_euclid`
- `strict_neg`
- `strict_shl`
- `strict_shr`
- `strict_pow`

Additionally, signed integers have:

- `strict_add_unsigned`
- `strict_sub_unsigned`
- `strict_abs`

And unsigned integers have:

- `strict_add_signed`

The `div` and `rem` operations are the same as normal division and remainder but are added for completeness similar to the corresponding `wrapping_*` operations.

I'm not sure if I missed any operations, I basically found them from the `wrapping_*` and `checked_*` operations on both integer types.
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Jan 21, 2024
Rollup merge of rust-lang#116090 - rmehri01:strict_integer_ops, r=m-ou-se

Implement strict integer operations that panic on overflow

This PR implements the first part of the ACP for adding panic on overflow style arithmetic operations (rust-lang/libs-team#270), mentioned in rust-lang#116064.

It adds the following operations on both signed and unsigned integers:

- `strict_add`
- `strict_sub`
- `strict_mul`
- `strict_div`
- `strict_div_euclid`
- `strict_rem`
- `strict_rem_euclid`
- `strict_neg`
- `strict_shl`
- `strict_shr`
- `strict_pow`

Additionally, signed integers have:

- `strict_add_unsigned`
- `strict_sub_unsigned`
- `strict_abs`

And unsigned integers have:

- `strict_add_signed`

The `div` and `rem` operations are the same as normal division and remainder but are added for completeness similar to the corresponding `wrapping_*` operations.

I'm not sure if I missed any operations, I basically found them from the `wrapping_*` and `checked_*` operations on both integer types.
lnicola pushed a commit to lnicola/rust-analyzer that referenced this issue Apr 7, 2024
Implement strict integer operations that panic on overflow

This PR implements the first part of the ACP for adding panic on overflow style arithmetic operations (rust-lang/libs-team#270), mentioned in #116064.

It adds the following operations on both signed and unsigned integers:

- `strict_add`
- `strict_sub`
- `strict_mul`
- `strict_div`
- `strict_div_euclid`
- `strict_rem`
- `strict_rem_euclid`
- `strict_neg`
- `strict_shl`
- `strict_shr`
- `strict_pow`

Additionally, signed integers have:

- `strict_add_unsigned`
- `strict_sub_unsigned`
- `strict_abs`

And unsigned integers have:

- `strict_add_signed`

The `div` and `rem` operations are the same as normal division and remainder but are added for completeness similar to the corresponding `wrapping_*` operations.

I'm not sure if I missed any operations, I basically found them from the `wrapping_*` and `checked_*` operations on both integer types.
RalfJung pushed a commit to RalfJung/rust-analyzer that referenced this issue Apr 27, 2024
Implement strict integer operations that panic on overflow

This PR implements the first part of the ACP for adding panic on overflow style arithmetic operations (rust-lang/libs-team#270), mentioned in #116064.

It adds the following operations on both signed and unsigned integers:

- `strict_add`
- `strict_sub`
- `strict_mul`
- `strict_div`
- `strict_div_euclid`
- `strict_rem`
- `strict_rem_euclid`
- `strict_neg`
- `strict_shl`
- `strict_shr`
- `strict_pow`

Additionally, signed integers have:

- `strict_add_unsigned`
- `strict_sub_unsigned`
- `strict_abs`

And unsigned integers have:

- `strict_add_signed`

The `div` and `rem` operations are the same as normal division and remainder but are added for completeness similar to the corresponding `wrapping_*` operations.

I'm not sure if I missed any operations, I basically found them from the `wrapping_*` and `checked_*` operations on both integer types.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ACP-accepted API Change Proposal is accepted (seconded with no objections) api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api
Projects
None yet
Development

No branches or pull requests

2 participants