about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-05-30 22:14:00 +0000
committerbors <bors@rust-lang.org>2025-05-30 22:14:00 +0000
commit0b00e6847b576656d80d7d1e845100fe1663ce0e (patch)
treeead0c5cc4b639059704e6e8d688a665ad3ad3830
parent70b3f4666e24ce22fc32f5e357dbcf85d3254e63 (diff)
parent955ebfc7d3f9af7df877e92d0264e9942f1f086c (diff)
downloadrust-0b00e6847b576656d80d7d1e845100fe1663ce0e.tar.gz
rust-0b00e6847b576656d80d7d1e845100fe1663ce0e.zip
Auto merge of #141791 - workingjubilee:rollup-qnqicy9, r=workingjubilee
Rollup of 8 pull requests

Successful merges:

 - rust-lang/rust#140825 (Add Range parameter to `BTreeMap::extract_if` and `BTreeSet::extract_if`)
 - rust-lang/rust#141077 (Fix the issue of typo of comma in arm parsing)
 - rust-lang/rust#141237 (Implement ((un)checked_)exact_div methods for integers)
 - rust-lang/rust#141494 (mir-opt: Do not transform non-int type in match_branches)
 - rust-lang/rust#141609 (core: begin deduplicating pointer docs)
 - rust-lang/rust#141768 (ci: use arm to calculate job matrix)
 - rust-lang/rust#141771 (Increase timeout for new bors try builds)
 - rust-lang/rust#141781 (Fix spans for unsafe binders)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--compiler/rustc_mir_transform/src/match_branches.rs6
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs54
-rw-r--r--compiler/rustc_resolve/src/late.rs3
-rw-r--r--library/alloc/src/collections/btree/map.rs62
-rw-r--r--library/alloc/src/collections/btree/map/tests.rs74
-rw-r--r--library/alloc/src/collections/btree/set.rs34
-rw-r--r--library/alloc/src/collections/btree/set/tests.rs12
-rw-r--r--library/alloctests/benches/btree/map.rs12
-rw-r--r--library/alloctests/benches/btree/set.rs8
-rw-r--r--library/alloctests/tests/autotraits.rs14
-rw-r--r--library/core/src/num/int_macros.rs106
-rw-r--r--library/core/src/num/uint_macros.rs102
-rw-r--r--library/core/src/ptr/const_ptr.rs63
-rw-r--r--library/core/src/ptr/docs/add.md32
-rw-r--r--library/core/src/ptr/docs/offset.md29
-rw-r--r--library/core/src/ptr/mut_ptr.rs66
-rw-r--r--rust-bors.toml3
-rw-r--r--src/tools/miri/tests/pass/btreemap.rs2
-rw-r--r--tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff29
-rw-r--r--tests/mir-opt/matches_reduce_branches.rs32
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs4
-rw-r--r--tests/ui/parser/match-arm-comma-typo-issue-140991.fixed24
-rw-r--r--tests/ui/parser/match-arm-comma-typo-issue-140991.rs24
-rw-r--r--tests/ui/parser/match-arm-comma-typo-issue-140991.stderr26
-rw-r--r--tests/ui/unsafe-binders/unused-lifetimes-2.fixed20
-rw-r--r--tests/ui/unsafe-binders/unused-lifetimes-2.rs20
-rw-r--r--tests/ui/unsafe-binders/unused-lifetimes-2.stderr16
-rw-r--r--tests/ui/unsafe-binders/unused-lifetimes.fixed20
-rw-r--r--tests/ui/unsafe-binders/unused-lifetimes.rs20
-rw-r--r--tests/ui/unsafe-binders/unused-lifetimes.stderr14
31 files changed, 716 insertions, 217 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4559246ce42..81fb39cdc56 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -46,7 +46,7 @@ jobs:
   # If you want to modify CI jobs, take a look at src/ci/github-actions/jobs.yml.
   calculate_matrix:
     name: Calculate job matrix
-    runs-on: ubuntu-24.04
+    runs-on: ubuntu-24.04-arm
     outputs:
       jobs: ${{ steps.jobs.outputs.jobs }}
       run_type: ${{ steps.jobs.outputs.run_type }}
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 8c0c3096899..5e511f1a418 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -284,12 +284,14 @@ fn can_cast(
     let v = match src_layout.ty.kind() {
         ty::Uint(_) => from_scalar.to_uint(src_layout.size),
         ty::Int(_) => from_scalar.to_int(src_layout.size) as u128,
-        _ => unreachable!("invalid int"),
+        // We can also transform the values of other integer representations (such as char),
+        // although this may not be practical in real-world scenarios.
+        _ => return false,
     };
     let size = match *cast_ty.kind() {
         ty::Int(t) => Integer::from_int_ty(&tcx, t).size(),
         ty::Uint(t) => Integer::from_uint_ty(&tcx, t).size(),
-        _ => unreachable!("invalid int"),
+        _ => return false,
     };
     let v = size.truncate(v);
     let cast_scalar = ScalarInt::try_from_uint(v, size).unwrap();
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 2a7910a6af4..1a44f4af8a6 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -3311,26 +3311,44 @@ impl<'a> Parser<'a> {
                             let sm = this.psess.source_map();
                             if let Ok(expr_lines) = sm.span_to_lines(expr_span)
                                 && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span)
-                                && arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col
                                 && expr_lines.lines.len() == 2
                             {
-                                // We check whether there's any trailing code in the parse span,
-                                // if there isn't, we very likely have the following:
-                                //
-                                // X |     &Y => "y"
-                                //   |        --    - missing comma
-                                //   |        |
-                                //   |        arrow_span
-                                // X |     &X => "x"
-                                //   |      - ^^ self.token.span
-                                //   |      |
-                                //   |      parsed until here as `"y" & X`
-                                err.span_suggestion_short(
-                                    arm_start_span.shrink_to_hi(),
-                                    "missing a comma here to end this `match` arm",
-                                    ",",
-                                    Applicability::MachineApplicable,
-                                );
+                                if arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col {
+                                    // We check whether there's any trailing code in the parse span,
+                                    // if there isn't, we very likely have the following:
+                                    //
+                                    // X |     &Y => "y"
+                                    //   |        --    - missing comma
+                                    //   |        |
+                                    //   |        arrow_span
+                                    // X |     &X => "x"
+                                    //   |      - ^^ self.token.span
+                                    //   |      |
+                                    //   |      parsed until here as `"y" & X`
+                                    err.span_suggestion_short(
+                                        arm_start_span.shrink_to_hi(),
+                                        "missing a comma here to end this `match` arm",
+                                        ",",
+                                        Applicability::MachineApplicable,
+                                    );
+                                } else if arm_start_lines.lines[0].end_col + rustc_span::CharPos(1)
+                                    == expr_lines.lines[0].end_col
+                                {
+                                    // similar to the above, but we may typo a `.` or `/` at the end of the line
+                                    let comma_span = arm_start_span
+                                        .shrink_to_hi()
+                                        .with_hi(arm_start_span.hi() + rustc_span::BytePos(1));
+                                    if let Ok(res) = sm.span_to_snippet(comma_span)
+                                        && (res == "." || res == "/")
+                                    {
+                                        err.span_suggestion_short(
+                                            comma_span,
+                                            "you might have meant to write a `,` to end this `match` arm",
+                                            ",",
+                                            Applicability::MachineApplicable,
+                                        );
+                                    }
+                                }
                             }
                         } else {
                             err.span_label(
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 4cfa079e49b..fb1534d0b27 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -934,8 +934,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
                 )
             }
             TyKind::UnsafeBinder(unsafe_binder) => {
-                // FIXME(unsafe_binder): Better span
-                let span = ty.span;
+                let span = ty.span.shrink_to_lo().to(unsafe_binder.inner_ty.span.shrink_to_lo());
                 self.with_generic_param_rib(
                     &unsafe_binder.generic_params,
                     RibKind::Normal,
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index ea81645aa64..52b98291ff9 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -1151,7 +1151,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
         K: Ord,
         F: FnMut(&K, &mut V) -> bool,
     {
-        self.extract_if(|k, v| !f(k, v)).for_each(drop);
+        self.extract_if(.., |k, v| !f(k, v)).for_each(drop);
     }
 
     /// Moves all elements from `other` into `self`, leaving `other` empty.
@@ -1397,7 +1397,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
         }
     }
 
-    /// Creates an iterator that visits all elements (key-value pairs) in
+    /// Creates an iterator that visits elements (key-value pairs) in the specified range in
     /// ascending key order and uses a closure to determine if an element
     /// should be removed.
     ///
@@ -1423,33 +1423,42 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// use std::collections::BTreeMap;
     ///
     /// let mut map: BTreeMap<i32, i32> = (0..8).map(|x| (x, x)).collect();
-    /// let evens: BTreeMap<_, _> = map.extract_if(|k, _v| k % 2 == 0).collect();
+    /// let evens: BTreeMap<_, _> = map.extract_if(.., |k, _v| k % 2 == 0).collect();
     /// let odds = map;
     /// assert_eq!(evens.keys().copied().collect::<Vec<_>>(), [0, 2, 4, 6]);
     /// assert_eq!(odds.keys().copied().collect::<Vec<_>>(), [1, 3, 5, 7]);
+    ///
+    /// let mut map: BTreeMap<i32, i32> = (0..8).map(|x| (x, x)).collect();
+    /// let low: BTreeMap<_, _> = map.extract_if(0..4, |_k, _v| true).collect();
+    /// let high = map;
+    /// assert_eq!(low.keys().copied().collect::<Vec<_>>(), [0, 1, 2, 3]);
+    /// assert_eq!(high.keys().copied().collect::<Vec<_>>(), [4, 5, 6, 7]);
     /// ```
     #[unstable(feature = "btree_extract_if", issue = "70530")]
-    pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F, A>
+    pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, R, F, A>
     where
         K: Ord,
+        R: RangeBounds<K>,
         F: FnMut(&K, &mut V) -> bool,
     {
-        let (inner, alloc) = self.extract_if_inner();
+        let (inner, alloc) = self.extract_if_inner(range);
         ExtractIf { pred, inner, alloc }
     }
 
-    pub(super) fn extract_if_inner(&mut self) -> (ExtractIfInner<'_, K, V>, A)
+    pub(super) fn extract_if_inner<R>(&mut self, range: R) -> (ExtractIfInner<'_, K, V, R>, A)
     where
         K: Ord,
+        R: RangeBounds<K>,
     {
         if let Some(root) = self.root.as_mut() {
             let (root, dormant_root) = DormantMutRef::new(root);
-            let front = root.borrow_mut().first_leaf_edge();
+            let first = root.borrow_mut().lower_bound(SearchBound::from_range(range.start_bound()));
             (
                 ExtractIfInner {
                     length: &mut self.length,
                     dormant_root: Some(dormant_root),
-                    cur_leaf_edge: Some(front),
+                    cur_leaf_edge: Some(first),
+                    range,
                 },
                 (*self.alloc).clone(),
             )
@@ -1459,6 +1468,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
                     length: &mut self.length,
                     dormant_root: None,
                     cur_leaf_edge: None,
+                    range,
                 },
                 (*self.alloc).clone(),
             )
@@ -1917,18 +1927,19 @@ pub struct ExtractIf<
     'a,
     K,
     V,
+    R,
     F,
     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global,
 > {
     pred: F,
-    inner: ExtractIfInner<'a, K, V>,
+    inner: ExtractIfInner<'a, K, V, R>,
     /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`.
     alloc: A,
 }
 
 /// Most of the implementation of ExtractIf are generic over the type
 /// of the predicate, thus also serving for BTreeSet::ExtractIf.
-pub(super) struct ExtractIfInner<'a, K, V> {
+pub(super) struct ExtractIfInner<'a, K, V, R> {
     /// Reference to the length field in the borrowed map, updated live.
     length: &'a mut usize,
     /// Buried reference to the root field in the borrowed map.
@@ -1938,10 +1949,13 @@ pub(super) struct ExtractIfInner<'a, K, V> {
     /// Empty if the map has no root, if iteration went beyond the last leaf edge,
     /// or if a panic occurred in the predicate.
     cur_leaf_edge: Option<Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>>,
+    /// Range over which iteration was requested.  We don't need the left side, but we
+    /// can't extract the right side without requiring K: Clone.
+    range: R,
 }
 
 #[unstable(feature = "btree_extract_if", issue = "70530")]
-impl<K, V, F, A> fmt::Debug for ExtractIf<'_, K, V, F, A>
+impl<K, V, R, F, A> fmt::Debug for ExtractIf<'_, K, V, R, F, A>
 where
     K: fmt::Debug,
     V: fmt::Debug,
@@ -1953,8 +1967,10 @@ where
 }
 
 #[unstable(feature = "btree_extract_if", issue = "70530")]
-impl<K, V, F, A: Allocator + Clone> Iterator for ExtractIf<'_, K, V, F, A>
+impl<K, V, R, F, A: Allocator + Clone> Iterator for ExtractIf<'_, K, V, R, F, A>
 where
+    K: PartialOrd,
+    R: RangeBounds<K>,
     F: FnMut(&K, &mut V) -> bool,
 {
     type Item = (K, V);
@@ -1968,7 +1984,7 @@ where
     }
 }
 
-impl<'a, K, V> ExtractIfInner<'a, K, V> {
+impl<'a, K, V, R> ExtractIfInner<'a, K, V, R> {
     /// Allow Debug implementations to predict the next element.
     pub(super) fn peek(&self) -> Option<(&K, &V)> {
         let edge = self.cur_leaf_edge.as_ref()?;
@@ -1978,10 +1994,22 @@ impl<'a, K, V> ExtractIfInner<'a, K, V> {
     /// Implementation of a typical `ExtractIf::next` method, given the predicate.
     pub(super) fn next<F, A: Allocator + Clone>(&mut self, pred: &mut F, alloc: A) -> Option<(K, V)>
     where
+        K: PartialOrd,
+        R: RangeBounds<K>,
         F: FnMut(&K, &mut V) -> bool,
     {
         while let Ok(mut kv) = self.cur_leaf_edge.take()?.next_kv() {
             let (k, v) = kv.kv_mut();
+
+            // On creation, we navigated directly to the left bound, so we need only check the
+            // right bound here to decide whether to stop.
+            match self.range.end_bound() {
+                Bound::Included(ref end) if (*k).le(end) => (),
+                Bound::Excluded(ref end) if (*k).lt(end) => (),
+                Bound::Unbounded => (),
+                _ => return None,
+            }
+
             if pred(k, v) {
                 *self.length -= 1;
                 let (kv, pos) = kv.remove_kv_tracking(
@@ -2013,7 +2041,13 @@ impl<'a, K, V> ExtractIfInner<'a, K, V> {
 }
 
 #[unstable(feature = "btree_extract_if", issue = "70530")]
-impl<K, V, F> FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {}
+impl<K, V, R, F> FusedIterator for ExtractIf<'_, K, V, R, F>
+where
+    K: PartialOrd,
+    R: RangeBounds<K>,
+    F: FnMut(&K, &mut V) -> bool,
+{
+}
 
 #[stable(feature = "btree_range", since = "1.17.0")]
 impl<'a, K, V> Iterator for Range<'a, K, V> {
diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs
index 5975134382e..79879d31d3d 100644
--- a/library/alloc/src/collections/btree/map/tests.rs
+++ b/library/alloc/src/collections/btree/map/tests.rs
@@ -944,7 +944,7 @@ mod test_extract_if {
     #[test]
     fn empty() {
         let mut map: BTreeMap<i32, i32> = BTreeMap::new();
-        map.extract_if(|_, _| unreachable!("there's nothing to decide on")).for_each(drop);
+        map.extract_if(.., |_, _| unreachable!("there's nothing to decide on")).for_each(drop);
         assert_eq!(map.height(), None);
         map.check();
     }
@@ -954,7 +954,7 @@ mod test_extract_if {
     fn consumed_keeping_all() {
         let pairs = (0..3).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
-        assert!(map.extract_if(|_, _| false).eq(iter::empty()));
+        assert!(map.extract_if(.., |_, _| false).eq(iter::empty()));
         map.check();
     }
 
@@ -963,18 +963,42 @@ mod test_extract_if {
     fn consumed_removing_all() {
         let pairs = (0..3).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs.clone());
-        assert!(map.extract_if(|_, _| true).eq(pairs));
+        assert!(map.extract_if(.., |_, _| true).eq(pairs));
         assert!(map.is_empty());
         map.check();
     }
 
+    #[test]
+    fn consumed_removing_some() {
+        let pairs = (0..3).map(|i| (i, i));
+        let map = BTreeMap::from_iter(pairs);
+        for x in 0..3 {
+            for y in 0..3 {
+                let mut map = map.clone();
+                assert!(map.extract_if(x..y, |_, _| true).eq((x..y).map(|i| (i, i))));
+                for i in 0..3 {
+                    assert_ne!(map.contains_key(&i), (x..y).contains(&i));
+                }
+            }
+        }
+        for x in 0..3 {
+            for y in 0..2 {
+                let mut map = map.clone();
+                assert!(map.extract_if(x..=y, |_, _| true).eq((x..=y).map(|i| (i, i))));
+                for i in 0..3 {
+                    assert_ne!(map.contains_key(&i), (x..=y).contains(&i));
+                }
+            }
+        }
+    }
+
     // Explicitly consumes the iterator and modifies values through it.
     #[test]
     fn mutating_and_keeping() {
         let pairs = (0..3).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
         assert!(
-            map.extract_if(|_, v| {
+            map.extract_if(.., |_, v| {
                 *v += 6;
                 false
             })
@@ -991,7 +1015,7 @@ mod test_extract_if {
         let pairs = (0..3).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
         assert!(
-            map.extract_if(|_, v| {
+            map.extract_if(.., |_, v| {
                 *v += 6;
                 true
             })
@@ -1005,7 +1029,7 @@ mod test_extract_if {
     fn underfull_keeping_all() {
         let pairs = (0..3).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
-        map.extract_if(|_, _| false).for_each(drop);
+        map.extract_if(.., |_, _| false).for_each(drop);
         assert!(map.keys().copied().eq(0..3));
         map.check();
     }
@@ -1015,7 +1039,7 @@ mod test_extract_if {
         let pairs = (0..3).map(|i| (i, i));
         for doomed in 0..3 {
             let mut map = BTreeMap::from_iter(pairs.clone());
-            map.extract_if(|i, _| *i == doomed).for_each(drop);
+            map.extract_if(.., |i, _| *i == doomed).for_each(drop);
             assert_eq!(map.len(), 2);
             map.check();
         }
@@ -1026,7 +1050,7 @@ mod test_extract_if {
         let pairs = (0..3).map(|i| (i, i));
         for sacred in 0..3 {
             let mut map = BTreeMap::from_iter(pairs.clone());
-            map.extract_if(|i, _| *i != sacred).for_each(drop);
+            map.extract_if(.., |i, _| *i != sacred).for_each(drop);
             assert!(map.keys().copied().eq(sacred..=sacred));
             map.check();
         }
@@ -1036,7 +1060,7 @@ mod test_extract_if {
     fn underfull_removing_all() {
         let pairs = (0..3).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
-        map.extract_if(|_, _| true).for_each(drop);
+        map.extract_if(.., |_, _| true).for_each(drop);
         assert!(map.is_empty());
         map.check();
     }
@@ -1045,7 +1069,7 @@ mod test_extract_if {
     fn height_0_keeping_all() {
         let pairs = (0..node::CAPACITY).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
-        map.extract_if(|_, _| false).for_each(drop);
+        map.extract_if(.., |_, _| false).for_each(drop);
         assert!(map.keys().copied().eq(0..node::CAPACITY));
         map.check();
     }
@@ -1055,7 +1079,7 @@ mod test_extract_if {
         let pairs = (0..node::CAPACITY).map(|i| (i, i));
         for doomed in 0..node::CAPACITY {
             let mut map = BTreeMap::from_iter(pairs.clone());
-            map.extract_if(|i, _| *i == doomed).for_each(drop);
+            map.extract_if(.., |i, _| *i == doomed).for_each(drop);
             assert_eq!(map.len(), node::CAPACITY - 1);
             map.check();
         }
@@ -1066,7 +1090,7 @@ mod test_extract_if {
         let pairs = (0..node::CAPACITY).map(|i| (i, i));
         for sacred in 0..node::CAPACITY {
             let mut map = BTreeMap::from_iter(pairs.clone());
-            map.extract_if(|i, _| *i != sacred).for_each(drop);
+            map.extract_if(.., |i, _| *i != sacred).for_each(drop);
             assert!(map.keys().copied().eq(sacred..=sacred));
             map.check();
         }
@@ -1076,7 +1100,7 @@ mod test_extract_if {
     fn height_0_removing_all() {
         let pairs = (0..node::CAPACITY).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
-        map.extract_if(|_, _| true).for_each(drop);
+        map.extract_if(.., |_, _| true).for_each(drop);
         assert!(map.is_empty());
         map.check();
     }
@@ -1084,7 +1108,7 @@ mod test_extract_if {
     #[test]
     fn height_0_keeping_half() {
         let mut map = BTreeMap::from_iter((0..16).map(|i| (i, i)));
-        assert_eq!(map.extract_if(|i, _| *i % 2 == 0).count(), 8);
+        assert_eq!(map.extract_if(.., |i, _| *i % 2 == 0).count(), 8);
         assert_eq!(map.len(), 8);
         map.check();
     }
@@ -1093,7 +1117,7 @@ mod test_extract_if {
     fn height_1_removing_all() {
         let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
-        map.extract_if(|_, _| true).for_each(drop);
+        map.extract_if(.., |_, _| true).for_each(drop);
         assert!(map.is_empty());
         map.check();
     }
@@ -1103,7 +1127,7 @@ mod test_extract_if {
         let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i));
         for doomed in 0..MIN_INSERTS_HEIGHT_1 {
             let mut map = BTreeMap::from_iter(pairs.clone());
-            map.extract_if(|i, _| *i == doomed).for_each(drop);
+            map.extract_if(.., |i, _| *i == doomed).for_each(drop);
             assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1 - 1);
             map.check();
         }
@@ -1114,7 +1138,7 @@ mod test_extract_if {
         let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i));
         for sacred in 0..MIN_INSERTS_HEIGHT_1 {
             let mut map = BTreeMap::from_iter(pairs.clone());
-            map.extract_if(|i, _| *i != sacred).for_each(drop);
+            map.extract_if(.., |i, _| *i != sacred).for_each(drop);
             assert!(map.keys().copied().eq(sacred..=sacred));
             map.check();
         }
@@ -1125,7 +1149,7 @@ mod test_extract_if {
         let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i));
         for doomed in (0..MIN_INSERTS_HEIGHT_2).step_by(12) {
             let mut map = BTreeMap::from_iter(pairs.clone());
-            map.extract_if(|i, _| *i == doomed).for_each(drop);
+            map.extract_if(.., |i, _| *i == doomed).for_each(drop);
             assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1);
             map.check();
         }
@@ -1136,7 +1160,7 @@ mod test_extract_if {
         let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i));
         for sacred in (0..MIN_INSERTS_HEIGHT_2).step_by(12) {
             let mut map = BTreeMap::from_iter(pairs.clone());
-            map.extract_if(|i, _| *i != sacred).for_each(drop);
+            map.extract_if(.., |i, _| *i != sacred).for_each(drop);
             assert!(map.keys().copied().eq(sacred..=sacred));
             map.check();
         }
@@ -1146,7 +1170,7 @@ mod test_extract_if {
     fn height_2_removing_all() {
         let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i));
         let mut map = BTreeMap::from_iter(pairs);
-        map.extract_if(|_, _| true).for_each(drop);
+        map.extract_if(.., |_, _| true).for_each(drop);
         assert!(map.is_empty());
         map.check();
     }
@@ -1162,7 +1186,7 @@ mod test_extract_if {
         map.insert(b.spawn(Panic::InDrop), ());
         map.insert(c.spawn(Panic::Never), ());
 
-        catch_unwind(move || map.extract_if(|dummy, _| dummy.query(true)).for_each(drop))
+        catch_unwind(move || map.extract_if(.., |dummy, _| dummy.query(true)).for_each(drop))
             .unwrap_err();
 
         assert_eq!(a.queried(), 1);
@@ -1185,7 +1209,7 @@ mod test_extract_if {
         map.insert(c.spawn(Panic::InQuery), ());
 
         catch_unwind(AssertUnwindSafe(|| {
-            map.extract_if(|dummy, _| dummy.query(true)).for_each(drop)
+            map.extract_if(.., |dummy, _| dummy.query(true)).for_each(drop)
         }))
         .unwrap_err();
 
@@ -1214,7 +1238,7 @@ mod test_extract_if {
         map.insert(c.spawn(Panic::InQuery), ());
 
         {
-            let mut it = map.extract_if(|dummy, _| dummy.query(true));
+            let mut it = map.extract_if(.., |dummy, _| dummy.query(true));
             catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
             // Iterator behavior after a panic is explicitly unspecified,
             // so this is just the current implementation:
@@ -1658,7 +1682,7 @@ fn assert_sync() {
     }
 
     fn extract_if<T: Sync + Ord>(v: &mut BTreeMap<T, T>) -> impl Sync + '_ {
-        v.extract_if(|_, _| false)
+        v.extract_if(.., |_, _| false)
     }
 
     fn iter<T: Sync>(v: &BTreeMap<T, T>) -> impl Sync + '_ {
@@ -1727,7 +1751,7 @@ fn assert_send() {
     }
 
     fn extract_if<T: Send + Ord>(v: &mut BTreeMap<T, T>) -> impl Send + '_ {
-        v.extract_if(|_, _| false)
+        v.extract_if(.., |_, _| false)
     }
 
     fn iter<T: Send + Sync>(v: &BTreeMap<T, T>) -> impl Send + '_ {
diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs
index 343934680b8..780bd8b0dd1 100644
--- a/library/alloc/src/collections/btree/set.rs
+++ b/library/alloc/src/collections/btree/set.rs
@@ -1109,7 +1109,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
         T: Ord,
         F: FnMut(&T) -> bool,
     {
-        self.extract_if(|v| !f(v)).for_each(drop);
+        self.extract_if(.., |v| !f(v)).for_each(drop);
     }
 
     /// Moves all elements from `other` into `self`, leaving `other` empty.
@@ -1187,7 +1187,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
         BTreeSet { map: self.map.split_off(value) }
     }
 
-    /// Creates an iterator that visits all elements in ascending order and
+    /// Creates an iterator that visits elements in the specified range in ascending order and
     /// uses a closure to determine if an element should be removed.
     ///
     /// If the closure returns `true`, the element is removed from the set and
@@ -1208,18 +1208,25 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
     /// use std::collections::BTreeSet;
     ///
     /// let mut set: BTreeSet<i32> = (0..8).collect();
-    /// let evens: BTreeSet<_> = set.extract_if(|v| v % 2 == 0).collect();
+    /// let evens: BTreeSet<_> = set.extract_if(.., |v| v % 2 == 0).collect();
     /// let odds = set;
     /// assert_eq!(evens.into_iter().collect::<Vec<_>>(), vec![0, 2, 4, 6]);
     /// assert_eq!(odds.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7]);
+    ///
+    /// let mut map: BTreeSet<i32> = (0..8).collect();
+    /// let low: BTreeSet<_> = map.extract_if(0..4, |_v| true).collect();
+    /// let high = map;
+    /// assert_eq!(low.into_iter().collect::<Vec<_>>(), [0, 1, 2, 3]);
+    /// assert_eq!(high.into_iter().collect::<Vec<_>>(), [4, 5, 6, 7]);
     /// ```
     #[unstable(feature = "btree_extract_if", issue = "70530")]
-    pub fn extract_if<'a, F>(&'a mut self, pred: F) -> ExtractIf<'a, T, F, A>
+    pub fn extract_if<'a, F, R>(&'a mut self, range: R, pred: F) -> ExtractIf<'a, T, R, F, A>
     where
         T: Ord,
+        R: RangeBounds<T>,
         F: 'a + FnMut(&T) -> bool,
     {
-        let (inner, alloc) = self.map.extract_if_inner();
+        let (inner, alloc) = self.map.extract_if_inner(range);
         ExtractIf { pred, inner, alloc }
     }
 
@@ -1554,17 +1561,18 @@ impl<'a, T, A: Allocator + Clone> IntoIterator for &'a BTreeSet<T, A> {
 pub struct ExtractIf<
     'a,
     T,
+    R,
     F,
     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global,
 > {
     pred: F,
-    inner: super::map::ExtractIfInner<'a, T, SetValZST>,
+    inner: super::map::ExtractIfInner<'a, T, SetValZST, R>,
     /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`.
     alloc: A,
 }
 
 #[unstable(feature = "btree_extract_if", issue = "70530")]
-impl<T, F, A> fmt::Debug for ExtractIf<'_, T, F, A>
+impl<T, R, F, A> fmt::Debug for ExtractIf<'_, T, R, F, A>
 where
     T: fmt::Debug,
     A: Allocator + Clone,
@@ -1577,8 +1585,10 @@ where
 }
 
 #[unstable(feature = "btree_extract_if", issue = "70530")]
-impl<'a, T, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, F, A>
+impl<'a, T, R, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, R, F, A>
 where
+    T: PartialOrd,
+    R: RangeBounds<T>,
     F: 'a + FnMut(&T) -> bool,
 {
     type Item = T;
@@ -1595,7 +1605,13 @@ where
 }
 
 #[unstable(feature = "btree_extract_if", issue = "70530")]
-impl<T, F, A: Allocator + Clone> FusedIterator for ExtractIf<'_, T, F, A> where F: FnMut(&T) -> bool {}
+impl<T, R, F, A: Allocator + Clone> FusedIterator for ExtractIf<'_, T, R, F, A>
+where
+    T: PartialOrd,
+    R: RangeBounds<T>,
+    F: FnMut(&T) -> bool,
+{
+}
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Ord, A: Allocator + Clone> Extend<T> for BTreeSet<T, A> {
diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs
index d538ef707eb..85c9a98c461 100644
--- a/library/alloc/src/collections/btree/set/tests.rs
+++ b/library/alloc/src/collections/btree/set/tests.rs
@@ -368,8 +368,8 @@ fn test_extract_if() {
     let mut x = BTreeSet::from([1]);
     let mut y = BTreeSet::from([1]);
 
-    x.extract_if(|_| true).for_each(drop);
-    y.extract_if(|_| false).for_each(drop);
+    x.extract_if(.., |_| true).for_each(drop);
+    y.extract_if(.., |_| false).for_each(drop);
     assert_eq!(x.len(), 0);
     assert_eq!(y.len(), 1);
 }
@@ -385,7 +385,7 @@ fn test_extract_if_drop_panic_leak() {
     set.insert(b.spawn(Panic::InDrop));
     set.insert(c.spawn(Panic::Never));
 
-    catch_unwind(move || set.extract_if(|dummy| dummy.query(true)).for_each(drop)).ok();
+    catch_unwind(move || set.extract_if(.., |dummy| dummy.query(true)).for_each(drop)).ok();
 
     assert_eq!(a.queried(), 1);
     assert_eq!(b.queried(), 1);
@@ -406,7 +406,7 @@ fn test_extract_if_pred_panic_leak() {
     set.insert(b.spawn(Panic::InQuery));
     set.insert(c.spawn(Panic::InQuery));
 
-    catch_unwind(AssertUnwindSafe(|| set.extract_if(|dummy| dummy.query(true)).for_each(drop)))
+    catch_unwind(AssertUnwindSafe(|| set.extract_if(.., |dummy| dummy.query(true)).for_each(drop)))
         .ok();
 
     assert_eq!(a.queried(), 1);
@@ -605,7 +605,7 @@ fn assert_sync() {
     }
 
     fn extract_if<T: Sync + Ord>(v: &mut BTreeSet<T>) -> impl Sync + '_ {
-        v.extract_if(|_| false)
+        v.extract_if(.., |_| false)
     }
 
     fn difference<T: Sync + Ord>(v: &BTreeSet<T>) -> impl Sync + '_ {
@@ -644,7 +644,7 @@ fn assert_send() {
     }
 
     fn extract_if<T: Send + Ord>(v: &mut BTreeSet<T>) -> impl Send + '_ {
-        v.extract_if(|_| false)
+        v.extract_if(.., |_| false)
     }
 
     fn difference<T: Send + Sync + Ord>(v: &BTreeSet<T>) -> impl Send + '_ {
diff --git a/library/alloctests/benches/btree/map.rs b/library/alloctests/benches/btree/map.rs
index 20f02dc3a96..778065fd965 100644
--- a/library/alloctests/benches/btree/map.rs
+++ b/library/alloctests/benches/btree/map.rs
@@ -386,7 +386,7 @@ pub fn clone_slim_100_and_clear(b: &mut Bencher) {
 #[bench]
 pub fn clone_slim_100_and_drain_all(b: &mut Bencher) {
     let src = slim_map(100);
-    b.iter(|| src.clone().extract_if(|_, _| true).count())
+    b.iter(|| src.clone().extract_if(.., |_, _| true).count())
 }
 
 #[bench]
@@ -394,7 +394,7 @@ pub fn clone_slim_100_and_drain_half(b: &mut Bencher) {
     let src = slim_map(100);
     b.iter(|| {
         let mut map = src.clone();
-        assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 100 / 2);
+        assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 100 / 2);
         assert_eq!(map.len(), 100 / 2);
     })
 }
@@ -457,7 +457,7 @@ pub fn clone_slim_10k_and_clear(b: &mut Bencher) {
 #[bench]
 pub fn clone_slim_10k_and_drain_all(b: &mut Bencher) {
     let src = slim_map(10_000);
-    b.iter(|| src.clone().extract_if(|_, _| true).count())
+    b.iter(|| src.clone().extract_if(.., |_, _| true).count())
 }
 
 #[bench]
@@ -465,7 +465,7 @@ pub fn clone_slim_10k_and_drain_half(b: &mut Bencher) {
     let src = slim_map(10_000);
     b.iter(|| {
         let mut map = src.clone();
-        assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 10_000 / 2);
+        assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 10_000 / 2);
         assert_eq!(map.len(), 10_000 / 2);
     })
 }
@@ -528,7 +528,7 @@ pub fn clone_fat_val_100_and_clear(b: &mut Bencher) {
 #[bench]
 pub fn clone_fat_val_100_and_drain_all(b: &mut Bencher) {
     let src = fat_val_map(100);
-    b.iter(|| src.clone().extract_if(|_, _| true).count())
+    b.iter(|| src.clone().extract_if(.., |_, _| true).count())
 }
 
 #[bench]
@@ -536,7 +536,7 @@ pub fn clone_fat_val_100_and_drain_half(b: &mut Bencher) {
     let src = fat_val_map(100);
     b.iter(|| {
         let mut map = src.clone();
-        assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 100 / 2);
+        assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 100 / 2);
         assert_eq!(map.len(), 100 / 2);
     })
 }
diff --git a/library/alloctests/benches/btree/set.rs b/library/alloctests/benches/btree/set.rs
index 5aa395b4d52..027c86a89a5 100644
--- a/library/alloctests/benches/btree/set.rs
+++ b/library/alloctests/benches/btree/set.rs
@@ -69,7 +69,7 @@ pub fn clone_100_and_clear(b: &mut Bencher) {
 #[bench]
 pub fn clone_100_and_drain_all(b: &mut Bencher) {
     let src = slim_set(100);
-    b.iter(|| src.clone().extract_if(|_| true).count())
+    b.iter(|| src.clone().extract_if(.., |_| true).count())
 }
 
 #[bench]
@@ -77,7 +77,7 @@ pub fn clone_100_and_drain_half(b: &mut Bencher) {
     let src = slim_set(100);
     b.iter(|| {
         let mut set = src.clone();
-        assert_eq!(set.extract_if(|i| i % 2 == 0).count(), 100 / 2);
+        assert_eq!(set.extract_if(.., |i| i % 2 == 0).count(), 100 / 2);
         assert_eq!(set.len(), 100 / 2);
     })
 }
@@ -140,7 +140,7 @@ pub fn clone_10k_and_clear(b: &mut Bencher) {
 #[bench]
 pub fn clone_10k_and_drain_all(b: &mut Bencher) {
     let src = slim_set(10_000);
-    b.iter(|| src.clone().extract_if(|_| true).count())
+    b.iter(|| src.clone().extract_if(.., |_| true).count())
 }
 
 #[bench]
@@ -148,7 +148,7 @@ pub fn clone_10k_and_drain_half(b: &mut Bencher) {
     let src = slim_set(10_000);
     b.iter(|| {
         let mut set = src.clone();
-        assert_eq!(set.extract_if(|i| i % 2 == 0).count(), 10_000 / 2);
+        assert_eq!(set.extract_if(.., |i| i % 2 == 0).count(), 10_000 / 2);
         assert_eq!(set.len(), 10_000 / 2);
     })
 }
diff --git a/library/alloctests/tests/autotraits.rs b/library/alloctests/tests/autotraits.rs
index 6b82deeac8a..ad0a1038596 100644
--- a/library/alloctests/tests/autotraits.rs
+++ b/library/alloctests/tests/autotraits.rs
@@ -1,3 +1,5 @@
+use std::ops::Range;
+
 fn require_sync<T: Sync>(_: T) {}
 fn require_send_sync<T: Send + Sync>(_: T) {}
 
@@ -55,7 +57,13 @@ fn test_btree_map() {
 
     require_send_sync(async {
         let _v = None::<
-            alloc::collections::btree_map::ExtractIf<'_, &u32, &u32, fn(&&u32, &mut &u32) -> bool>,
+            alloc::collections::btree_map::ExtractIf<
+                '_,
+                &u32,
+                &u32,
+                Range<u32>,
+                fn(&&u32, &mut &u32) -> bool,
+            >,
         >;
         async {}.await;
     });
@@ -144,7 +152,9 @@ fn test_btree_set() {
     });
 
     require_send_sync(async {
-        let _v = None::<alloc::collections::btree_set::ExtractIf<'_, &u32, fn(&&u32) -> bool>>;
+        let _v = None::<
+            alloc::collections::btree_set::ExtractIf<'_, &u32, Range<u32>, fn(&&u32) -> bool>,
+        >;
         async {}.await;
     });
 
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index f320a194271..65560f63c18 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -4,7 +4,7 @@ macro_rules! int_impl {
         ActualT = $ActualT:ident,
         UnsignedT = $UnsignedT:ty,
 
-        // There are all for use *only* in doc comments.
+        // These are all for use *only* in doc comments.
         // As such, they're all passed as literals -- passing them as a string
         // literal is fine if they need to be multiple code tokens.
         // In non-comments, use the associated constants rather than these.
@@ -1018,6 +1018,110 @@ macro_rules! int_impl {
             if b { overflow_panic::div() } else { a }
         }
 
+        /// Checked integer division without remainder. Computes `self / rhs`,
+        /// returning `None` if `rhs == 0`, the division results in overflow,
+        /// or `self % rhs != 0`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// #![feature(exact_div)]
+        #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_exact_div(-1), Some(", stringify!($Max), "));")]
+        #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").checked_exact_div(2), None);")]
+        #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_exact_div(-1), None);")]
+        #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_exact_div(0), None);")]
+        /// ```
+        #[unstable(
+            feature = "exact_div",
+            issue = "139911",
+        )]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const fn checked_exact_div(self, rhs: Self) -> Option<Self> {
+            if intrinsics::unlikely(rhs == 0 || ((self == Self::MIN) && (rhs == -1))) {
+                None
+            } else {
+                // SAFETY: division by zero and overflow are checked above
+                unsafe {
+                    if intrinsics::unlikely(intrinsics::unchecked_rem(self, rhs) != 0) {
+                        None
+                    } else {
+                        Some(intrinsics::exact_div(self, rhs))
+                    }
+                }
+            }
+        }
+
+        /// Checked integer division without remainder. Computes `self / rhs`.
+        ///
+        /// # Panics
+        ///
+        /// This function will panic  if `rhs == 0`, the division results in overflow,
+        /// or `self % rhs != 0`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// #![feature(exact_div)]
+        #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")]
+        #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")]
+        #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).exact_div(-1), ", stringify!($Max), ");")]
+        /// ```
+        ///
+        /// ```should_panic
+        /// #![feature(exact_div)]
+        #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")]
+        /// ```
+        /// ```should_panic
+        /// #![feature(exact_div)]
+        #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.exact_div(-1);")]
+        /// ```
+        #[unstable(
+            feature = "exact_div",
+            issue = "139911",
+        )]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const fn exact_div(self, rhs: Self) -> Self {
+            match self.checked_exact_div(rhs) {
+                Some(x) => x,
+                None => panic!("Failed to divide without remainder"),
+            }
+        }
+
+        /// Unchecked integer division without remainder. Computes `self / rhs`.
+        ///
+        /// # Safety
+        ///
+        /// This results in undefined behavior when `rhs == 0`, `self % rhs != 0`, or
+        #[doc = concat!("`self == ", stringify!($SelfT), "::MIN && rhs == -1`,")]
+        /// i.e. when [`checked_exact_div`](Self::checked_exact_div) would return `None`.
+        #[unstable(
+            feature = "exact_div",
+            issue = "139911",
+        )]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const unsafe fn unchecked_exact_div(self, rhs: Self) -> Self {
+            assert_unsafe_precondition!(
+                check_language_ub,
+                concat!(stringify!($SelfT), "::unchecked_exact_div cannot overflow, divide by zero, or leave a remainder"),
+                (
+                    lhs: $SelfT = self,
+                    rhs: $SelfT = rhs,
+                ) => rhs > 0 && lhs % rhs == 0 && (lhs != <$SelfT>::MIN || rhs != -1),
+            );
+            // SAFETY: Same precondition
+            unsafe { intrinsics::exact_div(self, rhs) }
+        }
+
         /// Checked integer remainder. Computes `self % rhs`, returning `None` if
         /// `rhs == 0` or the division results in overflow.
         ///
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 10597854ff8..5f82e6af86b 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -1110,6 +1110,108 @@ macro_rules! uint_impl {
             self / rhs
         }
 
+        /// Checked integer division without remainder. Computes `self / rhs`.
+        ///
+        /// # Panics
+        ///
+        /// This function will panic  if `rhs == 0` or `self % rhs != 0`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// #![feature(exact_div)]
+        #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")]
+        #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")]
+        /// ```
+        ///
+        /// ```should_panic
+        /// #![feature(exact_div)]
+        #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")]
+        /// ```
+        #[unstable(
+            feature = "exact_div",
+            issue = "139911",
+        )]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const fn checked_exact_div(self, rhs: Self) -> Option<Self> {
+            if intrinsics::unlikely(rhs == 0) {
+                None
+            } else {
+                // SAFETY: division by zero is checked above
+                unsafe {
+                    if intrinsics::unlikely(intrinsics::unchecked_rem(self, rhs) != 0) {
+                        None
+                    } else {
+                        Some(intrinsics::exact_div(self, rhs))
+                    }
+                }
+            }
+        }
+
+        /// Checked integer division without remainder. Computes `self / rhs`.
+        ///
+        /// # Panics
+        ///
+        /// This function will panic  if `rhs == 0` or `self % rhs != 0`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// #![feature(exact_div)]
+        #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")]
+        #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")]
+        /// ```
+        ///
+        /// ```should_panic
+        /// #![feature(exact_div)]
+        #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")]
+        /// ```
+        #[unstable(
+            feature = "exact_div",
+            issue = "139911",
+        )]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const fn exact_div(self, rhs: Self) -> Self {
+            match self.checked_exact_div(rhs) {
+                Some(x) => x,
+                None => panic!("Failed to divide without remainder"),
+            }
+        }
+
+        /// Unchecked integer division without remainder. Computes `self / rhs`.
+        ///
+        /// # Safety
+        ///
+        /// This results in undefined behavior when `rhs == 0` or `self % rhs != 0`,
+        /// i.e. when [`checked_exact_div`](Self::checked_exact_div) would return `None`.
+        #[unstable(
+            feature = "exact_div",
+            issue = "139911",
+        )]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const unsafe fn unchecked_exact_div(self, rhs: Self) -> Self {
+            assert_unsafe_precondition!(
+                check_language_ub,
+                concat!(stringify!($SelfT), "::unchecked_exact_div divide by zero or leave a remainder"),
+                (
+                    lhs: $SelfT = self,
+                    rhs: $SelfT = rhs,
+                ) => rhs > 0 && lhs % rhs == 0,
+            );
+            // SAFETY: Same precondition
+            unsafe { intrinsics::exact_div(self, rhs) }
+        }
+
         /// Checked integer remainder. Computes `self % rhs`, returning `None`
         /// if `rhs == 0`.
         ///
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 31267bfb1f9..f94737138dc 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -397,35 +397,7 @@ impl<T: ?Sized> *const T {
         if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit<T>) }) }
     }
 
-    /// Adds a signed offset to a pointer.
-    ///
-    /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
-    /// offset of `3 * size_of::<T>()` bytes.
-    ///
-    /// # Safety
-    ///
-    /// If any of the following conditions are violated, the result is Undefined Behavior:
-    ///
-    /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
-    ///   "wrapping around"), must fit in an `isize`.
-    ///
-    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
-    ///   [allocated object], and the entire memory range between `self` and the result must be in
-    ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
-    ///   of the address space. Note that "range" here refers to a half-open range as usual in Rust,
-    ///   i.e., `self..result` for non-negative offsets and `result..self` for negative offsets.
-    ///
-    /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
-    /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
-    /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
-    /// safe.
-    ///
-    /// Consider using [`wrapping_offset`] instead if these constraints are
-    /// difficult to satisfy. The only advantage of this method is that it
-    /// enables more aggressive compiler optimizations.
-    ///
-    /// [`wrapping_offset`]: #method.wrapping_offset
-    /// [allocated object]: crate::ptr#allocated-object
+    #[doc = include_str!("./docs/offset.md")]
     ///
     /// # Examples
     ///
@@ -905,38 +877,7 @@ impl<T: ?Sized> *const T {
         }
     }
 
-    /// Adds an unsigned offset to a pointer.
-    ///
-    /// This can only move the pointer forward (or not move it). If you need to move forward or
-    /// backward depending on the value, then you might want [`offset`](#method.offset) instead
-    /// which takes a signed offset.
-    ///
-    /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
-    /// offset of `3 * size_of::<T>()` bytes.
-    ///
-    /// # Safety
-    ///
-    /// If any of the following conditions are violated, the result is Undefined Behavior:
-    ///
-    /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
-    ///   "wrapping around"), must fit in an `isize`.
-    ///
-    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
-    ///   [allocated object], and the entire memory range between `self` and the result must be in
-    ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
-    ///   of the address space.
-    ///
-    /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
-    /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
-    /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
-    /// safe.
-    ///
-    /// Consider using [`wrapping_add`] instead if these constraints are
-    /// difficult to satisfy. The only advantage of this method is that it
-    /// enables more aggressive compiler optimizations.
-    ///
-    /// [`wrapping_add`]: #method.wrapping_add
-    /// [allocated object]: crate::ptr#allocated-object
+    #[doc = include_str!("./docs/add.md")]
     ///
     /// # Examples
     ///
diff --git a/library/core/src/ptr/docs/add.md b/library/core/src/ptr/docs/add.md
new file mode 100644
index 00000000000..555dc11c1bb
--- /dev/null
+++ b/library/core/src/ptr/docs/add.md
@@ -0,0 +1,32 @@
+Adds an unsigned offset to a pointer.
+
+This can only move the pointer forward (or not move it). If you need to move forward or
+backward depending on the value, then you might want [`offset`](#method.offset) instead
+which takes a signed offset.
+
+`count` is in units of T; e.g., a `count` of 3 represents a pointer
+offset of `3 * size_of::<T>()` bytes.
+
+# Safety
+
+If any of the following conditions are violated, the result is Undefined Behavior:
+
+* The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
+"wrapping around"), must fit in an `isize`.
+
+* If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
+[allocated object], and the entire memory range between `self` and the result must be in
+bounds of that allocated object. In particular, this range must not "wrap around" the edge
+of the address space.
+
+Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
+stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
+This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
+safe.
+
+Consider using [`wrapping_add`] instead if these constraints are
+difficult to satisfy. The only advantage of this method is that it
+enables more aggressive compiler optimizations.
+
+[`wrapping_add`]: #method.wrapping_add
+[allocated object]: crate::ptr#allocated-object
diff --git a/library/core/src/ptr/docs/offset.md b/library/core/src/ptr/docs/offset.md
new file mode 100644
index 00000000000..6e431e054b0
--- /dev/null
+++ b/library/core/src/ptr/docs/offset.md
@@ -0,0 +1,29 @@
+Adds a signed offset to a pointer.
+
+`count` is in units of T; e.g., a `count` of 3 represents a pointer
+offset of `3 * size_of::<T>()` bytes.
+
+# Safety
+
+If any of the following conditions are violated, the result is Undefined Behavior:
+
+* The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
+"wrapping around"), must fit in an `isize`.
+
+* If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
+[allocated object], and the entire memory range between `self` and the result must be in
+bounds of that allocated object. In particular, this range must not "wrap around" the edge
+of the address space. Note that "range" here refers to a half-open range as usual in Rust,
+i.e., `self..result` for non-negative offsets and `result..self` for negative offsets.
+
+Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
+stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
+This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
+safe.
+
+Consider using [`wrapping_offset`] instead if these constraints are
+difficult to satisfy. The only advantage of this method is that it
+enables more aggressive compiler optimizations.
+
+[`wrapping_offset`]: #method.wrapping_offset
+[allocated object]: crate::ptr#allocated-object
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index b1b3379d741..040d91e9124 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -394,34 +394,7 @@ impl<T: ?Sized> *mut T {
         if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit<T>) }) }
     }
 
-    /// Adds a signed offset to a pointer.
-    ///
-    /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
-    /// offset of `3 * size_of::<T>()` bytes.
-    ///
-    /// # Safety
-    ///
-    /// If any of the following conditions are violated, the result is Undefined Behavior:
-    ///
-    /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
-    ///   "wrapping around"), must fit in an `isize`.
-    ///
-    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
-    ///   [allocated object], and the entire memory range between `self` and the result must be in
-    ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
-    ///   of the address space.
-    ///
-    /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
-    /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
-    /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
-    /// safe.
-    ///
-    /// Consider using [`wrapping_offset`] instead if these constraints are
-    /// difficult to satisfy. The only advantage of this method is that it
-    /// enables more aggressive compiler optimizations.
-    ///
-    /// [`wrapping_offset`]: #method.wrapping_offset
-    /// [allocated object]: crate::ptr#allocated-object
+    #[doc = include_str!("./docs/offset.md")]
     ///
     /// # Examples
     ///
@@ -996,44 +969,13 @@ impl<T: ?Sized> *mut T {
         unsafe { (self as *const T).byte_offset_from_unsigned(origin) }
     }
 
-    /// Adds an unsigned offset to a pointer.
-    ///
-    /// This can only move the pointer forward (or not move it). If you need to move forward or
-    /// backward depending on the value, then you might want [`offset`](#method.offset) instead
-    /// which takes a signed offset.
-    ///
-    /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
-    /// offset of `3 * size_of::<T>()` bytes.
-    ///
-    /// # Safety
-    ///
-    /// If any of the following conditions are violated, the result is Undefined Behavior:
-    ///
-    /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
-    ///   "wrapping around"), must fit in an `isize`.
-    ///
-    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
-    ///   [allocated object], and the entire memory range between `self` and the result must be in
-    ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
-    ///   of the address space.
-    ///
-    /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
-    /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
-    /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
-    /// safe.
-    ///
-    /// Consider using [`wrapping_add`] instead if these constraints are
-    /// difficult to satisfy. The only advantage of this method is that it
-    /// enables more aggressive compiler optimizations.
-    ///
-    /// [`wrapping_add`]: #method.wrapping_add
-    /// [allocated object]: crate::ptr#allocated-object
+    #[doc = include_str!("./docs/add.md")]
     ///
     /// # Examples
     ///
     /// ```
-    /// let s: &str = "123";
-    /// let ptr: *const u8 = s.as_ptr();
+    /// let mut s: String = "123".to_string();
+    /// let ptr: *mut u8 = s.as_mut_ptr();
     ///
     /// unsafe {
     ///     assert_eq!('2', *ptr.add(1) as char);
diff --git a/rust-bors.toml b/rust-bors.toml
index f27eb239367..fbfaa980f05 100644
--- a/rust-bors.toml
+++ b/rust-bors.toml
@@ -1 +1,2 @@
-timeout = 14400
+# 6 hours timeout for CI builds
+timeout = 21600
diff --git a/src/tools/miri/tests/pass/btreemap.rs b/src/tools/miri/tests/pass/btreemap.rs
index 1213f81a6f1..1d65e69bf72 100644
--- a/src/tools/miri/tests/pass/btreemap.rs
+++ b/src/tools/miri/tests/pass/btreemap.rs
@@ -50,7 +50,7 @@ pub fn main() {
     test_all_refs(&mut 13, b.values_mut());
 
     // Test forgetting the extractor.
-    let mut d = b.extract_if(|_, i| *i < 30);
+    let mut d = b.extract_if(.., |_, i| *i < 30);
     d.next().unwrap();
     mem::forget(d);
 }
diff --git a/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff
new file mode 100644
index 00000000000..81e900a34c0
--- /dev/null
+++ b/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff
@@ -0,0 +1,29 @@
+- // MIR for `match_non_int_failed` before MatchBranchSimplification
++ // MIR for `match_non_int_failed` after MatchBranchSimplification
+  
+  fn match_non_int_failed(_1: char) -> u8 {
+      let mut _0: u8;
+  
+      bb0: {
+          switchInt(copy _1) -> [97: bb1, 98: bb2, otherwise: bb3];
+      }
+  
+      bb1: {
+          _0 = const 97_u8;
+          goto -> bb4;
+      }
+  
+      bb2: {
+          _0 = const 98_u8;
+          goto -> bb4;
+      }
+  
+      bb3: {
+          unreachable;
+      }
+  
+      bb4: {
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs
index 00131b0116d..89ef3bfb308 100644
--- a/tests/mir-opt/matches_reduce_branches.rs
+++ b/tests/mir-opt/matches_reduce_branches.rs
@@ -627,6 +627,37 @@ fn match_i128_u128(i: EnumAi128) -> u128 {
     }
 }
 
+// EMIT_MIR matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff
+#[custom_mir(dialect = "runtime")]
+fn match_non_int_failed(i: char) -> u8 {
+    // CHECK-LABEL: fn match_non_int_failed(
+    // CHECK: switchInt
+    // CHECK: return
+    mir! {
+        {
+            match i {
+                'a' => bb1,
+                'b' => bb2,
+                _ => unreachable_bb,
+            }
+        }
+        bb1 = {
+            RET = 97;
+            Goto(ret)
+        }
+        bb2 = {
+            RET = 98;
+            Goto(ret)
+        }
+        unreachable_bb = {
+            Unreachable()
+        }
+        ret = {
+            Return()
+        }
+    }
+}
+
 fn main() {
     let _ = foo(None);
     let _ = foo(Some(()));
@@ -664,4 +695,5 @@ fn main() {
     let _ = match_i128_u128(EnumAi128::A);
 
     let _ = my_is_some(None);
+    let _ = match_non_int_failed('a');
 }
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs
index 7a4d7d9a81e..afb16cf58e8 100644
--- a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs
@@ -14,14 +14,14 @@ fn main() {
     map.insert("c", ());
 
     {
-        let mut it = map.extract_if(|_, _| true);
+        let mut it = map.extract_if(.., |_, _| true);
         catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
         let result = catch_unwind(AssertUnwindSafe(|| it.next()));
         assert!(matches!(result, Ok(None)));
     }
 
     {
-        let mut it = map.extract_if(|_, _| true);
+        let mut it = map.extract_if(.., |_, _| true);
         catch_unwind(AssertUnwindSafe(|| while let Some(_) = it.next() {})).unwrap_err();
         let result = catch_unwind(AssertUnwindSafe(|| it.next()));
         assert!(matches!(result, Ok(None)));
diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed b/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed
new file mode 100644
index 00000000000..4d99e4bdc1c
--- /dev/null
+++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed
@@ -0,0 +1,24 @@
+//@ run-rustfix
+
+pub enum Foo {
+    X, Y
+}
+
+pub fn typo1(foo: Foo) -> Foo {
+    use Foo::*;
+    match foo {
+        X => Y,
+        Y => X, //~ ERROR expected one of
+    }
+}
+
+pub fn typo2(foo: Foo) -> Foo {
+    use Foo::*;
+    match foo {
+        X => Y,
+        Y => X, //~ ERROR expected one of
+    }
+}
+
+
+fn main() { }
diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.rs b/tests/ui/parser/match-arm-comma-typo-issue-140991.rs
new file mode 100644
index 00000000000..3baf1ff3fa1
--- /dev/null
+++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.rs
@@ -0,0 +1,24 @@
+//@ run-rustfix
+
+pub enum Foo {
+    X, Y
+}
+
+pub fn typo1(foo: Foo) -> Foo {
+    use Foo::*;
+    match foo {
+        X => Y.
+        Y => X, //~ ERROR expected one of
+    }
+}
+
+pub fn typo2(foo: Foo) -> Foo {
+    use Foo::*;
+    match foo {
+        X => Y/
+        Y => X, //~ ERROR expected one of
+    }
+}
+
+
+fn main() { }
diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr b/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr
new file mode 100644
index 00000000000..19532d14245
--- /dev/null
+++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr
@@ -0,0 +1,26 @@
+error: expected one of `(`, `,`, `.`, `::`, `?`, `}`, or an operator, found `=>`
+  --> $DIR/match-arm-comma-typo-issue-140991.rs:11:11
+   |
+LL |         Y => X,
+   |           ^^ expected one of 7 possible tokens
+   |
+help: you might have meant to write a `,` to end this `match` arm
+   |
+LL -         X => Y.
+LL +         X => Y,
+   |
+
+error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>`
+  --> $DIR/match-arm-comma-typo-issue-140991.rs:19:11
+   |
+LL |         Y => X,
+   |           ^^ expected one of 8 possible tokens
+   |
+help: you might have meant to write a `,` to end this `match` arm
+   |
+LL -         X => Y/
+LL +         X => Y,
+   |
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.fixed b/tests/ui/unsafe-binders/unused-lifetimes-2.fixed
new file mode 100644
index 00000000000..714a5fdaf03
--- /dev/null
+++ b/tests/ui/unsafe-binders/unused-lifetimes-2.fixed
@@ -0,0 +1,20 @@
+// regression test for #141758
+//@ run-rustfix
+//@ check-pass
+
+#![warn(unused_lifetimes)]
+#![allow(incomplete_features, unused_imports, dead_code)]
+#![feature(unsafe_binders)]
+
+use std::unsafe_binder::unwrap_binder;
+
+#[derive(Copy, Clone)]
+pub struct S([usize; 8]);
+
+// Regression test for <https://github.com/rust-lang/rust/issues/141418>.
+pub fn by_value(_x: unsafe<'a> &'a S) -> usize {
+    //~^ WARN lifetime parameter `'b` never used
+    0
+}
+
+fn main() {}
diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.rs b/tests/ui/unsafe-binders/unused-lifetimes-2.rs
new file mode 100644
index 00000000000..5b34cf91163
--- /dev/null
+++ b/tests/ui/unsafe-binders/unused-lifetimes-2.rs
@@ -0,0 +1,20 @@
+// regression test for #141758
+//@ run-rustfix
+//@ check-pass
+
+#![warn(unused_lifetimes)]
+#![allow(incomplete_features, unused_imports, dead_code)]
+#![feature(unsafe_binders)]
+
+use std::unsafe_binder::unwrap_binder;
+
+#[derive(Copy, Clone)]
+pub struct S([usize; 8]);
+
+// Regression test for <https://github.com/rust-lang/rust/issues/141418>.
+pub fn by_value(_x: unsafe<'a, 'b> &'a S) -> usize {
+    //~^ WARN lifetime parameter `'b` never used
+    0
+}
+
+fn main() {}
diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.stderr b/tests/ui/unsafe-binders/unused-lifetimes-2.stderr
new file mode 100644
index 00000000000..bca8a15d56b
--- /dev/null
+++ b/tests/ui/unsafe-binders/unused-lifetimes-2.stderr
@@ -0,0 +1,16 @@
+warning: lifetime parameter `'b` never used
+  --> $DIR/unused-lifetimes-2.rs:15:32
+   |
+LL | pub fn by_value(_x: unsafe<'a, 'b> &'a S) -> usize {
+   |                              --^^
+   |                              |
+   |                              help: elide the unused lifetime
+   |
+note: the lint level is defined here
+  --> $DIR/unused-lifetimes-2.rs:5:9
+   |
+LL | #![warn(unused_lifetimes)]
+   |         ^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/unsafe-binders/unused-lifetimes.fixed b/tests/ui/unsafe-binders/unused-lifetimes.fixed
new file mode 100644
index 00000000000..4295b6a848c
--- /dev/null
+++ b/tests/ui/unsafe-binders/unused-lifetimes.fixed
@@ -0,0 +1,20 @@
+// regression test for #141758
+//@ run-rustfix
+//@ check-pass
+
+#![warn(unused_lifetimes)]
+#![allow(incomplete_features, unused_imports, dead_code)]
+#![feature(unsafe_binders)]
+
+use std::unsafe_binder::unwrap_binder;
+
+#[derive(Copy, Clone)]
+pub struct S([usize; 8]);
+
+// Regression test for <https://github.com/rust-lang/rust/issues/141418>.
+pub fn by_value(_x: S) -> usize {
+    //~^ WARN lifetime parameter `'a` never used
+    0
+}
+
+fn main() {}
diff --git a/tests/ui/unsafe-binders/unused-lifetimes.rs b/tests/ui/unsafe-binders/unused-lifetimes.rs
new file mode 100644
index 00000000000..b1382328318
--- /dev/null
+++ b/tests/ui/unsafe-binders/unused-lifetimes.rs
@@ -0,0 +1,20 @@
+// regression test for #141758
+//@ run-rustfix
+//@ check-pass
+
+#![warn(unused_lifetimes)]
+#![allow(incomplete_features, unused_imports, dead_code)]
+#![feature(unsafe_binders)]
+
+use std::unsafe_binder::unwrap_binder;
+
+#[derive(Copy, Clone)]
+pub struct S([usize; 8]);
+
+// Regression test for <https://github.com/rust-lang/rust/issues/141418>.
+pub fn by_value(_x: unsafe<'a> S) -> usize {
+    //~^ WARN lifetime parameter `'a` never used
+    0
+}
+
+fn main() {}
diff --git a/tests/ui/unsafe-binders/unused-lifetimes.stderr b/tests/ui/unsafe-binders/unused-lifetimes.stderr
new file mode 100644
index 00000000000..d9a5216301f
--- /dev/null
+++ b/tests/ui/unsafe-binders/unused-lifetimes.stderr
@@ -0,0 +1,14 @@
+warning: lifetime parameter `'a` never used
+  --> $DIR/unused-lifetimes.rs:15:28
+   |
+LL | pub fn by_value(_x: unsafe<'a> S) -> usize {
+   |                     -------^^-- help: elide the unused lifetime
+   |
+note: the lint level is defined here
+  --> $DIR/unused-lifetimes.rs:5:9
+   |
+LL | #![warn(unused_lifetimes)]
+   |         ^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+