1use core::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub};
13
14use super::euclid::Euclid;
15
16c0nst::c0nst! {
17pub c0nst trait StrictAdd: Sized + [c0nst] Add<Self> {
19 fn strict_add(self, v: Self) -> <Self as Add<Self>>::Output;
28}
29}
30
31c0nst::c0nst! {
32pub c0nst trait StrictSub: Sized + [c0nst] Sub<Self> {
34 fn strict_sub(self, v: Self) -> <Self as Sub<Self>>::Output;
37}
38}
39
40c0nst::c0nst! {
41pub c0nst trait StrictMul: Sized + [c0nst] Mul<Self> {
43 fn strict_mul(self, v: Self) -> <Self as Mul<Self>>::Output;
46}
47}
48
49macro_rules! strict_binary_impl {
50 ($trait_name:ident, $method:ident, $overflowing:ident, $msg:expr, $t:ty) => {
51 c0nst::c0nst! {
52 c0nst impl $trait_name for $t {
53 #[inline]
54 #[track_caller]
55 fn $method(self, v: Self) -> Self {
56 let (val, overflowed) = <$t>::$overflowing(self, v);
57 if overflowed {
58 panic!($msg)
59 }
60 val
61 }
62 }
63 }
64 };
65}
66
67macro_rules! strict_binary_impl_all {
68 ($trait_name:ident, $method:ident, $overflowing:ident, $msg:expr) => {
69 strict_binary_impl!($trait_name, $method, $overflowing, $msg, u8);
70 strict_binary_impl!($trait_name, $method, $overflowing, $msg, u16);
71 strict_binary_impl!($trait_name, $method, $overflowing, $msg, u32);
72 strict_binary_impl!($trait_name, $method, $overflowing, $msg, u64);
73 strict_binary_impl!($trait_name, $method, $overflowing, $msg, usize);
74 strict_binary_impl!($trait_name, $method, $overflowing, $msg, u128);
75 strict_binary_impl!($trait_name, $method, $overflowing, $msg, i8);
76 strict_binary_impl!($trait_name, $method, $overflowing, $msg, i16);
77 strict_binary_impl!($trait_name, $method, $overflowing, $msg, i32);
78 strict_binary_impl!($trait_name, $method, $overflowing, $msg, i64);
79 strict_binary_impl!($trait_name, $method, $overflowing, $msg, isize);
80 strict_binary_impl!($trait_name, $method, $overflowing, $msg, i128);
81 };
82}
83
84strict_binary_impl_all!(
85 StrictAdd,
86 strict_add,
87 overflowing_add,
88 "attempt to add with overflow"
89);
90strict_binary_impl_all!(
91 StrictSub,
92 strict_sub,
93 overflowing_sub,
94 "attempt to subtract with overflow"
95);
96strict_binary_impl_all!(
97 StrictMul,
98 strict_mul,
99 overflowing_mul,
100 "attempt to multiply with overflow"
101);
102
103c0nst::c0nst! {
104pub c0nst trait StrictDiv: Sized + [c0nst] Div<Self> {
106 fn strict_div(self, v: Self) -> <Self as Div<Self>>::Output;
109}
110}
111
112c0nst::c0nst! {
113pub c0nst trait StrictRem: Sized + [c0nst] Rem<Self> {
115 fn strict_rem(self, v: Self) -> <Self as Rem<Self>>::Output;
118}
119}
120
121strict_binary_impl_all!(
122 StrictDiv,
123 strict_div,
124 overflowing_div,
125 "attempt to divide with overflow"
126);
127strict_binary_impl_all!(
128 StrictRem,
129 strict_rem,
130 overflowing_rem,
131 "attempt to calculate the remainder with overflow"
132);
133
134c0nst::c0nst! {
135pub c0nst trait StrictNeg: Sized {
137 type Output;
140 fn strict_neg(self) -> Self::Output;
141}
142}
143
144macro_rules! strict_neg_impl {
145 ($($t:ty)*) => {$(
146 c0nst::c0nst! {
147 c0nst impl StrictNeg for $t {
148 type Output = $t;
149 #[inline]
150 #[track_caller]
151 fn strict_neg(self) -> Self {
152 let (val, overflowed) = <$t>::overflowing_neg(self);
153 if overflowed {
154 panic!("attempt to negate with overflow")
155 }
156 val
157 }
158 }
159 }
160 )*}
161}
162
163strict_neg_impl!(usize u8 u16 u32 u64 u128);
164strict_neg_impl!(isize i8 i16 i32 i64 i128);
165
166c0nst::c0nst! {
167pub c0nst trait StrictAbs: Sized + [c0nst] Neg {
169 fn strict_abs(self) -> <Self as Neg>::Output;
172}
173}
174
175macro_rules! strict_abs_impl {
176 ($($t:ty)*) => {$(
177 c0nst::c0nst! {
178 c0nst impl StrictAbs for $t {
179 #[inline]
180 #[track_caller]
181 fn strict_abs(self) -> Self {
182 match <$t>::checked_abs(self) {
184 Some(val) => val,
185 None => panic!("attempt to negate with overflow"),
186 }
187 }
188 }
189 }
190 )*}
191}
192
193strict_abs_impl!(isize i8 i16 i32 i64 i128);
194
195c0nst::c0nst! {
196pub c0nst trait StrictShl: Sized + [c0nst] Shl<u32> {
198 fn strict_shl(self, rhs: u32) -> <Self as Shl<u32>>::Output;
201}
202}
203
204c0nst::c0nst! {
205pub c0nst trait StrictShr: Sized + [c0nst] Shr<u32> {
207 fn strict_shr(self, rhs: u32) -> <Self as Shr<u32>>::Output;
210}
211}
212
213macro_rules! strict_shift_impl {
214 ($trait_name:ident, $method:ident, $checked:ident, $msg:expr, $($t:ty)*) => {$(
215 c0nst::c0nst! {
216 c0nst impl $trait_name for $t {
217 #[inline]
218 #[track_caller]
219 fn $method(self, rhs: u32) -> Self {
220 match <$t>::$checked(self, rhs) {
221 Some(val) => val,
222 None => panic!($msg),
223 }
224 }
225 }
226 }
227 )*}
228}
229
230strict_shift_impl!(
231 StrictShl,
232 strict_shl,
233 checked_shl,
234 "attempt to shift left with overflow",
235 usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128
236);
237strict_shift_impl!(
238 StrictShr,
239 strict_shr,
240 checked_shr,
241 "attempt to shift right with overflow",
242 usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128
243);
244
245c0nst::c0nst! {
246pub c0nst trait StrictPow: Sized + [c0nst] Mul<Self> {
248 fn strict_pow(self, exp: u32) -> <Self as Mul<Self>>::Output;
251}
252}
253
254strict_shift_impl!(
255 StrictPow,
256 strict_pow,
257 checked_pow,
258 "attempt to exponentiate with overflow",
259 usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128
260);
261
262c0nst::c0nst! {
263pub c0nst trait StrictEuclid: [c0nst] Euclid {
265 fn strict_div_euclid(self, v: Self) -> <Self as Div<Self>>::Output;
269
270 fn strict_rem_euclid(self, v: Self) -> <Self as Rem<Self>>::Output;
274}
275}
276
277macro_rules! strict_euclid_impl {
278 ($($t:ty)*) => {$(
279 c0nst::c0nst! {
280 c0nst impl StrictEuclid for $t {
281 #[inline]
282 #[track_caller]
283 fn strict_div_euclid(self, v: $t) -> Self {
284 let (val, overflowed) = <$t>::overflowing_div_euclid(self, v);
285 if overflowed {
286 panic!("attempt to divide with overflow")
287 }
288 val
289 }
290
291 #[inline]
292 #[track_caller]
293 fn strict_rem_euclid(self, v: $t) -> Self {
294 let (val, overflowed) = <$t>::overflowing_rem_euclid(self, v);
295 if overflowed {
296 panic!("attempt to calculate the remainder with overflow")
297 }
298 val
299 }
300 }
301 }
302 )*}
303}
304
305strict_euclid_impl!(usize u8 u16 u32 u64 u128);
306strict_euclid_impl!(isize i8 i16 i32 i64 i128);
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn strict_ok_paths() {
314 assert_eq!(StrictAdd::strict_add(250u8, 5), 255);
315 assert_eq!(StrictSub::strict_sub(5i8, 10), -5);
316 assert_eq!(StrictMul::strict_mul(16u8, 15), 240);
317 assert_eq!(StrictDiv::strict_div(100i32, -3), -33);
318 assert_eq!(StrictRem::strict_rem(100i32, -3), 1);
319 assert_eq!(StrictNeg::strict_neg(-100i8), 100);
320 assert_eq!(StrictNeg::strict_neg(0u8), 0);
321 assert_eq!(StrictAbs::strict_abs(-100i8), 100);
322 assert_eq!(StrictShl::strict_shl(1u8, 7), 0x80);
323 assert_eq!(StrictShr::strict_shr(0x80u8, 7), 1);
324 assert_eq!(StrictPow::strict_pow(3u8, 5), 243);
325 assert_eq!(StrictEuclid::strict_div_euclid(-7i32, 4), -2);
326 assert_eq!(StrictEuclid::strict_rem_euclid(-7i32, 4), 1);
327 }
328
329 #[test]
330 #[should_panic(expected = "attempt to add with overflow")]
331 fn strict_add_panics() {
332 let _ = StrictAdd::strict_add(u8::MAX, 1);
333 }
334
335 #[test]
336 #[should_panic(expected = "attempt to negate with overflow")]
337 fn strict_neg_unsigned_panics() {
338 let _ = StrictNeg::strict_neg(1u8);
339 }
340
341 #[test]
342 #[should_panic(expected = "attempt to negate with overflow")]
343 fn strict_abs_panics() {
344 let _ = StrictAbs::strict_abs(i8::MIN);
345 }
346
347 #[test]
348 #[should_panic(expected = "attempt to shift left with overflow")]
349 fn strict_shl_panics() {
350 let _ = StrictShl::strict_shl(1u8, 8);
351 }
352
353 #[test]
354 #[should_panic(expected = "attempt to exponentiate with overflow")]
355 fn strict_pow_panics() {
356 let _ = StrictPow::strict_pow(2u8, 8);
357 }
358
359 #[test]
360 #[should_panic(expected = "attempt to divide with overflow")]
361 fn strict_div_euclid_panics() {
362 let _ = StrictEuclid::strict_div_euclid(i8::MIN, -1);
363 }
364}