Change:
const INV_TABLE_MOD_16: [u8; 8] = [1, 11, 13, 7, 9, 3, 5, 15]; /// Modulo for which the `INV_TABLE_MOD_16` is intended. const INV_TABLE_MOD: usize = 16; <del> /// INV_TABLE_MOD² const INV_TABLE_MOD_SQUARED: usize = INV_TABLE_MOD * INV_TABLE_MOD; let table_inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1] as usize; </del> // SAFETY: `m` is required to be a power-of-two, hence non-zero. let m_minus_one = unsafe { unchecked_sub(m, 1) }; <del> if m <= INV_TABLE_MOD { table_inverse & m_minus_one } else { // We iterate "up" using the following formula: // // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$ </del> <ins> let mut inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1] as usize; let mut mod_gate = INV_TABLE_MOD; // We iterate "up" using the following formula: // // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$ // // This application needs to be applied at least until `2²ⁿ ≥ m`, at which point we can // finally reduce the computation to our desired `m` by taking `inverse mod m`. // // This computation is `O(log log m)`, which is to say, that on 64-bit machines this loop // will always finish in at most 4 iterations. loop { // y = y * (2 - xy) mod n </ins> // <del> // until 2²ⁿ ≥ m. Then we can reduce to our desired `m` by taking the result `mod m`. let mut inverse = table_inverse; let mut going_mod = INV_TABLE_MOD_SQUARED; loop { // y = y * (2 - xy) mod n // // Note, that we use wrapping operations here intentionally – the original formula // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod // usize::MAX` instead, because we take the result `mod n` at the end // anyway. inverse = wrapping_mul(inverse, wrapping_sub(2usize, wrapping_mul(x, inverse))); if going_mod >= m { return inverse & m_minus_one; } going_mod = wrapping_mul(going_mod, going_mod); </del> <ins> // Note, that we use wrapping operations here intentionally – the original formula // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod // usize::MAX` instead, because we take the result `mod n` at the end // anyway. if mod_gate >= m { break; } inverse = wrapping_mul(inverse, wrapping_sub(2usize, wrapping_mul(x, inverse))); let (new_gate, overflow) = mul_with_overflow(mod_gate, mod_gate); if overflow { break; </ins> } <ins> mod_gate = new_gate; </ins> } <ins> inverse & m_minus_one </ins> } let addr = p.addr();

Discussion:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --I tried this code: I expected to see this happen: The program terminates. Instead, this happened: The program loops indefinitely. I encountered this while working on <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --:
Hangs here: label I-hang
CC
Thanks for the report. Priority wise, this seems like it isn’t going to be super high priority to resolve, given that 1<<47 is 128TiB. 128TiB, unfortunately, is half of the maximum supported address space on many modern x8664 and aarch64 deployments, so it is conceivable that this could be actually hit. Still would be nice to actually fix it. As for the strategies to fix this, there are a couple of approaches to consider: could store the into a u128 (2 usize on any supported platform today.) This is not super future-proof, and unfortunately probably somewhat more costly in the non-const-evaluated case. could use instead of . When overflows on the last iteration, it will become the maximum value that fits in and the termination condition will be hit. Not super clear what codegen quality implications are for using saturatingmul; a widening/checked multiplication and additionally compare the high bits for in the terminating condition. This is an alteration of 1. I suspect this is going to produce the best code, but that remains to be seen.
Would it work to change the termination condition from to ? Where means it wrapped, so the logical value is out of the type's range, and thus bigger than .
Or weirder: store instead of , so the comparison can be done using and the squaring operation can be replaced with a doubling (bit shift).