Skip to content

Commit c52c2ee

Browse files
committed
refactor: Deduplicate pow implementations
`strict_pow` can be implemented in terms of `checked_pow`, `wrapping_pow` can be implemented in terms of `overflowing_pow`, and `pow` can be implemented in terms of `strict_pow` or `wrapping_pow`.
1 parent 7ae2563 commit c52c2ee

5 files changed

Lines changed: 48 additions & 207 deletions

File tree

library/core/src/num/imp/overflow_panic.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,9 @@ pub(in crate::num) const fn shr() -> ! {
4949
pub(in crate::num) const fn shl() -> ! {
5050
panic!("attempt to shift left with overflow")
5151
}
52+
53+
#[cold]
54+
#[track_caller]
55+
pub(in crate::num) const fn pow() -> ! {
56+
panic!("attempt to exponentiate with overflow")
57+
}

library/core/src/num/int_macros.rs

Lines changed: 20 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,23 +1807,10 @@ macro_rules! int_impl {
18071807
without modifying the original"]
18081808
#[inline]
18091809
#[track_caller]
1810-
pub const fn strict_pow(self, mut exp: u32) -> Self {
1811-
if exp == 0 {
1812-
return 1;
1813-
}
1814-
let mut base = self;
1815-
let mut acc: Self = 1;
1816-
1817-
loop {
1818-
if (exp & 1) == 1 {
1819-
acc = acc.strict_mul(base);
1820-
// since exp!=0, finally the exp must be 1.
1821-
if exp == 1 {
1822-
return acc;
1823-
}
1824-
}
1825-
exp /= 2;
1826-
base = base.strict_mul(base);
1810+
pub const fn strict_pow(self, exp: u32) -> Self {
1811+
match self.checked_pow(exp) {
1812+
Some(x) => x,
1813+
None => imp::overflow_panic::pow(),
18271814
}
18281815
}
18291816

@@ -2438,43 +2425,9 @@ macro_rules! int_impl {
24382425
#[must_use = "this returns the result of the operation, \
24392426
without modifying the original"]
24402427
#[inline]
2441-
pub const fn wrapping_pow(self, mut exp: u32) -> Self {
2442-
if exp == 0 {
2443-
return 1;
2444-
}
2445-
let mut base = self;
2446-
let mut acc: Self = 1;
2447-
2448-
if intrinsics::is_val_statically_known(exp) {
2449-
while exp > 1 {
2450-
if (exp & 1) == 1 {
2451-
acc = acc.wrapping_mul(base);
2452-
}
2453-
exp /= 2;
2454-
base = base.wrapping_mul(base);
2455-
}
2456-
2457-
// since exp!=0, finally the exp must be 1.
2458-
// Deal with the final bit of the exponent separately, since
2459-
// squaring the base afterwards is not necessary.
2460-
acc.wrapping_mul(base)
2461-
} else {
2462-
// This is faster than the above when the exponent is not known
2463-
// at compile time. We can't use the same code for the constant
2464-
// exponent case because LLVM is currently unable to unroll
2465-
// this loop.
2466-
loop {
2467-
if (exp & 1) == 1 {
2468-
acc = acc.wrapping_mul(base);
2469-
// since exp!=0, finally the exp must be 1.
2470-
if exp == 1 {
2471-
return acc;
2472-
}
2473-
}
2474-
exp /= 2;
2475-
base = base.wrapping_mul(base);
2476-
}
2477-
}
2428+
pub const fn wrapping_pow(self, exp: u32) -> Self {
2429+
let (a, _) = self.overflowing_pow(exp);
2430+
a
24782431
}
24792432

24802433
/// Calculates `self` + `rhs`.
@@ -3040,29 +2993,26 @@ macro_rules! int_impl {
30402993
#[inline]
30412994
pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) {
30422995
if exp == 0 {
3043-
return (1,false);
2996+
return (1, false);
30442997
}
2998+
30452999
let mut base = self;
30463000
let mut acc: Self = 1;
3047-
let mut overflown = false;
3048-
// Scratch space for storing results of overflowing_mul.
3049-
let mut r;
3001+
let mut overflow = false;
3002+
let mut tmp_overflow;
30503003

30513004
loop {
30523005
if (exp & 1) == 1 {
3053-
r = acc.overflowing_mul(base);
3006+
(acc, tmp_overflow) = acc.overflowing_mul(base);
3007+
overflow |= tmp_overflow;
30543008
// since exp!=0, finally the exp must be 1.
30553009
if exp == 1 {
3056-
r.1 |= overflown;
3057-
return r;
3010+
return (acc, overflow);
30583011
}
3059-
acc = r.0;
3060-
overflown |= r.1;
30613012
}
30623013
exp /= 2;
3063-
r = base.overflowing_mul(base);
3064-
base = r.0;
3065-
overflown |= r.1;
3014+
(base, tmp_overflow) = base.overflowing_mul(base);
3015+
overflow |= tmp_overflow;
30663016
}
30673017
}
30683018

@@ -3082,43 +3032,11 @@ macro_rules! int_impl {
30823032
without modifying the original"]
30833033
#[inline]
30843034
#[rustc_inherit_overflow_checks]
3085-
pub const fn pow(self, mut exp: u32) -> Self {
3086-
if exp == 0 {
3087-
return 1;
3088-
}
3089-
let mut base = self;
3090-
let mut acc = 1;
3091-
3092-
if intrinsics::is_val_statically_known(exp) {
3093-
while exp > 1 {
3094-
if (exp & 1) == 1 {
3095-
acc = acc * base;
3096-
}
3097-
exp /= 2;
3098-
base = base * base;
3099-
}
3100-
3101-
// since exp!=0, finally the exp must be 1.
3102-
// Deal with the final bit of the exponent separately, since
3103-
// squaring the base afterwards is not necessary and may cause a
3104-
// needless overflow.
3105-
acc * base
3035+
pub const fn pow(self, exp: u32) -> Self {
3036+
if intrinsics::overflow_checks() {
3037+
self.strict_pow(exp)
31063038
} else {
3107-
// This is faster than the above when the exponent is not known
3108-
// at compile time. We can't use the same code for the constant
3109-
// exponent case because LLVM is currently unable to unroll
3110-
// this loop.
3111-
loop {
3112-
if (exp & 1) == 1 {
3113-
acc = acc * base;
3114-
// since exp!=0, finally the exp must be 1.
3115-
if exp == 1 {
3116-
return acc;
3117-
}
3118-
}
3119-
exp /= 2;
3120-
base = base * base;
3121-
}
3039+
self.wrapping_pow(exp)
31223040
}
31233041
}
31243042

library/core/src/num/uint_macros.rs

Lines changed: 20 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2272,23 +2272,10 @@ macro_rules! uint_impl {
22722272
without modifying the original"]
22732273
#[inline]
22742274
#[track_caller]
2275-
pub const fn strict_pow(self, mut exp: u32) -> Self {
2276-
if exp == 0 {
2277-
return 1;
2278-
}
2279-
let mut base = self;
2280-
let mut acc: Self = 1;
2281-
2282-
loop {
2283-
if (exp & 1) == 1 {
2284-
acc = acc.strict_mul(base);
2285-
// since exp!=0, finally the exp must be 1.
2286-
if exp == 1 {
2287-
return acc;
2288-
}
2289-
}
2290-
exp /= 2;
2291-
base = base.strict_mul(base);
2275+
pub const fn strict_pow(self, exp: u32) -> Self {
2276+
match self.checked_pow(exp) {
2277+
None => imp::overflow_panic::pow(),
2278+
Some(a) => a,
22922279
}
22932280
}
22942281

@@ -2778,43 +2765,9 @@ macro_rules! uint_impl {
27782765
#[must_use = "this returns the result of the operation, \
27792766
without modifying the original"]
27802767
#[inline]
2781-
pub const fn wrapping_pow(self, mut exp: u32) -> Self {
2782-
if exp == 0 {
2783-
return 1;
2784-
}
2785-
let mut base = self;
2786-
let mut acc: Self = 1;
2787-
2788-
if intrinsics::is_val_statically_known(exp) {
2789-
while exp > 1 {
2790-
if (exp & 1) == 1 {
2791-
acc = acc.wrapping_mul(base);
2792-
}
2793-
exp /= 2;
2794-
base = base.wrapping_mul(base);
2795-
}
2796-
2797-
// since exp!=0, finally the exp must be 1.
2798-
// Deal with the final bit of the exponent separately, since
2799-
// squaring the base afterwards is not necessary.
2800-
acc.wrapping_mul(base)
2801-
} else {
2802-
// This is faster than the above when the exponent is not known
2803-
// at compile time. We can't use the same code for the constant
2804-
// exponent case because LLVM is currently unable to unroll
2805-
// this loop.
2806-
loop {
2807-
if (exp & 1) == 1 {
2808-
acc = acc.wrapping_mul(base);
2809-
// since exp!=0, finally the exp must be 1.
2810-
if exp == 1 {
2811-
return acc;
2812-
}
2813-
}
2814-
exp /= 2;
2815-
base = base.wrapping_mul(base);
2816-
}
2817-
}
2768+
pub const fn wrapping_pow(self, exp: u32) -> Self {
2769+
let (a, _) = self.overflowing_pow(exp);
2770+
a
28182771
}
28192772

28202773
/// Calculates `self` + `rhs`.
@@ -3456,30 +3409,26 @@ macro_rules! uint_impl {
34563409
without modifying the original"]
34573410
#[inline]
34583411
pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) {
3459-
if exp == 0{
3460-
return (1,false);
3412+
if exp == 0 {
3413+
return (1, false);
34613414
}
34623415
let mut base = self;
34633416
let mut acc: Self = 1;
3464-
let mut overflown = false;
3465-
// Scratch space for storing results of overflowing_mul.
3466-
let mut r;
3417+
let mut overflow = false;
3418+
let mut tmp_overflow;
34673419

34683420
loop {
34693421
if (exp & 1) == 1 {
3470-
r = acc.overflowing_mul(base);
3422+
(acc, tmp_overflow) = acc.overflowing_mul(base);
3423+
overflow |= tmp_overflow;
34713424
// since exp!=0, finally the exp must be 1.
34723425
if exp == 1 {
3473-
r.1 |= overflown;
3474-
return r;
3426+
return (acc, overflow);
34753427
}
3476-
acc = r.0;
3477-
overflown |= r.1;
34783428
}
34793429
exp /= 2;
3480-
r = base.overflowing_mul(base);
3481-
base = r.0;
3482-
overflown |= r.1;
3430+
(base, tmp_overflow) = base.overflowing_mul(base);
3431+
overflow |= tmp_overflow;
34833432
}
34843433
}
34853434

@@ -3497,43 +3446,11 @@ macro_rules! uint_impl {
34973446
without modifying the original"]
34983447
#[inline]
34993448
#[rustc_inherit_overflow_checks]
3500-
pub const fn pow(self, mut exp: u32) -> Self {
3501-
if exp == 0 {
3502-
return 1;
3503-
}
3504-
let mut base = self;
3505-
let mut acc = 1;
3506-
3507-
if intrinsics::is_val_statically_known(exp) {
3508-
while exp > 1 {
3509-
if (exp & 1) == 1 {
3510-
acc = acc * base;
3511-
}
3512-
exp /= 2;
3513-
base = base * base;
3514-
}
3515-
3516-
// since exp!=0, finally the exp must be 1.
3517-
// Deal with the final bit of the exponent separately, since
3518-
// squaring the base afterwards is not necessary and may cause a
3519-
// needless overflow.
3520-
acc * base
3449+
pub const fn pow(self, exp: u32) -> Self {
3450+
if intrinsics::overflow_checks() {
3451+
self.strict_pow(exp)
35213452
} else {
3522-
// This is faster than the above when the exponent is not known
3523-
// at compile time. We can't use the same code for the constant
3524-
// exponent case because LLVM is currently unable to unroll
3525-
// this loop.
3526-
loop {
3527-
if (exp & 1) == 1 {
3528-
acc = acc * base;
3529-
// since exp!=0, finally the exp must be 1.
3530-
if exp == 1 {
3531-
return acc;
3532-
}
3533-
}
3534-
exp /= 2;
3535-
base = base * base;
3536-
}
3453+
self.wrapping_pow(exp)
35373454
}
35383455
}
35393456

tests/ui/numbers-arithmetic/overflowing-pow-signed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-fail
22
//@ regex-error-pattern: thread 'main'.*panicked
3-
//@ error-pattern: attempt to multiply with overflow
3+
//@ regex-error-pattern: attempt to exponentiate with overflow
44
//@ needs-subprocess
55
//@ compile-flags: -C debug-assertions
66

tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-fail
22
//@ regex-error-pattern: thread 'main'.*panicked
3-
//@ error-pattern: attempt to multiply with overflow
3+
//@ regex-error-pattern: attempt to exponentiate with overflow
44
//@ needs-subprocess
55
//@ compile-flags: -C debug-assertions
66

0 commit comments

Comments
 (0)