unwrap_overflow_ops/
lib.rs

1//! Arithmetic operations that always panic overflow,
2//! providing a polyfill for the [`unwrap_overflow_ops` feature].
3//!
4//! To avoid conflicts with the standard library,
5//! methods are prefixed with `unwrap_` instead of `strict_`.
6//! For example [`i32::strict_add`] becomes [`UnwrapOverflowOps::unwrap_add`].
7//!
8//! Methods are available through the [`UnwrapOverflowOps`] extension trait.
9//! Some methods are only supported for signed/unsigned integers,
10//! and require [`UnwrapOverflowOpsSigned`] or [`UnwrapOverflowOpsUnsigned`].
11//!
12//! Import the entire crate to use all three traits:
13//! `use strict_overflow_ops::*;`
14//!
15//! ## Example
16//! ```
17//! use unwrap_overflow_ops::*;
18//!
19//! assert_eq!(0i32.unwrap_add(5), 5);
20//! assert_eq!(7u32.unwrap_add_signed(-3), 4);
21//! assert_eq!(-7i32.unwrap_neg(), 7);
22//! ```
23//!
24//! [`strict_overflow_ops` feature]: https://github.com/rust-lang/rust/issues/118260
25#![deny(missing_docs)]
26#![no_std]
27use core::fmt::Debug;
28
29macro_rules! _stringify_or_default {
30    (default: $default:tt; $val:ident) => (stringify!($val));
31    (default: $default:tt;) => ($default);
32}
33
34macro_rules! unwrap_num_ops {
35    ($($op:ident {
36        $(arg: $arg:ty,)?
37        res: $res:ty,
38        begin_doc: $begin_doc:literal,
39        basic_example: $basic_example:literal,
40        panic_example: $panic_example:literal,
41        $(example_type: $example_type:ident,)?
42    },)+ $(,)?) => (paste::paste! {$(
43        #[doc = $begin_doc]
44        ///
45        /// This is a polyfill for the [`strict_overflow_ops` feature],
46        #[doc = concat!(
47            "which offers equivalent methods for each primitive integer type (ex. [`",
48             _stringify_or_default!(default: "i32"; $($example_type)*),
49             "::strict_",
50             stringify!($op),
51             "`])."
52        )]
53        ///
54        /// [`strict_overflow_ops` feature]: https://github.com/rust-lang/rust/issues/118260
55        ///
56        /// # Panics
57        /// This function will always panic on overflow,
58        /// regardless of whether overflow checks are enabled.
59        ///
60        /// # Examples
61        /// Basic usage:
62        ///
63        /// ```
64        /// use unwrap_overflow_ops::*;
65        #[doc = $basic_example]
66        /// ```
67        ///
68        /// The following panics because of overflow:
69        ///
70        /// ```should_panic
71        /// use unwrap_overflow_ops::*;
72        #[doc = $panic_example]
73        /// ```
74        #[must_use = "this returns the result of the operation, without modifying the original"]
75        fn [<unwrap_ $op>](self, $(arg: $arg)*) -> $res;
76    )*});
77
78}
79
80
81/// An extension trait for arithmetic operations
82/// that are guaranteed to panic on overflow.
83///
84/// This is a polyfill for the [`strict_overflow_ops` feature].
85///
86/// These operations are only implemented for primitive integer types.
87///
88/// [`strict_overflow_ops` feature]: https://github.com/rust-lang/rust/issues/118260
89///
90/// ## Safety
91/// These methods are guarenteed to check for overflow,
92/// regardless of compiler settings and `cfg!(...)` flags.
93///
94/// The correctness of these methods can be relied upon for memory safety.
95pub unsafe trait UnwrapOverflowOps: Copy + Debug + Sized + sealed::Sealed {
96    unwrap_num_ops! {
97        add {
98            arg: Self,
99            res: Self,
100            begin_doc: "Strict integer addition. Computes `self + rhs`, panicking if overflow occurred.",
101            basic_example: "assert_eq!((i32::MAX - 2).unwrap_add(1), i32::MAX - 1);",
102            panic_example: "let _ = (i32::MAX - 2).unwrap_add(3);",
103        },
104        sub {
105            arg: Self,
106            res: Self,
107            begin_doc: "Strict integer subtraction. Computes `self - rhs`, panicking if overflow occurred.",
108            basic_example: "assert_eq!((i32::MIN + 2).unwrap_sub(1), i32::MIN + 1);",
109            panic_example: "let _ = (i32::MIN + 2).unwrap_sub(3);",
110        },
111        mul {
112            arg: Self,
113            res: Self,
114            begin_doc: "Strict integer multiplication. Computes `self * rhs`, panicking if overflow occurred.",
115            basic_example: "assert_eq!(i32::MAX.unwrap_mul(1), i32::MAX);",
116            panic_example: "let _ = i32::MAX.unwrap_mul(2);",
117        },
118        div {
119            arg: Self,
120            res: Self,
121            begin_doc: "Strict integer division. Computes `self / rhs`, panicking if overflow occurred.",
122            basic_example: "assert_eq!((i32::MIN + 1).unwrap_div(-1), 2147483647);",
123            panic_example: "let _ = i32::MIN.unwrap_div(-1);",
124        },
125        rem {
126            arg: Self,
127            res: Self,
128            begin_doc: "Strict integer remainder. Computes `self % rhs`, panicking if the division results in overflow.",
129            basic_example: "assert_eq!(5i32.unwrap_rem(2), 1);",
130            panic_example: "let _ = 5i32.unwrap_rem(0);",
131        },
132        shr {
133            arg: u32,
134            res: Self,
135            begin_doc: "Strict shift right. Computes `self >> rhs`, panicking `rhs` is larger than or equal to the number of bits in `self`.",
136            basic_example: "assert_eq!(0x10i32.unwrap_shr(4), 0x1);",
137            panic_example: "let _ = 0x10i32.unwrap_shr(128);",
138        },
139        shl {
140            arg: u32,
141            res: Self,
142            begin_doc: "Strict shift left. Computes self << rhs, panicking if `rhs` is larger than or equal to the number of bits in `self`.",
143            basic_example: "assert_eq!(0x1i32.unwrap_shl(4), 0x10);",
144            panic_example: "let _ = 0x1i32.unwrap_shl(129);",
145        },
146        pow {
147            arg: u32,
148            res: Self,
149            begin_doc: "Strict exponentiation. Computes `self.pow(exp)`, panicking if overflow occurred.",
150            basic_example: "assert_eq!(8i32.unwrap_pow(2), 64);",
151            panic_example: "let _ = i32::MAX.unwrap_pow(2);",
152        },
153    }
154}
155
156/// An extension trait for signed arithmetic operations
157/// that are guaranteed to panic on overflow.
158///
159/// This implements operations specific to signed integers,
160/// the [`UnwrapOverflowOps`] trait is for operations supported bya ll integers.
161///
162/// This is a polyfill for the [`strict_overflow_ops` feature].
163///
164/// These operations are only implemented for primitive integer types.
165///
166/// [`strict_overflow_ops` feature]: https://github.com/rust-lang/rust/issues/118260
167/// ## Safety
168/// These methods are guarenteed to check for overflow,
169/// regardless of compiler settings and `cfg!(...)` flags.
170///
171/// The correctness of these methods can be relied upon for memory safety.
172pub unsafe trait UnwrapOverflowOpsSigned: UnwrapOverflowOps {
173    /// The unsigned integer type with the same size
174    type Unsigned: UnwrapOverflowOpsUnsigned;
175    unwrap_num_ops! {
176        add_unsigned {
177            arg: Self::Unsigned,
178            res: Self,
179            begin_doc: "Strict addition with an unsigned integer. Computes `self + rhs`, panicking if overflow occurred.",
180            basic_example: "assert_eq!(1i32.unwrap_add_unsigned(2), 3);",
181            panic_example: "let _ = 1u32.unwrap_add_signed(-2);",
182        },
183        sub_unsigned {
184            arg: Self::Unsigned,
185            res: Self,
186            begin_doc: "Strict subtraction with an unsigned integer. Computes `self - rhs`, panicking if overflow occurred.",
187            basic_example: "assert_eq!(1i32.unwrap_sub_unsigned(2), -1);",
188            panic_example: "let _ = (i32::MIN + 2).unwrap_sub_unsigned(3);",
189        },
190        neg {
191            res: Self,
192            begin_doc: "Strict negation. Computes `-self`, panicking if `self == MIN`.",
193            basic_example: "assert_eq!(5i32.unwrap_neg(), -5);",
194            panic_example: "let _ = i32::MIN.unwrap_neg();",
195        },
196    }
197}
198
199/// An extension trait for unsigned arithmetic operations
200/// that are guaranteed to panic on overflow.
201///
202/// This implements operations specific to unsigned integers,
203/// the [`UnwrapOverflowOps`] trait is for operations supported by all integers.
204///
205/// This is a polyfill for the [`strict_overflow_ops` feature].
206///
207/// These operations are only implemented for primitive integer types.
208///
209/// [`strict_overflow_ops` feature]: https://github.com/rust-lang/rust/issues/118260
210///
211/// ## Safety
212/// These methods are guarenteed to check for overflow,
213/// regardless of compiler settings and `cfg!(...)` flags.
214///
215/// The correctness of these methods can be relied upon for memory safety.
216pub unsafe trait UnwrapOverflowOpsUnsigned: UnwrapOverflowOps {
217    /// The signed integer type with the same size
218    type Signed: UnwrapOverflowOpsSigned;
219    unwrap_num_ops! {
220        add_signed {
221            arg: Self::Signed,
222            res: Self,
223            begin_doc: "Strict addition with a signed integer. Computes `self + rhs`, panicking if overflow occurred.",
224            basic_example: "assert_eq!(1u32.unwrap_add_signed(2), 3);",
225            panic_example: "let _ = 1u32.unwrap_add_signed(-2);",
226            example_type: u32,
227        },
228    }
229}
230macro_rules! common_methods_impl {
231    () => (common_methods_impl! {
232        add(Self) -> Self,
233        sub(Self) -> Self,
234        mul(Self) -> Self,
235        div(Self) -> Self,
236        rem(Self) -> Self,
237        shr(u32) -> Self,
238        shl(u32) -> Self,
239        pow(u32) -> Self,
240    });
241    ($($op:ident($arg:ty) -> $res:ty),+ $(,)?) => (paste::paste! {$(
242        #[inline]
243        #[track_caller]
244        fn [<unwrap_ $op>](self, other: $arg) -> $res {
245            match self.[<checked_ $op>](other) {
246                Some(res) => res,
247                None => overflow_ops::$op(),
248            }
249        }
250    )*});
251
252}
253macro_rules! impl_signed_ints {
254    ($($size:tt),+) => (paste::paste!{ $(
255        unsafe impl UnwrapOverflowOps for [<i $size>] {
256            common_methods_impl!();
257        }
258        unsafe impl UnwrapOverflowOpsSigned for [<i $size>] {
259            type Unsigned = [<u $size>];
260            common_methods_impl! {
261                add_unsigned(Self::Unsigned) -> Self,
262                sub_unsigned(Self::Unsigned) -> Self,
263            }
264
265            #[inline]
266            #[track_caller]
267            fn unwrap_neg(self) -> Self {
268                match self.checked_neg() {
269                    Some(res) => res,
270                    None => overflow_ops::neg(),
271                }
272            }
273        }
274        impl sealed::Sealed for [<i $size>] {}
275    )* });
276}
277impl_signed_ints!(8, 16, 32, 64, 128, size);
278macro_rules! impl_unsigned_ints {
279    ($($size:tt),+) => (paste::paste!{ $(
280        unsafe impl UnwrapOverflowOps for [<u $size>] {
281            common_methods_impl!();
282        }
283
284        unsafe impl UnwrapOverflowOpsUnsigned for [<u $size>] {
285            type Signed = [<i $size>];
286            common_methods_impl!(add_signed(Self::Signed) -> Self);
287        }
288        impl sealed::Sealed for [<u $size>] {}
289    )* })
290}
291impl_unsigned_ints!(8, 16, 32, 64, 128, size);
292
293mod sealed {
294    pub trait Sealed {}
295}
296
297/// Fallback functions for panicking on overflow.
298///
299/// Mostly a reimplementation of the stdlib
300/// private module `core::src::num::overflow_panic`.
301mod overflow_ops {
302    macro_rules! overflow_panic_msg {
303        ($($op:ident => $name:literal),+ $(,)?) => {$(
304            #[cold]
305            #[track_caller]
306            pub fn $op() -> ! {
307                panic!(concat!("attempt to ", $name, " with overflow"))
308            }
309        )*};
310    }
311
312    overflow_panic_msg! {
313        add => "add",
314        sub => "subtract",
315        mul => "multiply",
316        div => "divide",
317        rem => "calculate the remainder",
318        neg => "negate",
319        shr => "shift right",
320        shl => "shift left",
321        pow => "take integer power",
322    }
323
324    // alias used by macros
325    pub use self::add as add_signed;
326    pub use self::add as add_unsigned;
327    pub use self::sub as sub_unsigned;
328}
329