about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-07-06 10:03:23 +0200
committerGitHub <noreply@github.com>2025-07-06 10:03:23 +0200
commit6fb00b1514174f215b09afbc574f15f196eb556a (patch)
tree9b70264127b960e591d48e6fbca1fcaf13ffcaf6
parent19f8ba4733c3eb8fa98ba06be63df8b621625d0b (diff)
parented3711ea29398b09483e4e2a3930567e9ba81d93 (diff)
downloadrust-6fb00b1514174f215b09afbc574f15f196eb556a.tar.gz
rust-6fb00b1514174f215b09afbc574f15f196eb556a.zip
Rollup merge of #143477 - folkertdev:use-is-multiple-of, r=joshtriplett
use `is_multiple_of` and `div_ceil`

In tricky logic, these functions are much more informative than the manual implementations. They also catch subtle bugs:

- the manual `is_multiple_of` often does not handle division by zero
- manual `div_ceil` often does not consider overflow

The transformation is free for `is_multiple_of` if the divisor is compile-time known to be non-zero. For `div_ceil` there is a small cost to considering overflow. Here is some assembly https://godbolt.org/z/5zP8KaE1d.
-rw-r--r--compiler/rustc_abi/src/lib.rs3
-rw-r--r--compiler/rustc_borrowck/src/polonius/legacy/location.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/va_arg.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs4
-rw-r--r--compiler/rustc_index/src/bit_set.rs4
-rw-r--r--compiler/rustc_passes/src/liveness/rwu_table.rs2
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs2
-rw-r--r--compiler/rustc_serialize/src/leb128.rs2
-rw-r--r--compiler/rustc_span/src/edit_distance.rs2
-rw-r--r--compiler/rustc_target/src/callconv/sparc64.rs6
-rw-r--r--compiler/rustc_target/src/callconv/x86.rs2
-rw-r--r--compiler/rustc_target/src/callconv/x86_64.rs2
-rw-r--r--compiler/rustc_target/src/callconv/xtensa.rs2
-rw-r--r--compiler/rustc_ty_utils/src/layout/invariant.rs2
-rw-r--r--library/core/src/slice/mod.rs8
-rw-r--r--library/core/src/slice/sort/shared/smallsort.rs2
-rw-r--r--library/core/src/slice/sort/stable/drift.rs4
-rw-r--r--library/core/src/str/count.rs2
-rw-r--r--library/core/src/str/iter.rs6
-rw-r--r--library/core/src/str/validations.rs2
-rw-r--r--library/portable-simd/crates/core_simd/src/lane_count.rs2
22 files changed, 33 insertions, 34 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 0df8921c9b7..a438545c76f 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -527,8 +527,7 @@ impl Size {
     /// not a multiple of 8.
     pub fn from_bits(bits: impl TryInto<u64>) -> Size {
         let bits = bits.try_into().ok().unwrap();
-        // Avoid potential overflow from `bits + 7`.
-        Size { raw: bits / 8 + ((bits % 8) + 7) / 8 }
+        Size { raw: bits.div_ceil(8) }
     }
 
     #[inline]
diff --git a/compiler/rustc_borrowck/src/polonius/legacy/location.rs b/compiler/rustc_borrowck/src/polonius/legacy/location.rs
index 5f816bb9bbd..618119a6a3d 100644
--- a/compiler/rustc_borrowck/src/polonius/legacy/location.rs
+++ b/compiler/rustc_borrowck/src/polonius/legacy/location.rs
@@ -109,6 +109,6 @@ impl PoloniusLocationTable {
 impl LocationIndex {
     fn is_start(self) -> bool {
         // even indices are start points; odd indices are mid points
-        (self.index() % 2) == 0
+        self.index().is_multiple_of(2)
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 4b07c8aef91..009e7e2487b 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -146,7 +146,7 @@ impl LlvmType for CastTarget {
                 "total size {:?} cannot be divided into units of zero size",
                 self.rest.total
             );
-            if self.rest.total.bytes() % self.rest.unit.size.bytes() != 0 {
+            if !self.rest.total.bytes().is_multiple_of(self.rest.unit.size.bytes()) {
                 assert_eq!(self.rest.unit.kind, RegKind::Integer, "only int regs can be split");
             }
             self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes())
diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs
index 4fe4c9bcbf2..486dc894a4e 100644
--- a/compiler/rustc_codegen_llvm/src/va_arg.rs
+++ b/compiler/rustc_codegen_llvm/src/va_arg.rs
@@ -172,10 +172,10 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
 
     let gr_type = target_ty.is_any_ptr() || target_ty.is_integral();
     let (reg_off, reg_top, slot_size) = if gr_type {
-        let nreg = (layout.size.bytes() + 7) / 8;
+        let nreg = layout.size.bytes().div_ceil(8);
         (gr_offs, gr_top, nreg * 8)
     } else {
-        let nreg = (layout.size.bytes() + 15) / 16;
+        let nreg = layout.size.bytes().div_ceil(16);
         (vr_offs, vr_top, nreg * 16)
     };
 
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index ff822b52a8d..c97d53a45de 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -537,7 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
         #[inline]
         fn is_offset_misaligned(offset: u64, align: Align) -> Option<Misalignment> {
-            if offset % align.bytes() == 0 {
+            if offset.is_multiple_of(align.bytes()) {
                 None
             } else {
                 // The biggest power of two through which `offset` is divisible.
@@ -1554,7 +1554,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                         // If the allocation is N-aligned, and the offset is not divisible by N,
                         // then `base + offset` has a non-zero remainder after division by `N`,
                         // which means `base + offset` cannot be null.
-                        if offset.bytes() % info.align.bytes() != 0 {
+                        if !offset.bytes().is_multiple_of(info.align.bytes()) {
                             return interp_ok(false);
                         }
                         // We don't know enough, this might be null.
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index a4885aabe1f..645d95b1dba 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -1744,13 +1744,13 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
 
 #[inline]
 fn num_words<T: Idx>(domain_size: T) -> usize {
-    (domain_size.index() + WORD_BITS - 1) / WORD_BITS
+    domain_size.index().div_ceil(WORD_BITS)
 }
 
 #[inline]
 fn num_chunks<T: Idx>(domain_size: T) -> usize {
     assert!(domain_size.index() > 0);
-    (domain_size.index() + CHUNK_BITS - 1) / CHUNK_BITS
+    domain_size.index().div_ceil(CHUNK_BITS)
 }
 
 #[inline]
diff --git a/compiler/rustc_passes/src/liveness/rwu_table.rs b/compiler/rustc_passes/src/liveness/rwu_table.rs
index 4c1f6ea141e..a1177946f86 100644
--- a/compiler/rustc_passes/src/liveness/rwu_table.rs
+++ b/compiler/rustc_passes/src/liveness/rwu_table.rs
@@ -44,7 +44,7 @@ impl RWUTable {
     const WORD_RWU_COUNT: usize = Self::WORD_BITS / Self::RWU_BITS;
 
     pub(super) fn new(live_nodes: usize, vars: usize) -> RWUTable {
-        let live_node_words = (vars + Self::WORD_RWU_COUNT - 1) / Self::WORD_RWU_COUNT;
+        let live_node_words = vars.div_ceil(Self::WORD_RWU_COUNT);
         Self { live_nodes, vars, live_node_words, words: vec![0u8; live_node_words * live_nodes] }
     }
 
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 3c1fc731784..06e59eb4ccc 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -597,7 +597,7 @@ where
         // from disk. Re-hashing results is fairly expensive, so we can't
         // currently afford to verify every hash. This subset should still
         // give us some coverage of potential bugs though.
-        let try_verify = prev_fingerprint.split().1.as_u64() % 32 == 0;
+        let try_verify = prev_fingerprint.split().1.as_u64().is_multiple_of(32);
         if std::intrinsics::unlikely(
             try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich,
         ) {
diff --git a/compiler/rustc_serialize/src/leb128.rs b/compiler/rustc_serialize/src/leb128.rs
index 954c1f728f2..da328dcea03 100644
--- a/compiler/rustc_serialize/src/leb128.rs
+++ b/compiler/rustc_serialize/src/leb128.rs
@@ -7,7 +7,7 @@ use crate::serialize::Decoder;
 /// Returns the length of the longest LEB128 encoding for `T`, assuming `T` is an integer type
 pub const fn max_leb128_len<T>() -> usize {
     // The longest LEB128 encoding for an integer uses 7 bits per byte.
-    (size_of::<T>() * 8 + 6) / 7
+    (size_of::<T>() * 8).div_ceil(7)
 }
 
 /// Returns the length of the longest LEB128 encoding of all supported integer types.
diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs
index 4f3202b694c..416e9daa8fb 100644
--- a/compiler/rustc_span/src/edit_distance.rs
+++ b/compiler/rustc_span/src/edit_distance.rs
@@ -130,7 +130,7 @@ pub fn edit_distance_with_substrings(a: &str, b: &str, limit: usize) -> Option<u
         1 // Exact substring match, but not a total word match so return non-zero
     } else if !big_len_diff {
         // Not a big difference in length, discount cost of length difference
-        score + (len_diff + 1) / 2
+        score + len_diff.div_ceil(2)
     } else {
         // A big difference in length, add back the difference in length to the score
         score + len_diff
diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs
index 186826c08fc..ecc9067ced3 100644
--- a/compiler/rustc_target/src/callconv/sparc64.rs
+++ b/compiler/rustc_target/src/callconv/sparc64.rs
@@ -90,7 +90,7 @@ where
         _ => {}
     }
 
-    if (offset.bytes() % 4) != 0
+    if !offset.bytes().is_multiple_of(4)
         && matches!(scalar2.primitive(), Primitive::Float(Float::F32 | Float::F64))
     {
         offset += Size::from_bytes(4 - (offset.bytes() % 4));
@@ -181,7 +181,7 @@ where
                 // Structure { float, int, int } doesn't like to be handled like
                 // { float, long int }. Other way around it doesn't mind.
                 if data.last_offset < arg.layout.size
-                    && (data.last_offset.bytes() % 8) != 0
+                    && !data.last_offset.bytes().is_multiple_of(8)
                     && data.prefix_index < data.prefix.len()
                 {
                     data.prefix[data.prefix_index] = Some(Reg::i32());
@@ -190,7 +190,7 @@ where
                 }
 
                 let mut rest_size = arg.layout.size - data.last_offset;
-                if (rest_size.bytes() % 8) != 0 && data.prefix_index < data.prefix.len() {
+                if !rest_size.bytes().is_multiple_of(8) && data.prefix_index < data.prefix.len() {
                     data.prefix[data.prefix_index] = Some(Reg::i32());
                     rest_size = rest_size - Reg::i32().size;
                 }
diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs
index 8328f818f9b..bdf116ff303 100644
--- a/compiler/rustc_target/src/callconv/x86.rs
+++ b/compiler/rustc_target/src/callconv/x86.rs
@@ -171,7 +171,7 @@ pub(crate) fn fill_inregs<'a, Ty, C>(
             continue;
         }
 
-        let size_in_regs = (arg.layout.size.bits() + 31) / 32;
+        let size_in_regs = arg.layout.size.bits().div_ceil(32);
 
         if size_in_regs == 0 {
             continue;
diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs
index 700ee73c8fd..d8db7ed6e4c 100644
--- a/compiler/rustc_target/src/callconv/x86_64.rs
+++ b/compiler/rustc_target/src/callconv/x86_64.rs
@@ -95,7 +95,7 @@ where
         Ok(())
     }
 
-    let n = ((arg.layout.size.bytes() + 7) / 8) as usize;
+    let n = arg.layout.size.bytes().div_ceil(8) as usize;
     if n > MAX_EIGHTBYTES {
         return Err(Memory);
     }
diff --git a/compiler/rustc_target/src/callconv/xtensa.rs b/compiler/rustc_target/src/callconv/xtensa.rs
index b687f0e20c6..a73a70a1a0c 100644
--- a/compiler/rustc_target/src/callconv/xtensa.rs
+++ b/compiler/rustc_target/src/callconv/xtensa.rs
@@ -54,7 +54,7 @@ where
     // Determine the number of GPRs needed to pass the current argument
     // according to the ABI. 2*XLen-aligned varargs are passed in "aligned"
     // register pairs, so may consume 3 registers.
-    let mut needed_arg_gprs = (size + 32 - 1) / 32;
+    let mut needed_arg_gprs = size.div_ceil(32);
     if needed_align == 64 {
         needed_arg_gprs += *arg_gprs_left % 2;
     }
diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs
index 4b65c05d0e9..1311ee31182 100644
--- a/compiler/rustc_ty_utils/src/layout/invariant.rs
+++ b/compiler/rustc_ty_utils/src/layout/invariant.rs
@@ -8,7 +8,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout};
 pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
     let tcx = cx.tcx();
 
-    if layout.size.bytes() % layout.align.abi.bytes() != 0 {
+    if !layout.size.bytes().is_multiple_of(layout.align.abi.bytes()) {
         bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
     }
     if layout.size.bytes() >= tcx.data_layout.obj_size_bound() {
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 3a3f44c6b85..dc09ba8d788 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -1316,7 +1316,7 @@ impl<T> [T] {
         assert_unsafe_precondition!(
             check_language_ub,
             "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks",
-            (n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0,
+            (n: usize = N, len: usize = self.len()) => n != 0 && len.is_multiple_of(n),
         );
         // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
         let new_len = unsafe { exact_div(self.len(), N) };
@@ -1512,7 +1512,7 @@ impl<T> [T] {
         assert_unsafe_precondition!(
             check_language_ub,
             "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks",
-            (n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0
+            (n: usize = N, len: usize = self.len()) => n != 0 && len.is_multiple_of(n)
         );
         // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
         let new_len = unsafe { exact_div(self.len(), N) };
@@ -4866,7 +4866,7 @@ impl<T> [T] {
 
         let byte_offset = elem_start.wrapping_sub(self_start);
 
-        if byte_offset % size_of::<T>() != 0 {
+        if !byte_offset.is_multiple_of(size_of::<T>()) {
             return None;
         }
 
@@ -4920,7 +4920,7 @@ impl<T> [T] {
 
         let byte_start = subslice_start.wrapping_sub(self_start);
 
-        if byte_start % size_of::<T>() != 0 {
+        if !byte_start.is_multiple_of(size_of::<T>()) {
             return None;
         }
 
diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs
index 4280f7570db..400daba16c1 100644
--- a/library/core/src/slice/sort/shared/smallsort.rs
+++ b/library/core/src/slice/sort/shared/smallsort.rs
@@ -823,7 +823,7 @@ unsafe fn bidirectional_merge<T: FreezeMarker, F: FnMut(&T, &T) -> bool>(
         let right_end = right_rev.wrapping_add(1);
 
         // Odd length, so one element is left unconsumed in the input.
-        if len % 2 != 0 {
+        if !len.is_multiple_of(2) {
             let left_nonempty = left < left_end;
             let last_src = if left_nonempty { left } else { right };
             ptr::copy_nonoverlapping(last_src, dst, 1);
diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs
index cf1df1e91a5..1edffe095a8 100644
--- a/library/core/src/slice/sort/stable/drift.rs
+++ b/library/core/src/slice/sort/stable/drift.rs
@@ -158,7 +158,7 @@ fn merge_tree_scale_factor(n: usize) -> u64 {
         panic!("Platform not supported");
     }
 
-    ((1 << 62) + n as u64 - 1) / n as u64
+    (1u64 << 62).div_ceil(n as u64)
 }
 
 // Note: merge_tree_depth output is < 64 when left < right as f*x and f*y must
@@ -182,7 +182,7 @@ fn sqrt_approx(n: usize) -> usize {
     // Finally we note that the exponentiation / division can be done directly
     // with shifts. We OR with 1 to avoid zero-checks in the integer log.
     let ilog = (n | 1).ilog2();
-    let shift = (1 + ilog) / 2;
+    let shift = ilog.div_ceil(2);
     ((1 << shift) + (n >> shift)) / 2
 }
 
diff --git a/library/core/src/str/count.rs b/library/core/src/str/count.rs
index 452403b23de..f59ad3e66b4 100644
--- a/library/core/src/str/count.rs
+++ b/library/core/src/str/count.rs
@@ -52,7 +52,7 @@ fn do_count_chars(s: &str) -> usize {
     // Check the properties of `CHUNK_SIZE` and `UNROLL_INNER` that are required
     // for correctness.
     const _: () = assert!(CHUNK_SIZE < 256);
-    const _: () = assert!(CHUNK_SIZE % UNROLL_INNER == 0);
+    const _: () = assert!(CHUNK_SIZE.is_multiple_of(UNROLL_INNER));
 
     // SAFETY: transmuting `[u8]` to `[usize]` is safe except for size
     // differences which are handled by `align_to`.
diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs
index 425c4eaee28..bcf886484ad 100644
--- a/library/core/src/str/iter.rs
+++ b/library/core/src/str/iter.rs
@@ -102,7 +102,7 @@ impl<'a> Iterator for Chars<'a> {
         // `(len + 3)` can't overflow, because we know that the `slice::Iter`
         // belongs to a slice in memory which has a maximum length of
         // `isize::MAX` (that's well below `usize::MAX`).
-        ((len + 3) / 4, Some(len))
+        (len.div_ceil(4), Some(len))
     }
 
     #[inline]
@@ -1532,11 +1532,11 @@ impl<'a> Iterator for EncodeUtf16<'a> {
         // belongs to a slice in memory which has a maximum length of
         // `isize::MAX` (that's well below `usize::MAX`)
         if self.extra == 0 {
-            ((len + 2) / 3, Some(len))
+            (len.div_ceil(3), Some(len))
         } else {
             // We're in the middle of a surrogate pair, so add the remaining
             // surrogate to the bounds.
-            ((len + 2) / 3 + 1, Some(len + 1))
+            (len.div_ceil(3) + 1, Some(len + 1))
         }
     }
 }
diff --git a/library/core/src/str/validations.rs b/library/core/src/str/validations.rs
index 8174e4ff97d..b54d6478e58 100644
--- a/library/core/src/str/validations.rs
+++ b/library/core/src/str/validations.rs
@@ -219,7 +219,7 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
             // Ascii case, try to skip forward quickly.
             // When the pointer is aligned, read 2 words of data per iteration
             // until we find a word containing a non-ascii byte.
-            if align != usize::MAX && align.wrapping_sub(index) % USIZE_BYTES == 0 {
+            if align != usize::MAX && align.wrapping_sub(index).is_multiple_of(USIZE_BYTES) {
                 let ptr = v.as_ptr();
                 while index < blocks_end {
                     // SAFETY: since `align - index` and `ascii_block_size` are
diff --git a/library/portable-simd/crates/core_simd/src/lane_count.rs b/library/portable-simd/crates/core_simd/src/lane_count.rs
index 280b27bc9bc..bbdfd5f5f3e 100644
--- a/library/portable-simd/crates/core_simd/src/lane_count.rs
+++ b/library/portable-simd/crates/core_simd/src/lane_count.rs
@@ -8,7 +8,7 @@ pub struct LaneCount<const N: usize>;
 
 impl<const N: usize> LaneCount<N> {
     /// The number of bytes in a bitmask with this many lanes.
-    pub const BITMASK_LEN: usize = (N + 7) / 8;
+    pub const BITMASK_LEN: usize = N.div_ceil(8);
 }
 
 /// Statically guarantees that a lane count is marked as supported.