about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-12-18 16:21:57 +0000
committerbors <bors@rust-lang.org>2024-12-18 16:21:57 +0000
commita52085d9f6a6e596b0cbad4502cddf86bc878028 (patch)
tree1297ae0a4686318bc8377eded4e43b0371916743
parent057bdb37eccff6a2bd402509bbbadb9d73ad7bf5 (diff)
parent29d201a3cb0ce0d0052bb4edd5fdca9c2d08894d (diff)
downloadrust-a52085d9f6a6e596b0cbad4502cddf86bc878028.tar.gz
rust-a52085d9f6a6e596b0cbad4502cddf86bc878028.zip
Auto merge of #134470 - jieyouxu:rollup-kld7kmk, r=jieyouxu
Rollup of 11 pull requests

Successful merges:

 - #130786 ( mir-opt: a sub-BB of a cleanup BB must also be a cleanup BB in `EarlyOtherwiseBranch`)
 - #133926 (Fix const conditions for RPITITs)
 - #134161 (Overhaul token cursors)
 - #134253 (Overhaul keyword handling)
 - #134394 (Clarify the match ergonomics 2024 migration lint's output)
 - #134399 (Do not do if ! else, use unnegated cond and swap the branches instead)
 - #134420 (refactor: replace &PathBuf with &Path to enhance generality)
 - #134436 (tests/assembly/asm: Remove uses of rustc_attrs and lang_items features by using minicore)
 - #134444 (Fix `x build --stage 1 std` when using cg_cranelift as the default backend)
 - #134452 (fix(LazyCell): documentation of get[_mut] was wrong)
 - #134460 (Merge some patterns together)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs69
-rw-r--r--compiler/rustc_ast/src/token.rs8
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs76
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/concat_idents.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/trace_macros.rs6
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/metavar_expr.rs39
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs52
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs41
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs71
-rw-r--r--compiler/rustc_lint/src/builtin.rs2
-rw-r--r--compiler/rustc_lint/src/internal.rs26
-rw-r--r--compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs2
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs4
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs13
-rw-r--r--compiler/rustc_mir_build/messages.ftl2
-rw-r--r--compiler/rustc_mir_build/src/builder/cfg.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/custom/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/custom/parse.rs13
-rw-r--r--compiler/rustc_mir_build/src/builder/misc.rs6
-rw-r--r--compiler/rustc_mir_build/src/errors.rs28
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs29
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/visitor.rs1
-rw-r--r--compiler/rustc_mir_transform/src/early_otherwise_branch.rs283
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs12
-rw-r--r--compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs8
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs4
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs92
-rw-r--r--compiler/rustc_parse/src/parser/tests.rs10
-rw-r--r--compiler/rustc_parse/src/parser/tokenstream/tests.rs8
-rw-r--r--compiler/rustc_session/src/config.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs85
-rw-r--r--library/core/src/cell/lazy.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs5
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs2
-rw-r--r--src/bootstrap/src/core/config/config.rs10
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/librustdoc/clean/render_macro_matchers.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs6
-rw-r--r--src/tools/compiletest/src/debuggers.rs4
-rw-r--r--src/tools/compiletest/src/lib.rs7
-rw-r--r--src/tools/compiletest/src/runtest.rs2
-rw-r--r--src/tools/rustc-perf-wrapper/src/main.rs2
-rw-r--r--src/tools/rustfmt/src/macros.rs30
-rw-r--r--src/tools/rustfmt/src/parse/macros/mod.rs87
-rw-r--r--tests/assembly/asm/aarch64-el2vmsa.rs12
-rw-r--r--tests/assembly/asm/aarch64-modifiers.rs20
-rw-r--r--tests/assembly/asm/aarch64-types.rs36
-rw-r--r--tests/assembly/asm/arm-modifiers.rs28
-rw-r--r--tests/assembly/asm/arm-types.rs33
-rw-r--r--tests/assembly/asm/avr-modifiers.rs24
-rw-r--r--tests/assembly/asm/avr-types.rs24
-rw-r--r--tests/assembly/asm/bpf-types.rs28
-rw-r--r--tests/assembly/asm/hexagon-types.rs27
-rw-r--r--tests/assembly/asm/loongarch-type.rs29
-rw-r--r--tests/assembly/asm/m68k-types.rs24
-rw-r--r--tests/assembly/asm/mips-types.rs30
-rw-r--r--tests/assembly/asm/msp430-types.rs24
-rw-r--r--tests/assembly/asm/nvptx-types.rs28
-rw-r--r--tests/assembly/asm/powerpc-types.rs34
-rw-r--r--tests/assembly/asm/riscv-types.rs31
-rw-r--r--tests/assembly/asm/s390x-types.rs34
-rw-r--r--tests/assembly/asm/sparc-types.rs31
-rw-r--r--tests/assembly/asm/wasm-types.rs28
-rw-r--r--tests/assembly/asm/x86-modifiers.rs24
-rw-r--r--tests/assembly/asm/x86-types.rs26
-rw-r--r--tests/auxiliary/minicore.rs24
-rw-r--r--tests/mir-opt/early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff77
-rw-r--r--tests/mir-opt/early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff61
-rw-r--r--tests/mir-opt/early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff61
-rw-r--r--tests/mir-opt/early_otherwise_branch.rs48
-rw-r--r--tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff126
-rw-r--r--tests/mir-opt/early_otherwise_branch_unwind.rs38
-rw-r--r--tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff119
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed32
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs32
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr185
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs14
-rw-r--r--tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr98
-rw-r--r--tests/ui/traits/const-traits/const-cond-for-rpitit.rs22
-rw-r--r--tests/ui/traits/const-traits/const-impl-trait.rs25
-rw-r--r--tests/ui/traits/const-traits/const-impl-trait.stderr187
87 files changed, 1715 insertions, 1191 deletions
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 4ce1d4882ef..97385b2eaab 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -1,7 +1,6 @@
 //! Functions dealing with attributes and meta items.
 
 use std::fmt::Debug;
-use std::iter;
 use std::sync::atomic::{AtomicU32, Ordering};
 
 use rustc_index::bit_set::GrowableBitSet;
@@ -16,7 +15,9 @@ use crate::ast::{
 };
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Delimiter, Token};
-use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenTree};
+use crate::tokenstream::{
+    DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
+};
 use crate::util::comments;
 use crate::util::literal::escape_string_symbol;
 
@@ -365,12 +366,9 @@ impl MetaItem {
         }
     }
 
-    fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
-    where
-        I: Iterator<Item = &'a TokenTree>,
-    {
+    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItem> {
         // FIXME: Share code with `parse_path`.
-        let tt = tokens.next().map(|tt| TokenTree::uninterpolate(tt));
+        let tt = iter.next().map(|tt| TokenTree::uninterpolate(tt));
         let path = match tt.as_deref() {
             Some(&TokenTree::Token(
                 Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
@@ -378,9 +376,9 @@ impl MetaItem {
             )) => 'arm: {
                 let mut segments = if let &token::Ident(name, _) = kind {
                     if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
-                        tokens.peek()
+                        iter.peek()
                     {
-                        tokens.next();
+                        iter.next();
                         thin_vec![PathSegment::from_ident(Ident::new(name, span))]
                     } else {
                         break 'arm Path::from_ident(Ident::new(name, span));
@@ -390,16 +388,16 @@ impl MetaItem {
                 };
                 loop {
                     if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
-                        tokens.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
+                        iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
                     {
                         segments.push(PathSegment::from_ident(Ident::new(name, span)));
                     } else {
                         return None;
                     }
                     if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
-                        tokens.peek()
+                        iter.peek()
                     {
-                        tokens.next();
+                        iter.next();
                     } else {
                         break;
                     }
@@ -420,8 +418,8 @@ impl MetaItem {
             }
             _ => return None,
         };
-        let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
-        let kind = MetaItemKind::from_tokens(tokens)?;
+        let list_closing_paren_pos = iter.peek().map(|tt| tt.span().hi());
+        let kind = MetaItemKind::from_tokens(iter)?;
         let hi = match &kind {
             MetaItemKind::NameValue(lit) => lit.span.hi(),
             MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
@@ -438,12 +436,12 @@ impl MetaItem {
 impl MetaItemKind {
     // public because it can be called in the hir
     pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {
-        let mut tokens = tokens.trees().peekable();
+        let mut iter = tokens.iter();
         let mut result = ThinVec::new();
-        while tokens.peek().is_some() {
-            let item = MetaItemInner::from_tokens(&mut tokens)?;
+        while iter.peek().is_some() {
+            let item = MetaItemInner::from_tokens(&mut iter)?;
             result.push(item);
-            match tokens.next() {
+            match iter.next() {
                 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
                 _ => return None,
             }
@@ -451,12 +449,10 @@ impl MetaItemKind {
         Some(result)
     }
 
-    fn name_value_from_tokens<'a>(
-        tokens: &mut impl Iterator<Item = &'a TokenTree>,
-    ) -> Option<MetaItemKind> {
-        match tokens.next() {
+    fn name_value_from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {
+        match iter.next() {
             Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
-                MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees())
+                MetaItemKind::name_value_from_tokens(&mut inner_tokens.iter())
             }
             Some(TokenTree::Token(token, _)) => {
                 MetaItemLit::from_token(token).map(MetaItemKind::NameValue)
@@ -465,19 +461,17 @@ impl MetaItemKind {
         }
     }
 
-    fn from_tokens<'a>(
-        tokens: &mut iter::Peekable<impl Iterator<Item = &'a TokenTree>>,
-    ) -> Option<MetaItemKind> {
-        match tokens.peek() {
+    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {
+        match iter.peek() {
             Some(TokenTree::Delimited(.., Delimiter::Parenthesis, inner_tokens)) => {
                 let inner_tokens = inner_tokens.clone();
-                tokens.next();
+                iter.next();
                 MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
             }
             Some(TokenTree::Delimited(..)) => None,
             Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
-                tokens.next();
-                MetaItemKind::name_value_from_tokens(tokens)
+                iter.next();
+                MetaItemKind::name_value_from_tokens(iter)
             }
             _ => Some(MetaItemKind::Word),
         }
@@ -593,22 +587,19 @@ impl MetaItemInner {
         self.meta_item().is_some()
     }
 
-    fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemInner>
-    where
-        I: Iterator<Item = &'a TokenTree>,
-    {
-        match tokens.peek() {
+    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemInner> {
+        match iter.peek() {
             Some(TokenTree::Token(token, _)) if let Some(lit) = MetaItemLit::from_token(token) => {
-                tokens.next();
+                iter.next();
                 return Some(MetaItemInner::Lit(lit));
             }
             Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
-                tokens.next();
-                return MetaItemInner::from_tokens(&mut inner_tokens.trees().peekable());
+                iter.next();
+                return MetaItemInner::from_tokens(&mut inner_tokens.iter());
             }
             _ => {}
         }
-        MetaItem::from_tokens(tokens).map(MetaItemInner::MetaItem)
+        MetaItem::from_tokens(iter).map(MetaItemInner::MetaItem)
     }
 }
 
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index ab82f18133e..f639e785bc4 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -903,7 +903,8 @@ impl Token {
         self.is_non_raw_ident_where(|id| id.name == kw)
     }
 
-    /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this token is an identifier equal to `kw` ignoring the case.
+    /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this
+    /// token is an identifier equal to `kw` ignoring the case.
     pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool {
         self.is_keyword(kw)
             || (case == Case::Insensitive
@@ -916,6 +917,11 @@ impl Token {
         self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
     }
 
+    /// Don't use this unless you're doing something very loose and heuristic-y.
+    pub fn is_any_keyword(&self) -> bool {
+        self.is_non_raw_ident_where(Ident::is_any_keyword)
+    }
+
     /// Returns true for reserved identifiers used internally for elided lifetimes,
     /// unnamed method parameters, crate root module, error recovery etc.
     pub fn is_special_ident(&self) -> bool {
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index c6b6addc946..e7b393d869d 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -99,7 +99,7 @@ where
     CTX: crate::HashStableContext,
 {
     fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
-        for sub_tt in self.trees() {
+        for sub_tt in self.iter() {
             sub_tt.hash_stable(hcx, hasher);
         }
     }
@@ -406,7 +406,7 @@ impl Eq for TokenStream {}
 
 impl PartialEq<TokenStream> for TokenStream {
     fn eq(&self, other: &TokenStream) -> bool {
-        self.trees().eq(other.trees())
+        self.iter().eq(other.iter())
     }
 }
 
@@ -423,24 +423,24 @@ impl TokenStream {
         self.0.len()
     }
 
-    pub fn trees(&self) -> RefTokenTreeCursor<'_> {
-        RefTokenTreeCursor::new(self)
+    pub fn get(&self, index: usize) -> Option<&TokenTree> {
+        self.0.get(index)
     }
 
-    pub fn into_trees(self) -> TokenTreeCursor {
-        TokenTreeCursor::new(self)
+    pub fn iter(&self) -> TokenStreamIter<'_> {
+        TokenStreamIter::new(self)
     }
 
     /// Compares two `TokenStream`s, checking equality without regarding span information.
     pub fn eq_unspanned(&self, other: &TokenStream) -> bool {
-        let mut t1 = self.trees();
-        let mut t2 = other.trees();
-        for (t1, t2) in iter::zip(&mut t1, &mut t2) {
-            if !t1.eq_unspanned(t2) {
+        let mut iter1 = self.iter();
+        let mut iter2 = other.iter();
+        for (tt1, tt2) in iter::zip(&mut iter1, &mut iter2) {
+            if !tt1.eq_unspanned(tt2) {
                 return false;
             }
         }
-        t1.next().is_none() && t2.next().is_none()
+        iter1.next().is_none() && iter2.next().is_none()
     }
 
     /// Create a token stream containing a single token with alone spacing. The
@@ -509,7 +509,7 @@ impl TokenStream {
     #[must_use]
     pub fn flattened(&self) -> TokenStream {
         fn can_skip(stream: &TokenStream) -> bool {
-            stream.trees().all(|tree| match tree {
+            stream.iter().all(|tree| match tree {
                 TokenTree::Token(token, _) => !matches!(
                     token.kind,
                     token::NtIdent(..) | token::NtLifetime(..) | token::Interpolated(..)
@@ -522,7 +522,7 @@ impl TokenStream {
             return self.clone();
         }
 
-        self.trees().map(|tree| TokenStream::flatten_token_tree(tree)).collect()
+        self.iter().map(|tree| TokenStream::flatten_token_tree(tree)).collect()
     }
 
     // If `vec` is not empty, try to glue `tt` onto its last token. The return
@@ -665,25 +665,26 @@ impl TokenStream {
     }
 }
 
-/// By-reference iterator over a [`TokenStream`], that produces `&TokenTree`
-/// items.
 #[derive(Clone)]
-pub struct RefTokenTreeCursor<'t> {
+pub struct TokenStreamIter<'t> {
     stream: &'t TokenStream,
     index: usize,
 }
 
-impl<'t> RefTokenTreeCursor<'t> {
+impl<'t> TokenStreamIter<'t> {
     fn new(stream: &'t TokenStream) -> Self {
-        RefTokenTreeCursor { stream, index: 0 }
+        TokenStreamIter { stream, index: 0 }
     }
 
-    pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
-        self.stream.0.get(self.index + n)
+    // Peeking could be done via `Peekable`, but most iterators need peeking,
+    // and this is simple and avoids the need to use `peekable` and `Peekable`
+    // at all the use sites.
+    pub fn peek(&self) -> Option<&'t TokenTree> {
+        self.stream.0.get(self.index)
     }
 }
 
-impl<'t> Iterator for RefTokenTreeCursor<'t> {
+impl<'t> Iterator for TokenStreamIter<'t> {
     type Item = &'t TokenTree;
 
     fn next(&mut self) -> Option<&'t TokenTree> {
@@ -694,39 +695,6 @@ impl<'t> Iterator for RefTokenTreeCursor<'t> {
     }
 }
 
-/// Owning by-value iterator over a [`TokenStream`], that produces `&TokenTree`
-/// items.
-///
-/// Doesn't impl `Iterator` because Rust doesn't permit an owning iterator to
-/// return `&T` from `next`; the need for an explicit lifetime in the `Item`
-/// associated type gets in the way. Instead, use `next_ref` (which doesn't
-/// involve associated types) for getting individual elements, or
-/// `RefTokenTreeCursor` if you really want an `Iterator`, e.g. in a `for`
-/// loop.
-#[derive(Clone, Debug)]
-pub struct TokenTreeCursor {
-    pub stream: TokenStream,
-    index: usize,
-}
-
-impl TokenTreeCursor {
-    fn new(stream: TokenStream) -> Self {
-        TokenTreeCursor { stream, index: 0 }
-    }
-
-    #[inline]
-    pub fn next_ref(&mut self) -> Option<&TokenTree> {
-        self.stream.0.get(self.index).map(|tree| {
-            self.index += 1;
-            tree
-        })
-    }
-
-    pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
-        self.stream.0.get(self.index + n)
-    }
-}
-
 #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
 pub struct DelimSpan {
     pub open: Span,
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 70b72e88d7f..24c1c0f221e 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -725,7 +725,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
     // E.g. we have seen cases where a proc macro can handle `a :: b` but not
     // `a::b`. See #117433 for some examples.
     fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
-        let mut iter = tts.trees().peekable();
+        let mut iter = tts.iter().peekable();
         while let Some(tt) = iter.next() {
             let spacing = self.print_tt(tt, convert_dollar_crate);
             if let Some(next) = iter.peek() {
diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs
index 208b499eb7a..a721f5b84c5 100644
--- a/compiler/rustc_builtin_macros/src/concat_idents.rs
+++ b/compiler/rustc_builtin_macros/src/concat_idents.rs
@@ -18,7 +18,7 @@ pub(crate) fn expand_concat_idents<'cx>(
     }
 
     let mut res_str = String::new();
-    for (i, e) in tts.trees().enumerate() {
+    for (i, e) in tts.iter().enumerate() {
         if i & 1 == 1 {
             match e {
                 TokenTree::Token(Token { kind: token::Comma, .. }, _) => {}
diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs
index 670ddc0415f..8264c17b4d1 100644
--- a/compiler/rustc_builtin_macros/src/trace_macros.rs
+++ b/compiler/rustc_builtin_macros/src/trace_macros.rs
@@ -9,9 +9,9 @@ pub(crate) fn expand_trace_macros(
     sp: Span,
     tt: TokenStream,
 ) -> MacroExpanderResult<'static> {
-    let mut cursor = tt.trees();
+    let mut iter = tt.iter();
     let mut err = false;
-    let value = match &cursor.next() {
+    let value = match iter.next() {
         Some(TokenTree::Token(token, _)) if token.is_keyword(kw::True) => true,
         Some(TokenTree::Token(token, _)) if token.is_keyword(kw::False) => false,
         _ => {
@@ -19,7 +19,7 @@ pub(crate) fn expand_trace_macros(
             false
         }
     };
-    err |= cursor.next().is_some();
+    err |= iter.next().is_some();
     if err {
         cx.dcx().emit_err(errors::TraceMacros { span: sp });
     } else {
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index ed49dfe1761..3dc39fc131a 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -26,7 +26,7 @@ use std::fmt::Write as _;
 use std::fs::{self, File};
 use std::io::{self, IsTerminal, Read, Write};
 use std::panic::{self, PanicHookInfo, catch_unwind};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::process::{self, Command, Stdio};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::{Arc, OnceLock};
@@ -460,7 +460,7 @@ fn run_compiler(
     })
 }
 
-fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &PathBuf) {
+fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) {
     let output_filenames = tcxt.output_filenames(());
     let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-");
     let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json");
diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs
index da6e620a24f..1ccb070f83a 100644
--- a/compiler/rustc_expand/src/mbe/metavar_expr.rs
+++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs
@@ -1,5 +1,5 @@
 use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, Token, TokenKind};
-use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{TokenStream, TokenStreamIter, TokenTree};
 use rustc_ast::{LitIntType, LitKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, PResult};
@@ -38,14 +38,14 @@ impl MetaVarExpr {
         outer_span: Span,
         psess: &'psess ParseSess,
     ) -> PResult<'psess, MetaVarExpr> {
-        let mut tts = input.trees();
-        let ident = parse_ident(&mut tts, psess, outer_span)?;
-        let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = tts.next() else {
+        let mut iter = input.iter();
+        let ident = parse_ident(&mut iter, psess, outer_span)?;
+        let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = iter.next() else {
             let msg = "meta-variable expression parameter must be wrapped in parentheses";
             return Err(psess.dcx().struct_span_err(ident.span, msg));
         };
-        check_trailing_token(&mut tts, psess)?;
-        let mut iter = args.trees();
+        check_trailing_token(&mut iter, psess)?;
+        let mut iter = args.iter();
         let rslt = match ident.as_str() {
             "concat" => {
                 let mut result = Vec::new();
@@ -73,7 +73,7 @@ impl MetaVarExpr {
                         }
                     };
                     result.push(element);
-                    if iter.look_ahead(0).is_none() {
+                    if iter.peek().is_none() {
                         break;
                     }
                     if !try_eat_comma(&mut iter) {
@@ -142,7 +142,7 @@ pub(crate) enum MetaVarExprConcatElem {
 
 // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
 fn check_trailing_token<'psess>(
-    iter: &mut RefTokenTreeCursor<'_>,
+    iter: &mut TokenStreamIter<'_>,
     psess: &'psess ParseSess,
 ) -> PResult<'psess, ()> {
     if let Some(tt) = iter.next() {
@@ -158,14 +158,14 @@ fn check_trailing_token<'psess>(
 
 /// Parse a meta-variable `count` expression: `count(ident[, depth])`
 fn parse_count<'psess>(
-    iter: &mut RefTokenTreeCursor<'_>,
+    iter: &mut TokenStreamIter<'_>,
     psess: &'psess ParseSess,
     span: Span,
 ) -> PResult<'psess, MetaVarExpr> {
     eat_dollar(iter, psess, span)?;
     let ident = parse_ident(iter, psess, span)?;
     let depth = if try_eat_comma(iter) {
-        if iter.look_ahead(0).is_none() {
+        if iter.peek().is_none() {
             return Err(psess.dcx().struct_span_err(
                 span,
                 "`count` followed by a comma must have an associated index indicating its depth",
@@ -180,7 +180,7 @@ fn parse_count<'psess>(
 
 /// Parses the depth used by index(depth) and len(depth).
 fn parse_depth<'psess>(
-    iter: &mut RefTokenTreeCursor<'_>,
+    iter: &mut TokenStreamIter<'_>,
     psess: &'psess ParseSess,
     span: Span,
 ) -> PResult<'psess, usize> {
@@ -203,7 +203,7 @@ fn parse_depth<'psess>(
 
 /// Parses an generic ident
 fn parse_ident<'psess>(
-    iter: &mut RefTokenTreeCursor<'_>,
+    iter: &mut TokenStreamIter<'_>,
     psess: &'psess ParseSess,
     fallback_span: Span,
 ) -> PResult<'psess, Ident> {
@@ -235,7 +235,7 @@ fn parse_ident_from_token<'psess>(
 }
 
 fn parse_token<'psess, 't>(
-    iter: &mut RefTokenTreeCursor<'t>,
+    iter: &mut TokenStreamIter<'t>,
     psess: &'psess ParseSess,
     fallback_span: Span,
 ) -> PResult<'psess, &'t Token> {
@@ -250,8 +250,8 @@ fn parse_token<'psess, 't>(
 
 /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
 /// iterator is not modified and the result is `false`.
-fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
-    if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) {
+fn try_eat_comma(iter: &mut TokenStreamIter<'_>) -> bool {
+    if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.peek() {
         let _ = iter.next();
         return true;
     }
@@ -260,8 +260,8 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
 
 /// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the
 /// iterator is not modified and the result is `false`.
-fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
-    if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) {
+fn try_eat_dollar(iter: &mut TokenStreamIter<'_>) -> bool {
+    if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.peek() {
         let _ = iter.next();
         return true;
     }
@@ -270,12 +270,11 @@ fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
 
 /// Expects that the next item is a dollar sign.
 fn eat_dollar<'psess>(
-    iter: &mut RefTokenTreeCursor<'_>,
+    iter: &mut TokenStreamIter<'_>,
     psess: &'psess ParseSess,
     span: Span,
 ) -> PResult<'psess, ()> {
-    if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) {
-        let _ = iter.next();
+    if try_eat_dollar(iter) {
         return Ok(());
     }
     Err(psess.dcx().struct_span_err(
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 1addfabea23..a27d47892e4 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -1,4 +1,5 @@
 use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token};
+use rustc_ast::tokenstream::TokenStreamIter;
 use rustc_ast::{NodeId, tokenstream};
 use rustc_ast_pretty::pprust;
 use rustc_feature::Features;
@@ -48,25 +49,25 @@ pub(super) fn parse(
 
     // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming
     // additional trees if need be.
-    let mut trees = input.trees().peekable();
-    while let Some(tree) = trees.next() {
+    let mut iter = input.iter();
+    while let Some(tree) = iter.next() {
         // Given the parsed tree, if there is a metavar and we are expecting matchers, actually
         // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
-        let tree = parse_tree(tree, &mut trees, parsing_patterns, sess, node_id, features, edition);
+        let tree = parse_tree(tree, &mut iter, parsing_patterns, sess, node_id, features, edition);
         match tree {
             TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
                 // Not consuming the next token immediately, as it may not be a colon
-                let span = match trees.peek() {
+                let span = match iter.peek() {
                     Some(&tokenstream::TokenTree::Token(
                         Token { kind: token::Colon, span: colon_span },
                         _,
                     )) => {
                         // Consume the colon first
-                        trees.next();
+                        iter.next();
 
                         // It's ok to consume the next tree no matter how,
                         // since if it's not a token then it will be an invalid declaration.
-                        match trees.next() {
+                        match iter.next() {
                             Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
                                 Some((fragment, _)) => {
                                     let span = token.span.with_lo(start_sp.lo());
@@ -142,14 +143,14 @@ fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Sess
 /// # Parameters
 ///
 /// - `tree`: the tree we wish to convert.
-/// - `outer_trees`: an iterator over trees. We may need to read more tokens from it in order to finish
+/// - `outer_iter`: an iterator over trees. We may need to read more tokens from it in order to finish
 ///   converting `tree`
 /// - `parsing_patterns`: same as [parse].
 /// - `sess`: the parsing session. Any errors will be emitted to this session.
 /// - `features`: language features so we can do feature gating.
 fn parse_tree<'a>(
     tree: &'a tokenstream::TokenTree,
-    outer_trees: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
+    outer_iter: &mut TokenStreamIter<'a>,
     parsing_patterns: bool,
     sess: &Session,
     node_id: NodeId,
@@ -162,15 +163,16 @@ fn parse_tree<'a>(
         &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span: dollar_span }, _) => {
             // FIXME: Handle `Invisible`-delimited groups in a more systematic way
             // during parsing.
-            let mut next = outer_trees.next();
-            let mut trees: Box<dyn Iterator<Item = &tokenstream::TokenTree>>;
-            match next {
+            let mut next = outer_iter.next();
+            let mut iter_storage;
+            let mut iter: &mut TokenStreamIter<'_> = match next {
                 Some(tokenstream::TokenTree::Delimited(.., delim, tts)) if delim.skip() => {
-                    trees = Box::new(tts.trees());
-                    next = trees.next();
+                    iter_storage = tts.iter();
+                    next = iter_storage.next();
+                    &mut iter_storage
                 }
-                _ => trees = Box::new(outer_trees),
-            }
+                _ => outer_iter,
+            };
 
             match next {
                 // `tree` is followed by a delimited set of token trees.
@@ -229,7 +231,7 @@ fn parse_tree<'a>(
                     let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition);
                     // Get the Kleene operator and optional separator
                     let (separator, kleene) =
-                        parse_sep_and_kleene_op(&mut trees, delim_span.entire(), sess);
+                        parse_sep_and_kleene_op(&mut iter, delim_span.entire(), sess);
                     // Count the number of captured "names" (i.e., named metavars)
                     let num_captures =
                         if parsing_patterns { count_metavar_decls(&sequence) } else { 0 };
@@ -312,11 +314,11 @@ fn kleene_op(token: &Token) -> Option<KleeneOp> {
 /// - Ok(Ok((op, span))) if the next token tree is a KleeneOp
 /// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp
 /// - Err(span) if the next token tree is not a token
-fn parse_kleene_op<'a>(
-    input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
+fn parse_kleene_op(
+    iter: &mut TokenStreamIter<'_>,
     span: Span,
 ) -> Result<Result<(KleeneOp, Span), Token>, Span> {
-    match input.next() {
+    match iter.next() {
         Some(tokenstream::TokenTree::Token(token, _)) => match kleene_op(token) {
             Some(op) => Ok(Ok((op, token.span))),
             None => Ok(Err(token.clone())),
@@ -333,22 +335,22 @@ fn parse_kleene_op<'a>(
 /// itself. Note that here we are parsing the _macro_ itself, rather than trying to match some
 /// stream of tokens in an invocation of a macro.
 ///
-/// This function will take some input iterator `input` corresponding to `span` and a parsing
-/// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene
+/// This function will take some input iterator `iter` corresponding to `span` and a parsing
+/// session `sess`. If the next one (or possibly two) tokens in `iter` correspond to a Kleene
 /// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an
 /// error with the appropriate span is emitted to `sess` and a dummy value is returned.
-fn parse_sep_and_kleene_op<'a>(
-    input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
+fn parse_sep_and_kleene_op(
+    iter: &mut TokenStreamIter<'_>,
     span: Span,
     sess: &Session,
 ) -> (Option<Token>, KleeneToken) {
     // We basically look at two token trees here, denoted as #1 and #2 below
-    let span = match parse_kleene_op(input, span) {
+    let span = match parse_kleene_op(iter, span) {
         // #1 is a `?`, `+`, or `*` KleeneOp
         Ok(Ok((op, span))) => return (None, KleeneToken::new(op, span)),
 
         // #1 is a separator followed by #2, a KleeneOp
-        Ok(Err(token)) => match parse_kleene_op(input, token.span) {
+        Ok(Err(token)) => match parse_kleene_op(iter, token.span) {
             // #2 is the `?` Kleene op, which does not take a separator (error)
             Ok(Ok((KleeneOp::ZeroOrOne, span))) => {
                 // Error!
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 0adff4eaf9d..8577aa110af 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -111,9 +111,9 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
         // Estimate the capacity as `stream.len()` rounded up to the next power
         // of two to limit the number of required reallocations.
         let mut trees = Vec::with_capacity(stream.len().next_power_of_two());
-        let mut cursor = stream.trees();
+        let mut iter = stream.iter();
 
-        while let Some(tree) = cursor.next() {
+        while let Some(tree) = iter.next() {
             let (Token { kind, span }, joint) = match tree.clone() {
                 tokenstream::TokenTree::Delimited(span, _, delim, tts) => {
                     let delimiter = pm::Delimiter::from_internal(delim);
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 0b81f469371..d3ff1f7bebe 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -371,10 +371,9 @@ pub(super) fn explicit_item_bounds_with_filter(
                 associated_type_bounds(tcx, def_id, opaque_ty.bounds, opaque_ty.span, filter);
             return ty::EarlyBinder::bind(bounds);
         }
-        Some(ty::ImplTraitInTraitData::Impl { .. }) => span_bug!(
-            tcx.def_span(def_id),
-            "item bounds for RPITIT in impl to be fed on def-id creation"
-        ),
+        Some(ty::ImplTraitInTraitData::Impl { .. }) => {
+            span_bug!(tcx.def_span(def_id), "RPITIT in impl should not have item bounds")
+        }
         None => {}
     }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index c5fb3474022..8f84492146f 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -956,6 +956,15 @@ pub(super) fn const_conditions<'tcx>(
         bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}");
     }
 
+    match tcx.opt_rpitit_info(def_id.to_def_id()) {
+        // RPITITs inherit const conditions of their parent fn
+        Some(
+            ty::ImplTraitInTraitData::Impl { fn_def_id }
+            | ty::ImplTraitInTraitData::Trait { fn_def_id, .. },
+        ) => return tcx.const_conditions(fn_def_id),
+        None => {}
+    }
+
     let (generics, trait_def_id_and_supertraits, has_parent) = match tcx.hir_node_by_def_id(def_id)
     {
         Node::Item(item) => match item.kind {
@@ -1059,19 +1068,29 @@ pub(super) fn explicit_implied_const_bounds<'tcx>(
         bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}");
     }
 
-    let bounds = match tcx.hir_node_by_def_id(def_id) {
-        Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => {
-            implied_predicates_with_filter(
-                tcx,
-                def_id.to_def_id(),
-                PredicateFilter::SelfConstIfConst,
-            )
-        }
-        Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. })
-        | Node::OpaqueTy(_) => {
+    let bounds = match tcx.opt_rpitit_info(def_id.to_def_id()) {
+        // RPITIT's bounds are the same as opaque type bounds, but with
+        // a projection self type.
+        Some(ty::ImplTraitInTraitData::Trait { .. }) => {
             explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst)
         }
-        _ => bug!("explicit_implied_const_bounds called on wrong item: {def_id:?}"),
+        Some(ty::ImplTraitInTraitData::Impl { .. }) => {
+            span_bug!(tcx.def_span(def_id), "RPITIT in impl should not have item bounds")
+        }
+        None => match tcx.hir_node_by_def_id(def_id) {
+            Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => {
+                implied_predicates_with_filter(
+                    tcx,
+                    def_id.to_def_id(),
+                    PredicateFilter::SelfConstIfConst,
+                )
+            }
+            Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. })
+            | Node::OpaqueTy(_) => {
+                explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst)
+            }
+            _ => bug!("explicit_implied_const_bounds called on wrong item: {def_id:?}"),
+        },
     };
 
     bounds.map_bound(|bounds| {
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 06392deb8ff..98b28240f4c 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -717,12 +717,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     BindingMode(def_br, Mutability::Mut)
                 } else {
                     // `mut` resets the binding mode on edition <= 2021
-                    *self
-                        .typeck_results
-                        .borrow_mut()
-                        .rust_2024_migration_desugared_pats_mut()
-                        .entry(pat_info.top_info.hir_id)
-                        .or_default() |= pat.span.at_least_rust_2024();
+                    self.add_rust_2024_migration_desugared_pat(
+                        pat_info.top_info.hir_id,
+                        pat.span,
+                        ident.span,
+                        "requires binding by-value, but the implicit default is by-reference",
+                    );
                     BindingMode(ByRef::No, Mutability::Mut)
                 }
             }
@@ -730,12 +730,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             BindingMode(ByRef::Yes(_), _) => {
                 if matches!(def_br, ByRef::Yes(_)) {
                     // `ref`/`ref mut` overrides the binding mode on edition <= 2021
-                    *self
-                        .typeck_results
-                        .borrow_mut()
-                        .rust_2024_migration_desugared_pats_mut()
-                        .entry(pat_info.top_info.hir_id)
-                        .or_default() |= pat.span.at_least_rust_2024();
+                    self.add_rust_2024_migration_desugared_pat(
+                        pat_info.top_info.hir_id,
+                        pat.span,
+                        ident.span,
+                        "cannot override to bind by-reference when that is the implicit default",
+                    );
                 }
                 user_bind_annot
             }
@@ -2265,12 +2265,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Reset binding mode on old editions
             if pat_info.binding_mode != ByRef::No {
                 pat_info.binding_mode = ByRef::No;
-                *self
-                    .typeck_results
-                    .borrow_mut()
-                    .rust_2024_migration_desugared_pats_mut()
-                    .entry(pat_info.top_info.hir_id)
-                    .or_default() |= pat.span.at_least_rust_2024();
+                self.add_rust_2024_migration_desugared_pat(
+                    pat_info.top_info.hir_id,
+                    pat.span,
+                    inner.span,
+                    "cannot implicitly match against multiple layers of reference",
+                )
             }
         }
 
@@ -2629,4 +2629,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => (false, ty),
         }
     }
+
+    /// Record a pattern that's invalid under Rust 2024 match ergonomics, along with a problematic
+    /// span, so that the pattern migration lint can desugar it during THIR construction.
+    fn add_rust_2024_migration_desugared_pat(
+        &self,
+        pat_id: HirId,
+        subpat_span: Span,
+        cutoff_span: Span,
+        detailed_label: &str,
+    ) {
+        // Try to trim the span we're labeling to just the `&` or binding mode that's an issue.
+        // If the subpattern's span is is from an expansion, the emitted label will not be trimmed.
+        let source_map = self.tcx.sess.source_map();
+        let cutoff_span = source_map
+            .span_extend_prev_while(cutoff_span, char::is_whitespace)
+            .unwrap_or(cutoff_span);
+        // Ensure we use the syntax context and thus edition of `subpat_span`; this will be a hard
+        // error if the subpattern is of edition >= 2024.
+        let trimmed_span = subpat_span.until(cutoff_span).with_ctxt(subpat_span.ctxt());
+
+        // Only provide a detailed label if the problematic subpattern isn't from an expansion.
+        // In the case that it's from a macro, we'll add a more detailed note in the emitter.
+        let desc = if subpat_span.from_expansion() {
+            "default binding mode is reset within expansion"
+        } else {
+            detailed_label
+        };
+
+        self.typeck_results
+            .borrow_mut()
+            .rust_2024_migration_desugared_pats_mut()
+            .entry(pat_id)
+            .or_default()
+            .push((trimmed_span, desc.to_owned()));
+    }
 }
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 58465ec1cd9..6e823957cc6 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1828,7 +1828,7 @@ impl KeywordIdents {
     fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) {
         // Check if the preceding token is `$`, because we want to allow `$async`, etc.
         let mut prev_dollar = false;
-        for tt in tokens.trees() {
+        for tt in tokens.iter() {
             match tt {
                 // Only report non-raw idents.
                 TokenTree::Token(token, _) => {
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index d32666d8895..b31a4c74787 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -170,27 +170,11 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
                                 | PatKind::TupleStruct(qpath, ..)
                                 | PatKind::Struct(qpath, ..),
                             ..
-                        }) => {
-                            if let QPath::TypeRelative(qpath_ty, ..) = qpath
-                                && qpath_ty.hir_id == ty.hir_id
-                            {
-                                Some(path.span)
-                            } else {
-                                None
-                            }
-                        }
-                        Node::Expr(Expr { kind: ExprKind::Path(qpath), .. }) => {
-                            if let QPath::TypeRelative(qpath_ty, ..) = qpath
-                                && qpath_ty.hir_id == ty.hir_id
-                            {
-                                Some(path.span)
-                            } else {
-                                None
-                            }
-                        }
-                        // Can't unify these two branches because qpath below is `&&` and above is `&`
-                        // and `A | B` paths don't play well together with adjustments, apparently.
-                        Node::Expr(Expr { kind: ExprKind::Struct(qpath, ..), .. }) => {
+                        })
+                        | Node::Expr(
+                            Expr { kind: ExprKind::Path(qpath), .. }
+                            | &Expr { kind: ExprKind::Struct(qpath, ..), .. },
+                        ) => {
                             if let QPath::TypeRelative(qpath_ty, ..) = qpath
                                 && qpath_ty.hir_id == ty.hir_id
                             {
diff --git a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
index 23f4f728906..ce280fef8b6 100644
--- a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
+++ b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
@@ -84,7 +84,7 @@ impl Expr2024 {
         let mut prev_colon = false;
         let mut prev_identifier = false;
         let mut prev_dollar = false;
-        for tt in tokens.trees() {
+        for tt in tokens.iter() {
             debug!(
                 "check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}",
                 tt
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 7f3239fa57a..98ef7d58a50 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1348,8 +1348,8 @@ pub struct BasicBlockData<'tcx> {
 }
 
 impl<'tcx> BasicBlockData<'tcx> {
-    pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> {
-        BasicBlockData { statements: vec![], terminator, is_cleanup: false }
+    pub fn new(terminator: Option<Terminator<'tcx>>, is_cleanup: bool) -> BasicBlockData<'tcx> {
+        BasicBlockData { statements: vec![], terminator, is_cleanup }
     }
 
     /// Accessor for terminator.
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 551c113aa59..f94f52e4f61 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -74,9 +74,8 @@ pub struct TypeckResults<'tcx> {
     pat_binding_modes: ItemLocalMap<BindingMode>,
 
     /// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024
-    /// migration lint. The boolean indicates whether the emitted diagnostic should be a hard error
-    /// (if any of the incompatible pattern elements are in edition 2024).
-    rust_2024_migration_desugared_pats: ItemLocalMap<bool>,
+    /// migration lint. Problematic subpatterns are stored in the `Vec` for the lint to highlight.
+    rust_2024_migration_desugared_pats: ItemLocalMap<Vec<(Span, String)>>,
 
     /// Stores the types which were implicitly dereferenced in pattern binding modes
     /// for later usage in THIR lowering. For example,
@@ -419,14 +418,18 @@ impl<'tcx> TypeckResults<'tcx> {
         LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
     }
 
-    pub fn rust_2024_migration_desugared_pats(&self) -> LocalTableInContext<'_, bool> {
+    pub fn rust_2024_migration_desugared_pats(
+        &self,
+    ) -> LocalTableInContext<'_, Vec<(Span, String)>> {
         LocalTableInContext {
             hir_owner: self.hir_owner,
             data: &self.rust_2024_migration_desugared_pats,
         }
     }
 
-    pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalTableInContextMut<'_, bool> {
+    pub fn rust_2024_migration_desugared_pats_mut(
+        &mut self,
+    ) -> LocalTableInContextMut<'_, Vec<(Span, String)>> {
         LocalTableInContextMut {
             hir_owner: self.hir_owner,
             data: &mut self.rust_2024_migration_desugared_pats,
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index f647486f62a..edba247c7b0 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -285,7 +285,7 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
 
 mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
 
-mir_build_rust_2024_incompatible_pat = patterns are not allowed to reset the default binding mode in edition 2024
+mir_build_rust_2024_incompatible_pat = this pattern relies on behavior which may change in edition 2024
 
 mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
     .attributes = no other attributes may be applied
diff --git a/compiler/rustc_mir_build/src/builder/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs
index cca309115ba..42212f2518b 100644
--- a/compiler/rustc_mir_build/src/builder/cfg.rs
+++ b/compiler/rustc_mir_build/src/builder/cfg.rs
@@ -19,7 +19,7 @@ impl<'tcx> CFG<'tcx> {
     // it as #[inline(never)] to keep rustc's stack use in check.
     #[inline(never)]
     pub(crate) fn start_new_block(&mut self) -> BasicBlock {
-        self.basic_blocks.push(BasicBlockData::new(None))
+        self.basic_blocks.push(BasicBlockData::new(None, false))
     }
 
     pub(crate) fn start_new_cleanup_block(&mut self) -> BasicBlock {
diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs
index 34cdc288f0b..ca2e1dd7a1a 100644
--- a/compiler/rustc_mir_build/src/builder/custom/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs
@@ -64,7 +64,7 @@ pub(super) fn build_custom_mir<'tcx>(
     };
 
     body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));
-    body.basic_blocks_mut().push(BasicBlockData::new(None));
+    body.basic_blocks_mut().push(BasicBlockData::new(None, false));
     body.source_scopes.push(SourceScopeData {
         span,
         parent_scope: None,
diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs
index 538068e1fac..91e284604b6 100644
--- a/compiler/rustc_mir_build/src/builder/custom/parse.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs
@@ -199,10 +199,12 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
         match &self.thir[stmt].kind {
             StmtKind::Let { pattern, initializer: Some(initializer), .. } => {
                 let (var, ..) = self.parse_var(pattern)?;
-                let mut data = BasicBlockData::new(None);
-                data.is_cleanup = parse_by_kind!(self, *initializer, _, "basic block declaration",
-                    @variant(mir_basic_block, Normal) => false,
-                    @variant(mir_basic_block, Cleanup) => true,
+                let data = BasicBlockData::new(
+                    None,
+                    parse_by_kind!(self, *initializer, _, "basic block declaration",
+                        @variant(mir_basic_block, Normal) => false,
+                        @variant(mir_basic_block, Cleanup) => true,
+                    ),
                 );
                 let block = self.body.basic_blocks_mut().push(data);
                 self.block_map.insert(var, block);
@@ -308,8 +310,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
             ExprKind::Block { block } => &self.thir[*block],
         );
 
-        let mut data = BasicBlockData::new(None);
-        data.is_cleanup = is_cleanup;
+        let mut data = BasicBlockData::new(None, is_cleanup);
         for stmt_id in &*block.stmts {
             let stmt = self.statement_as_expr(*stmt_id)?;
             let span = self.thir[stmt].span;
diff --git a/compiler/rustc_mir_build/src/builder/misc.rs b/compiler/rustc_mir_build/src/builder/misc.rs
index a53ae05e84f..9ea56a9574f 100644
--- a/compiler/rustc_mir_build/src/builder/misc.rs
+++ b/compiler/rustc_mir_build/src/builder/misc.rs
@@ -56,10 +56,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     pub(crate) fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> {
         let tcx = self.tcx;
         let ty = place.ty(&self.local_decls, tcx).ty;
-        if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty) {
-            Operand::Move(place)
-        } else {
+        if self.infcx.type_is_copy_modulo_regions(self.param_env, ty) {
             Operand::Copy(place)
+        } else {
+            Operand::Move(place)
         }
     }
 }
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index e77d2496168..be5f8bdffb5 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1,7 +1,7 @@
 use rustc_errors::codes::*;
 use rustc_errors::{
     Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
-    MultiSpan, SubdiagMessageOp, Subdiagnostic,
+    MultiSpan, SubdiagMessageOp, Subdiagnostic, pluralize,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, Ty};
@@ -1088,18 +1088,20 @@ pub(crate) enum RustcBoxAttrReason {
 
 #[derive(LintDiagnostic)]
 #[diag(mir_build_rust_2024_incompatible_pat)]
-pub(crate) struct Rust2024IncompatiblePat {
+pub(crate) struct Rust2024IncompatiblePat<'a> {
     #[subdiagnostic]
-    pub(crate) sugg: Rust2024IncompatiblePatSugg,
+    pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>,
 }
 
-pub(crate) struct Rust2024IncompatiblePatSugg {
+pub(crate) struct Rust2024IncompatiblePatSugg<'a> {
     pub(crate) suggestion: Vec<(Span, String)>,
-    /// Whether the incompatibility is a hard error because a relevant span is in edition 2024.
-    pub(crate) is_hard_error: bool,
+    pub(crate) ref_pattern_count: usize,
+    pub(crate) binding_mode_count: usize,
+    /// Labeled spans for subpatterns invalid in Rust 2024.
+    pub(crate) labels: &'a [(Span, String)],
 }
 
-impl Subdiagnostic for Rust2024IncompatiblePatSugg {
+impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
         self,
         diag: &mut Diag<'_, G>,
@@ -1111,6 +1113,16 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
             } else {
                 Applicability::MaybeIncorrect
             };
-        diag.multipart_suggestion("desugar the match ergonomics", self.suggestion, applicability);
+        let plural_derefs = pluralize!(self.ref_pattern_count);
+        let and_modes = if self.binding_mode_count > 0 {
+            format!(" and variable binding mode{}", pluralize!(self.binding_mode_count))
+        } else {
+            String::new()
+        };
+        diag.multipart_suggestion_verbose(
+            format!("make the implied reference pattern{plural_derefs}{and_modes} explicit"),
+            self.suggestion,
+            applicability,
+        );
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 2dbc8b7b573..bdf243c87b6 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -6,6 +6,7 @@ mod const_to_pat;
 use std::cmp::Ordering;
 
 use rustc_abi::{FieldIdx, Integer};
+use rustc_errors::MultiSpan;
 use rustc_errors::codes::*;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
@@ -34,7 +35,7 @@ struct PatCtxt<'a, 'tcx> {
     typeck_results: &'a ty::TypeckResults<'tcx>,
 
     /// Used by the Rust 2024 migration lint.
-    rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg>,
+    rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg<'a>>,
 }
 
 pub(super) fn pat_from_hir<'a, 'tcx>(
@@ -50,24 +51,36 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
         rust_2024_migration_suggestion: typeck_results
             .rust_2024_migration_desugared_pats()
             .get(pat.hir_id)
-            .map(|&is_hard_error| Rust2024IncompatiblePatSugg {
+            .map(|labels| Rust2024IncompatiblePatSugg {
                 suggestion: Vec::new(),
-                is_hard_error,
+                ref_pattern_count: 0,
+                binding_mode_count: 0,
+                labels: labels.as_slice(),
             }),
     };
     let result = pcx.lower_pattern(pat);
     debug!("pat_from_hir({:?}) = {:?}", pat, result);
     if let Some(sugg) = pcx.rust_2024_migration_suggestion {
-        if sugg.is_hard_error {
+        let mut spans = MultiSpan::from_spans(sugg.labels.iter().map(|(span, _)| *span).collect());
+        for (span, label) in sugg.labels {
+            spans.push_span_label(*span, label.clone());
+        }
+        // If a relevant span is from at least edition 2024, this is a hard error.
+        let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
+        if is_hard_error {
             let mut err =
-                tcx.dcx().struct_span_err(pat.span, fluent::mir_build_rust_2024_incompatible_pat);
+                tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
+            if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
+                // provide the same reference link as the lint
+                err.note(format!("for more information, see {}", info.reference));
+            }
             err.subdiagnostic(sugg);
             err.emit();
         } else {
             tcx.emit_node_span_lint(
                 lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
                 pat.hir_id,
-                pat.span,
+                spans,
                 Rust2024IncompatiblePat { sugg },
             );
         }
@@ -133,6 +146,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                 })
                 .collect();
             s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
+            s.ref_pattern_count += adjustments.len();
         };
 
         adjusted_pat
@@ -371,7 +385,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                     s.suggestion.push((
                         pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
                         sugg_str.to_owned(),
-                    ))
+                    ));
+                    s.binding_mode_count += 1;
                 }
 
                 // A ref x pattern is the same node used for x, and as such it has
diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs
index d18e9fa33f0..a03aecee7be 100644
--- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs
@@ -35,7 +35,6 @@ where
 {
     fn visit_block_start(&mut self, _state: &A::Domain) {}
 
-    /// // njn: grep for "before", "primary", etc.
     /// Called after the "early" effect of the given statement is applied to `state`.
     fn visit_after_early_statement_effect(
         &mut self,
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index 17c8348140a..91e1395e764 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -129,18 +129,29 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
 
             let mut patch = MirPatch::new(body);
 
-            // create temp to store second discriminant in, `_s` in example above
-            let second_discriminant_temp =
-                patch.new_temp(opt_data.child_ty, opt_data.child_source.span);
+            let (second_discriminant_temp, second_operand) = if opt_data.need_hoist_discriminant {
+                // create temp to store second discriminant in, `_s` in example above
+                let second_discriminant_temp =
+                    patch.new_temp(opt_data.child_ty, opt_data.child_source.span);
 
-            patch.add_statement(parent_end, StatementKind::StorageLive(second_discriminant_temp));
+                patch.add_statement(
+                    parent_end,
+                    StatementKind::StorageLive(second_discriminant_temp),
+                );
 
-            // create assignment of discriminant
-            patch.add_assign(
-                parent_end,
-                Place::from(second_discriminant_temp),
-                Rvalue::Discriminant(opt_data.child_place),
-            );
+                // create assignment of discriminant
+                patch.add_assign(
+                    parent_end,
+                    Place::from(second_discriminant_temp),
+                    Rvalue::Discriminant(opt_data.child_place),
+                );
+                (
+                    Some(second_discriminant_temp),
+                    Operand::Move(Place::from(second_discriminant_temp)),
+                )
+            } else {
+                (None, Operand::Copy(opt_data.child_place))
+            };
 
             // create temp to store inequality comparison between the two discriminants, `_t` in
             // example above
@@ -149,11 +160,9 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
             let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span);
             patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp));
 
-            // create inequality comparison between the two discriminants
-            let comp_rvalue = Rvalue::BinaryOp(
-                nequal,
-                Box::new((parent_op.clone(), Operand::Move(Place::from(second_discriminant_temp)))),
-            );
+            // create inequality comparison
+            let comp_rvalue =
+                Rvalue::BinaryOp(nequal, Box::new((parent_op.clone(), second_operand)));
             patch.add_statement(
                 parent_end,
                 StatementKind::Assign(Box::new((Place::from(comp_temp), comp_rvalue))),
@@ -170,14 +179,17 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
             let eq_targets = SwitchTargets::new(eq_new_targets, parent_targets.otherwise());
 
             // Create `bbEq` in example above
-            let eq_switch = BasicBlockData::new(Some(Terminator {
-                source_info: bbs[parent].terminator().source_info,
-                kind: TerminatorKind::SwitchInt {
-                    // switch on the first discriminant, so we can mark the second one as dead
-                    discr: parent_op,
-                    targets: eq_targets,
-                },
-            }));
+            let eq_switch = BasicBlockData::new(
+                Some(Terminator {
+                    source_info: bbs[parent].terminator().source_info,
+                    kind: TerminatorKind::SwitchInt {
+                        // switch on the first discriminant, so we can mark the second one as dead
+                        discr: parent_op,
+                        targets: eq_targets,
+                    },
+                }),
+                bbs[parent].is_cleanup,
+            );
 
             let eq_bb = patch.new_block(eq_switch);
 
@@ -189,8 +201,13 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
                 TerminatorKind::if_(Operand::Move(Place::from(comp_temp)), true_case, false_case),
             );
 
-            // generate StorageDead for the second_discriminant_temp not in use anymore
-            patch.add_statement(parent_end, StatementKind::StorageDead(second_discriminant_temp));
+            if let Some(second_discriminant_temp) = second_discriminant_temp {
+                // generate StorageDead for the second_discriminant_temp not in use anymore
+                patch.add_statement(
+                    parent_end,
+                    StatementKind::StorageDead(second_discriminant_temp),
+                );
+            }
 
             // Generate a StorageDead for comp_temp in each of the targets, since we moved it into
             // the switch
@@ -218,6 +235,7 @@ struct OptimizationData<'tcx> {
     child_place: Place<'tcx>,
     child_ty: Ty<'tcx>,
     child_source: SourceInfo,
+    need_hoist_discriminant: bool,
 }
 
 fn evaluate_candidate<'tcx>(
@@ -226,49 +244,21 @@ fn evaluate_candidate<'tcx>(
     parent: BasicBlock,
 ) -> Option<OptimizationData<'tcx>> {
     let bbs = &body.basic_blocks;
+    // NB: If this BB is a cleanup, we may need to figure out what else needs to be handled.
+    if bbs[parent].is_cleanup {
+        return None;
+    }
     let TerminatorKind::SwitchInt { targets, discr: parent_discr } = &bbs[parent].terminator().kind
     else {
         return None;
     };
     let parent_ty = parent_discr.ty(body.local_decls(), tcx);
-    if !bbs[targets.otherwise()].is_empty_unreachable() {
-        // Someone could write code like this:
-        // ```rust
-        // let Q = val;
-        // if discriminant(P) == otherwise {
-        //     let ptr = &mut Q as *mut _ as *mut u8;
-        //     // It may be difficult for us to effectively determine whether values are valid.
-        //     // Invalid values can come from all sorts of corners.
-        //     unsafe { *ptr = 10; }
-        // }
-        //
-        // match P {
-        //    A => match Q {
-        //        A => {
-        //            // code
-        //        }
-        //        _ => {
-        //            // don't use Q
-        //        }
-        //    }
-        //    _ => {
-        //        // don't use Q
-        //    }
-        // };
-        // ```
-        //
-        // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant
-        // of an invalid value, which is UB.
-        // In order to fix this, **we would either need to show that the discriminant computation of
-        // `place` is computed in all branches**.
-        // FIXME(#95162) For the moment, we adopt a conservative approach and
-        // consider only the `otherwise` branch has no statements and an unreachable terminator.
-        return None;
-    }
     let (_, child) = targets.iter().next()?;
-    let child_terminator = &bbs[child].terminator();
-    let TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr } =
-        &child_terminator.kind
+
+    let Terminator {
+        kind: TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr },
+        source_info,
+    } = bbs[child].terminator()
     else {
         return None;
     };
@@ -276,25 +266,115 @@ fn evaluate_candidate<'tcx>(
     if child_ty != parent_ty {
         return None;
     }
-    let Some(StatementKind::Assign(boxed)) = &bbs[child].statements.first().map(|x| &x.kind) else {
+
+    // We only handle:
+    // ```
+    // bb4: {
+    //     _8 = discriminant((_3.1: Enum1));
+    //    switchInt(move _8) -> [2: bb7, otherwise: bb1];
+    // }
+    // ```
+    // and
+    // ```
+    // bb2: {
+    //     switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1];
+    // }
+    // ```
+    if bbs[child].statements.len() > 1 {
         return None;
+    }
+
+    // When thie BB has exactly one statement, this statement should be discriminant.
+    let need_hoist_discriminant = bbs[child].statements.len() == 1;
+    let child_place = if need_hoist_discriminant {
+        if !bbs[targets.otherwise()].is_empty_unreachable() {
+            // Someone could write code like this:
+            // ```rust
+            // let Q = val;
+            // if discriminant(P) == otherwise {
+            //     let ptr = &mut Q as *mut _ as *mut u8;
+            //     // It may be difficult for us to effectively determine whether values are valid.
+            //     // Invalid values can come from all sorts of corners.
+            //     unsafe { *ptr = 10; }
+            // }
+            //
+            // match P {
+            //    A => match Q {
+            //        A => {
+            //            // code
+            //        }
+            //        _ => {
+            //            // don't use Q
+            //        }
+            //    }
+            //    _ => {
+            //        // don't use Q
+            //    }
+            // };
+            // ```
+            //
+            // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an
+            // invalid value, which is UB.
+            // In order to fix this, **we would either need to show that the discriminant computation of
+            // `place` is computed in all branches**.
+            // FIXME(#95162) For the moment, we adopt a conservative approach and
+            // consider only the `otherwise` branch has no statements and an unreachable terminator.
+            return None;
+        }
+        // Handle:
+        // ```
+        // bb4: {
+        //     _8 = discriminant((_3.1: Enum1));
+        //    switchInt(move _8) -> [2: bb7, otherwise: bb1];
+        // }
+        // ```
+        let [
+            Statement {
+                kind: StatementKind::Assign(box (_, Rvalue::Discriminant(child_place))),
+                ..
+            },
+        ] = bbs[child].statements.as_slice()
+        else {
+            return None;
+        };
+        *child_place
+    } else {
+        // Handle:
+        // ```
+        // bb2: {
+        //     switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1];
+        // }
+        // ```
+        let Operand::Copy(child_place) = child_discr else {
+            return None;
+        };
+        *child_place
     };
-    let (_, Rvalue::Discriminant(child_place)) = &**boxed else {
-        return None;
+    let destination = if need_hoist_discriminant || bbs[targets.otherwise()].is_empty_unreachable()
+    {
+        child_targets.otherwise()
+    } else {
+        targets.otherwise()
     };
-    let destination = child_targets.otherwise();
 
     // Verify that the optimization is legal for each branch
     for (value, child) in targets.iter() {
-        if !verify_candidate_branch(&bbs[child], value, *child_place, destination) {
+        if !verify_candidate_branch(
+            &bbs[child],
+            value,
+            child_place,
+            destination,
+            need_hoist_discriminant,
+        ) {
             return None;
         }
     }
     Some(OptimizationData {
         destination,
-        child_place: *child_place,
+        child_place,
         child_ty,
-        child_source: child_terminator.source_info,
+        child_source: *source_info,
+        need_hoist_discriminant,
     })
 }
 
@@ -303,31 +383,48 @@ fn verify_candidate_branch<'tcx>(
     value: u128,
     place: Place<'tcx>,
     destination: BasicBlock,
+    need_hoist_discriminant: bool,
 ) -> bool {
-    // In order for the optimization to be correct, the branch must...
-    // ...have exactly one statement
-    if let [statement] = branch.statements.as_slice()
-        // ...assign the discriminant of `place` in that statement
-        && let StatementKind::Assign(boxed) = &statement.kind
-        && let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed
-        && *from_place == place
-        // ...make that assignment to a local
-        && discr_place.projection.is_empty()
-        // ...terminate on a `SwitchInt` that invalidates that local
-        && let TerminatorKind::SwitchInt { discr: switch_op, targets, .. } =
-            &branch.terminator().kind
-        && *switch_op == Operand::Move(*discr_place)
-        // ...fall through to `destination` if the switch misses
-        && destination == targets.otherwise()
-        // ...have a branch for value `value`
-        && let mut iter = targets.iter()
-        && let Some((target_value, _)) = iter.next()
-        && target_value == value
-        // ...and have no more branches
-        && iter.next().is_none()
-    {
-        true
+    // In order for the optimization to be correct, the terminator must be a `SwitchInt`.
+    let TerminatorKind::SwitchInt { discr: switch_op, targets } = &branch.terminator().kind else {
+        return false;
+    };
+    if need_hoist_discriminant {
+        // If we need hoist discriminant, the branch must have exactly one statement.
+        let [statement] = branch.statements.as_slice() else {
+            return false;
+        };
+        // The statement must assign the discriminant of `place`.
+        let StatementKind::Assign(box (discr_place, Rvalue::Discriminant(from_place))) =
+            statement.kind
+        else {
+            return false;
+        };
+        if from_place != place {
+            return false;
+        }
+        // The assignment must invalidate a local that terminate on a `SwitchInt`.
+        if !discr_place.projection.is_empty() || *switch_op != Operand::Move(discr_place) {
+            return false;
+        }
     } else {
-        false
+        // If we don't need hoist discriminant, the branch must not have any statements.
+        if !branch.statements.is_empty() {
+            return false;
+        }
+        // The place on `SwitchInt` must be the same.
+        if *switch_op != Operand::Copy(place) {
+            return false;
+        }
     }
+    // It must fall through to `destination` if the switch misses.
+    if destination != targets.otherwise() {
+        return false;
+    }
+    // It must have exactly one branch for value `value` and have no more branches.
+    let mut iter = targets.iter();
+    let (Some((target_value, _)), None) = (iter.next(), iter.next()) else {
+        return false;
+    };
+    target_value == value
 }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index f0acbaf56b6..35699acb318 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -572,11 +572,13 @@ impl<'tcx> Inliner<'tcx> {
         let return_block = if let Some(block) = target {
             // Prepare a new block for code that should execute when call returns. We don't use
             // target block directly since it might have other predecessors.
-            let mut data = BasicBlockData::new(Some(Terminator {
-                source_info: terminator.source_info,
-                kind: TerminatorKind::Goto { target: block },
-            }));
-            data.is_cleanup = caller_body[block].is_cleanup;
+            let data = BasicBlockData::new(
+                Some(Terminator {
+                    source_info: terminator.source_info,
+                    kind: TerminatorKind::Goto { target: block },
+                }),
+                caller_body[block].is_cleanup,
+            );
             Some(caller_body.basic_blocks_mut().push(data))
         } else {
             None
diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
index 139b25be0ab..f01bab75c4a 100644
--- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
+++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
@@ -96,7 +96,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
             typing_env,
 
             stack: Vec::with_capacity(Self::MAX_STACK_LEN),
-            last_bb: bbs.push(BasicBlockData::new(None)),
+            last_bb: bbs.push(BasicBlockData::new(None, false)),
             top_cleanup_bb: match tcx.sess.panic_strategy() {
                 PanicStrategy::Unwind => {
                     // Don't drop input arg because it's just a pointer
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index d1a725e729a..8417701ac0c 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -22,7 +22,7 @@ use rustc_errors::{
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::AllKeywords;
+use rustc_span::symbol::used_keywords;
 use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 use tracing::{debug, trace};
@@ -811,12 +811,12 @@ impl<'a> Parser<'a> {
             // so that it gets generated only when the diagnostic needs it.
             // Also, it is unlikely that this list is generated multiple times because the
             // parser halts after execution hits this path.
-            let all_keywords = AllKeywords::new().collect_used(|| prev_ident.span.edition());
+            let all_keywords = used_keywords(|| prev_ident.span.edition());
 
             // Otherwise, check the previous token with all the keywords as possible candidates.
             // This handles code like `Struct Human;` and `While a < b {}`.
-            // We check the previous token only when the current token is an identifier to avoid false
-            // positives like suggesting keyword `for` for `extern crate foo {}`.
+            // We check the previous token only when the current token is an identifier to avoid
+            // false positives like suggesting keyword `for` for `extern crate foo {}`.
             if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) {
                 err.subdiagnostic(misspelled_kw);
                 // We don't want other suggestions to be added as they are most likely meaningless
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 5a377464223..2f34dcb9308 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -8,6 +8,7 @@ use ast::token::IdentIsRaw;
 use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment, Recovered};
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
+use rustc_ast::tokenstream::TokenTree;
 use rustc_ast::util::case::Case;
 use rustc_ast::util::classify;
 use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};
@@ -2392,7 +2393,8 @@ impl<'a> Parser<'a> {
         }
 
         if self.token == TokenKind::Semi
-            && matches!(self.token_cursor.stack.last(), Some((.., Delimiter::Parenthesis)))
+            && let Some(last) = self.token_cursor.stack.last()
+            && let Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, _)) = last.curr()
             && self.may_recover()
         {
             // It is likely that the closure body is a block but where the
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 0d220e74c0e..4bc8f5913b2 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -24,9 +24,7 @@ use rustc_ast::ptr::P;
 use rustc_ast::token::{
     self, Delimiter, IdentIsRaw, InvisibleOrigin, MetaVarKind, Nonterminal, Token, TokenKind,
 };
-use rustc_ast::tokenstream::{
-    AttrsTarget, DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree, TokenTreeCursor,
-};
+use rustc_ast::tokenstream::{AttrsTarget, Spacing, TokenStream, TokenTree};
 use rustc_ast::util::case::Case;
 use rustc_ast::{
     self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID,
@@ -272,21 +270,48 @@ struct CaptureState {
     seen_attrs: IntervalSet<AttrId>,
 }
 
-/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that
+#[derive(Clone, Debug)]
+struct TokenTreeCursor {
+    stream: TokenStream,
+    /// Points to the current token tree in the stream. In `TokenCursor::curr`,
+    /// this can be any token tree. In `TokenCursor::stack`, this is always a
+    /// `TokenTree::Delimited`.
+    index: usize,
+}
+
+impl TokenTreeCursor {
+    #[inline]
+    fn new(stream: TokenStream) -> Self {
+        TokenTreeCursor { stream, index: 0 }
+    }
+
+    #[inline]
+    fn curr(&self) -> Option<&TokenTree> {
+        self.stream.get(self.index)
+    }
+
+    #[inline]
+    fn bump(&mut self) {
+        self.index += 1;
+    }
+}
+
+/// A `TokenStream` cursor that produces `Token`s. It's a bit odd that
 /// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b)
 /// use this type to emit them as a linear sequence. But a linear sequence is
 /// what the parser expects, for the most part.
 #[derive(Clone, Debug)]
 struct TokenCursor {
-    // Cursor for the current (innermost) token stream. The delimiters for this
-    // token stream are found in `self.stack.last()`; when that is `None` then
-    // we are in the outermost token stream which never has delimiters.
-    tree_cursor: TokenTreeCursor,
-
-    // Token streams surrounding the current one. The delimiters for stack[n]'s
-    // tokens are in `stack[n-1]`. `stack[0]` (when present) has no delimiters
-    // because it's the outermost token stream which never has delimiters.
-    stack: Vec<(TokenTreeCursor, DelimSpan, DelimSpacing, Delimiter)>,
+    // Cursor for the current (innermost) token stream. The index within the
+    // cursor can point to any token tree in the stream (or one past the end).
+    // The delimiters for this token stream are found in `self.stack.last()`;
+    // if that is `None` we are in the outermost token stream which never has
+    // delimiters.
+    curr: TokenTreeCursor,
+
+    // Token streams surrounding the current one. The index within each cursor
+    // always points to a `TokenTree::Delimited`.
+    stack: Vec<TokenTreeCursor>,
 }
 
 impl TokenCursor {
@@ -301,32 +326,33 @@ impl TokenCursor {
             // FIXME: we currently don't return `Delimiter::Invisible` open/close delims. To fix
             // #67062 we will need to, whereupon the `delim != Delimiter::Invisible` conditions
             // below can be removed.
-            if let Some(tree) = self.tree_cursor.next_ref() {
+            if let Some(tree) = self.curr.curr() {
                 match tree {
                     &TokenTree::Token(ref token, spacing) => {
                         debug_assert!(!matches!(
                             token.kind,
                             token::OpenDelim(_) | token::CloseDelim(_)
                         ));
-                        return (token.clone(), spacing);
+                        let res = (token.clone(), spacing);
+                        self.curr.bump();
+                        return res;
                     }
                     &TokenTree::Delimited(sp, spacing, delim, ref tts) => {
-                        let trees = tts.clone().into_trees();
-                        self.stack.push((
-                            mem::replace(&mut self.tree_cursor, trees),
-                            sp,
-                            spacing,
-                            delim,
-                        ));
+                        let trees = TokenTreeCursor::new(tts.clone());
+                        self.stack.push(mem::replace(&mut self.curr, trees));
                         if !delim.skip() {
                             return (Token::new(token::OpenDelim(delim), sp.open), spacing.open);
                         }
                         // No open delimiter to return; continue on to the next iteration.
                     }
                 };
-            } else if let Some((tree_cursor, span, spacing, delim)) = self.stack.pop() {
+            } else if let Some(parent) = self.stack.pop() {
                 // We have exhausted this token stream. Move back to its parent token stream.
-                self.tree_cursor = tree_cursor;
+                let Some(&TokenTree::Delimited(span, spacing, delim, _)) = parent.curr() else {
+                    panic!("parent should be Delimited")
+                };
+                self.curr = parent;
+                self.curr.bump(); // move past the `Delimited`
                 if !delim.skip() {
                     return (Token::new(token::CloseDelim(delim), span.close), spacing.close);
                 }
@@ -465,7 +491,7 @@ impl<'a> Parser<'a> {
             capture_cfg: false,
             restrictions: Restrictions::empty(),
             expected_tokens: Vec::new(),
-            token_cursor: TokenCursor { tree_cursor: stream.into_trees(), stack: Vec::new() },
+            token_cursor: TokenCursor { curr: TokenTreeCursor::new(stream), stack: Vec::new() },
             num_bump_calls: 0,
             break_last_token: 0,
             unmatched_angle_bracket_count: 0,
@@ -1191,7 +1217,7 @@ impl<'a> Parser<'a> {
         if dist == 1 {
             // The index is zero because the tree cursor's index always points
             // to the next token to be gotten.
-            match self.token_cursor.tree_cursor.look_ahead(0) {
+            match self.token_cursor.curr.curr() {
                 Some(tree) => {
                     // Indexing stayed within the current token tree.
                     match tree {
@@ -1201,12 +1227,13 @@ impl<'a> Parser<'a> {
                                 return looker(&Token::new(token::OpenDelim(delim), dspan.open));
                             }
                         }
-                    };
+                    }
                 }
                 None => {
                     // The tree cursor lookahead went (one) past the end of the
                     // current token tree. Try to return a close delimiter.
-                    if let Some(&(_, span, _, delim)) = self.token_cursor.stack.last()
+                    if let Some(last) = self.token_cursor.stack.last()
+                        && let Some(&TokenTree::Delimited(span, _, delim, _)) = last.curr()
                         && !delim.skip()
                     {
                         // We are not in the outermost token stream, so we have
@@ -1398,9 +1425,10 @@ impl<'a> Parser<'a> {
     pub fn parse_token_tree(&mut self) -> TokenTree {
         match self.token.kind {
             token::OpenDelim(..) => {
-                // Grab the tokens within the delimiters.
-                let stream = self.token_cursor.tree_cursor.stream.clone();
-                let (_, span, spacing, delim) = *self.token_cursor.stack.last().unwrap();
+                // Clone the `TokenTree::Delimited` that we are currently
+                // within. That's what we are going to return.
+                let tree = self.token_cursor.stack.last().unwrap().curr().unwrap().clone();
+                debug_assert_matches!(tree, TokenTree::Delimited(..));
 
                 // Advance the token cursor through the entire delimited
                 // sequence. After getting the `OpenDelim` we are *within* the
@@ -1420,7 +1448,7 @@ impl<'a> Parser<'a> {
 
                 // Consume close delimiter
                 self.bump();
-                TokenTree::Delimited(span, spacing, delim, stream)
+                tree
             }
             token::CloseDelim(_) | token::Eof => unreachable!(),
             _ => {
diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs
index ca9e78be201..3f8d66c2c95 100644
--- a/compiler/rustc_parse/src/parser/tests.rs
+++ b/compiler/rustc_parse/src/parser/tests.rs
@@ -2286,7 +2286,7 @@ fn bad_path_expr_1() {
 fn string_to_tts_macro() {
     create_default_session_globals_then(|| {
         let stream = string_to_stream("macro_rules! zip (($a)=>($a))".to_string());
-        let tts = &stream.trees().collect::<Vec<_>>()[..];
+        let tts = &stream.iter().collect::<Vec<_>>()[..];
 
         match tts {
             [
@@ -2298,14 +2298,14 @@ fn string_to_tts_macro() {
                 TokenTree::Token(Token { kind: token::Ident(name_zip, IdentIsRaw::No), .. }, _),
                 TokenTree::Delimited(.., macro_delim, macro_tts),
             ] if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" => {
-                let tts = &macro_tts.trees().collect::<Vec<_>>();
+                let tts = &macro_tts.iter().collect::<Vec<_>>();
                 match &tts[..] {
                     [
                         TokenTree::Delimited(.., first_delim, first_tts),
                         TokenTree::Token(Token { kind: token::FatArrow, .. }, _),
                         TokenTree::Delimited(.., second_delim, second_tts),
                     ] if macro_delim == &Delimiter::Parenthesis => {
-                        let tts = &first_tts.trees().collect::<Vec<_>>();
+                        let tts = &first_tts.iter().collect::<Vec<_>>();
                         match &tts[..] {
                             [
                                 TokenTree::Token(Token { kind: token::Dollar, .. }, _),
@@ -2317,7 +2317,7 @@ fn string_to_tts_macro() {
                             }
                             _ => panic!("value 3: {:?} {:?}", first_delim, first_tts),
                         }
-                        let tts = &second_tts.trees().collect::<Vec<_>>();
+                        let tts = &second_tts.iter().collect::<Vec<_>>();
                         match &tts[..] {
                             [
                                 TokenTree::Token(Token { kind: token::Dollar, .. }, _),
@@ -2545,7 +2545,7 @@ fn ttdelim_span() {
         .unwrap();
 
         let ast::ExprKind::MacCall(mac) = &expr.kind else { panic!("not a macro") };
-        let span = mac.args.tokens.trees().last().unwrap().span();
+        let span = mac.args.tokens.iter().last().unwrap().span();
 
         match psess.source_map().span_to_snippet(span) {
             Ok(s) => assert_eq!(&s[..], "{ body }"),
diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs
index b13b68c266a..037b5b1a9de 100644
--- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs
+++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs
@@ -23,8 +23,8 @@ fn test_concat() {
         let mut eq_res = TokenStream::default();
         eq_res.push_stream(test_fst);
         eq_res.push_stream(test_snd);
-        assert_eq!(test_res.trees().count(), 5);
-        assert_eq!(eq_res.trees().count(), 5);
+        assert_eq!(test_res.iter().count(), 5);
+        assert_eq!(eq_res.iter().count(), 5);
         assert_eq!(test_res.eq_unspanned(&eq_res), true);
     })
 }
@@ -33,7 +33,7 @@ fn test_concat() {
 fn test_to_from_bijection() {
     create_default_session_globals_then(|| {
         let test_start = string_to_ts("foo::bar(baz)");
-        let test_end = test_start.trees().cloned().collect();
+        let test_end = test_start.iter().cloned().collect();
         assert_eq!(test_start, test_end)
     })
 }
@@ -105,6 +105,6 @@ fn test_dotdotdot() {
         stream.push_tree(TokenTree::token_joint(token::Dot, sp(1, 2)));
         stream.push_tree(TokenTree::token_alone(token::Dot, sp(2, 3)));
         assert!(stream.eq_unspanned(&string_to_ts("...")));
-        assert_eq!(stream.trees().count(), 1);
+        assert_eq!(stream.iter().count(), 1);
     })
 }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 936c2ca87d6..4784a4d1953 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1076,7 +1076,7 @@ impl OutputFilenames {
         self.with_directory_and_extension(&self.out_directory, extension)
     }
 
-    pub fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
+    pub fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf {
         let mut path = directory.join(&self.filestem);
         path.set_extension(extension);
         path
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 7d99ca5a31e..a7ff0576f92 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -20,18 +20,26 @@ mod tests;
 
 // The proc macro code for this is in `compiler/rustc_macros/src/symbols.rs`.
 symbols! {
-    // If you modify this list, adjust `is_special`, `is_used_keyword`/`is_unused_keyword`
-    // and `AllKeywords`.
+    // This list includes things that are definitely keywords (e.g. `if`),
+    // a few things that are definitely not keywords (e.g. the empty symbol,
+    // `{{root}}`) and things where there is disagreement between people and/or
+    // documents (such as the Rust Reference) about whether it is a keyword
+    // (e.g. `_`).
+    //
+    // If you modify this list, adjust any relevant `Symbol::{is,can_be}_*` predicates and
+    // `used_keywords`.
     // But this should rarely be necessary if the keywords are kept in alphabetic order.
     Keywords {
         // Special reserved identifiers used internally for elided lifetimes,
         // unnamed method parameters, crate root module, error recovery etc.
+        // Matching predicates: `is_any_keyword`, `is_special`/`is_reserved`
         Empty:              "",
         PathRoot:           "{{root}}",
         DollarCrate:        "$crate",
         Underscore:         "_",
 
         // Keywords that are used in stable Rust.
+        // Matching predicates: `is_any_keyword`, `is_used_keyword_always`/`is_reserved`
         As:                 "as",
         Break:              "break",
         Const:              "const",
@@ -69,6 +77,7 @@ symbols! {
         While:              "while",
 
         // Keywords that are used in unstable Rust or reserved for future use.
+        // Matching predicates: `is_any_keyword`, `is_unused_keyword_always`/`is_reserved`
         Abstract:           "abstract",
         Become:             "become",
         Box:                "box",
@@ -83,23 +92,29 @@ symbols! {
         Yield:              "yield",
 
         // Edition-specific keywords that are used in stable Rust.
+        // Matching predicates: `is_any_keyword`, `is_used_keyword_conditional`/`is_reserved` (if
+        // the edition suffices)
         Async:              "async", // >= 2018 Edition only
         Await:              "await", // >= 2018 Edition only
         Dyn:                "dyn", // >= 2018 Edition only
 
         // Edition-specific keywords that are used in unstable Rust or reserved for future use.
+        // Matching predicates: `is_any_keyword`, `is_unused_keyword_conditional`/`is_reserved` (if
+        // the edition suffices)
+        Gen:                "gen", // >= 2024 Edition only
         Try:                "try", // >= 2018 Edition only
 
-        // Special lifetime names
+        // "Lifetime keywords": regular keywords with a leading `'`.
+        // Matching predicates: `is_any_keyword`
         UnderscoreLifetime: "'_",
         StaticLifetime:     "'static",
 
         // Weak keywords, have special meaning only in specific contexts.
+        // Matching predicates: `is_any_keyword`
         Auto:               "auto",
         Builtin:            "builtin",
         Catch:              "catch",
         Default:            "default",
-        Gen:                "gen",
         MacroRules:         "macro_rules",
         Raw:                "raw",
         Reuse:              "reuse",
@@ -2589,6 +2604,11 @@ pub mod sym {
 }
 
 impl Symbol {
+    /// Don't use this unless you're doing something very loose and heuristic-y.
+    pub fn is_any_keyword(self) -> bool {
+        self >= kw::As && self <= kw::Yeet
+    }
+
     fn is_special(self) -> bool {
         self <= kw::Underscore
     }
@@ -2606,8 +2626,8 @@ impl Symbol {
     }
 
     fn is_unused_keyword_conditional(self, edition: impl Copy + FnOnce() -> Edition) -> bool {
-        self == kw::Try && edition().at_least_rust_2018()
-            || self == kw::Gen && edition().at_least_rust_2024()
+        self == kw::Gen && edition().at_least_rust_2024()
+            || self == kw::Try && edition().at_least_rust_2018()
     }
 
     pub fn is_reserved(self, edition: impl Copy + FnOnce() -> Edition) -> bool {
@@ -2645,6 +2665,11 @@ impl Symbol {
 }
 
 impl Ident {
+    /// Don't use this unless you're doing something very loose and heuristic-y.
+    pub fn is_any_keyword(self) -> bool {
+        self.name.is_any_keyword()
+    }
+
     /// Returns `true` for reserved identifiers used internally for elided lifetimes,
     /// unnamed method parameters, crate root module, error recovery etc.
     pub fn is_special(self) -> bool {
@@ -2683,41 +2708,19 @@ impl Ident {
     }
 }
 
-/// An iterator over all the keywords in Rust.
-#[derive(Copy, Clone)]
-pub struct AllKeywords {
-    curr_idx: u32,
-    end_idx: u32,
-}
-
-impl AllKeywords {
-    /// Initialize a new iterator over all the keywords.
-    ///
-    /// *Note:* Please update this if a new keyword is added beyond the current
-    /// range.
-    pub fn new() -> Self {
-        AllKeywords { curr_idx: kw::Empty.as_u32(), end_idx: kw::Yeet.as_u32() }
-    }
-
-    /// Collect all the keywords in a given edition into a vector.
-    pub fn collect_used(&self, edition: impl Copy + FnOnce() -> Edition) -> Vec<Symbol> {
-        self.filter(|&keyword| {
-            keyword.is_used_keyword_always() || keyword.is_used_keyword_conditional(edition)
+/// Collect all the keywords in a given edition into a vector.
+///
+/// *Note:* Please update this if a new keyword is added beyond the current
+/// range.
+pub fn used_keywords(edition: impl Copy + FnOnce() -> Edition) -> Vec<Symbol> {
+    (kw::Empty.as_u32()..kw::Yeet.as_u32())
+        .filter_map(|kw| {
+            let kw = Symbol::new(kw);
+            if kw.is_used_keyword_always() || kw.is_used_keyword_conditional(edition) {
+                Some(kw)
+            } else {
+                None
+            }
         })
         .collect()
-    }
-}
-
-impl Iterator for AllKeywords {
-    type Item = Symbol;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.curr_idx <= self.end_idx {
-            let keyword = Symbol::new(self.curr_idx);
-            self.curr_idx += 1;
-            Some(keyword)
-        } else {
-            None
-        }
-    }
 }
diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs
index 5ac33516684..84cbbc71f40 100644
--- a/library/core/src/cell/lazy.rs
+++ b/library/core/src/cell/lazy.rs
@@ -219,7 +219,7 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
 }
 
 impl<T, F> LazyCell<T, F> {
-    /// Returns a reference to the value if initialized, or `None` if not.
+    /// Returns a mutable reference to the value if initialized, or `None` if not.
     ///
     /// # Examples
     ///
@@ -245,7 +245,7 @@ impl<T, F> LazyCell<T, F> {
         }
     }
 
-    /// Returns a mutable reference to the value if initialized, or `None` if not.
+    /// Returns a reference to the value if initialized, or `None` if not.
     ///
     /// # Examples
     ///
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 0cacd6e4f37..460b86163bd 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -523,6 +523,11 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
 
     let mut features = String::new();
 
+    if stage != 0 && builder.config.default_codegen_backend(target).as_deref() == Some("cranelift")
+    {
+        features += "compiler-builtins-no-f16-f128 ";
+    }
+
     if builder.no_std(target) == Some(true) {
         features += " compiler-builtins-mem";
         if !target.starts_with("bpf") {
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 7ed01f25c94..fbd0dc3ec30 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -209,7 +209,7 @@ pub fn setup(config: &Config, profile: Profile) {
     setup_config_toml(path, profile, config);
 }
 
-fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) {
+fn setup_config_toml(path: &Path, profile: Profile, config: &Config) {
     if profile == Profile::None {
         return;
     }
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 22d361ff091..435216ef534 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -1942,7 +1942,7 @@ impl Config {
                 );
 
                 let channel = config
-                    .read_file_by_commit(&PathBuf::from("src/ci/channel"), commit)
+                    .read_file_by_commit(Path::new("src/ci/channel"), commit)
                     .trim()
                     .to_owned();
 
@@ -2383,12 +2383,10 @@ impl Config {
     /// Return the version it would have used for the given commit.
     pub(crate) fn artifact_version_part(&self, commit: &str) -> String {
         let (channel, version) = if self.rust_info.is_managed_git_subrepository() {
-            let channel = self
-                .read_file_by_commit(&PathBuf::from("src/ci/channel"), commit)
-                .trim()
-                .to_owned();
+            let channel =
+                self.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned();
             let version =
-                self.read_file_by_commit(&PathBuf::from("src/version"), commit).trim().to_owned();
+                self.read_file_by_commit(Path::new("src/version"), commit).trim().to_owned();
             (channel, version)
         } else {
             let channel = fs::read_to_string(self.src.join("src/ci/channel"));
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 9e52f6884a4..ffec5dc5c58 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2604,7 +2604,7 @@ fn filter_tokens_from_list(
 ) -> Vec<TokenTree> {
     let mut tokens = Vec::with_capacity(args_tokens.len());
     let mut skip_next_comma = false;
-    for token in args_tokens.trees() {
+    for token in args_tokens.iter() {
         match token {
             TokenTree::Token(Token { kind: TokenKind::Comma, .. }, _) if skip_next_comma => {
                 skip_next_comma = false;
diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs
index d39ecf83ac0..3cc5f8d615a 100644
--- a/src/librustdoc/clean/render_macro_matchers.rs
+++ b/src/librustdoc/clean/render_macro_matchers.rs
@@ -131,7 +131,7 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) {
     use State::*;
 
     let mut state = Start;
-    for tt in tts.trees() {
+    for tt in tts.iter() {
         let (needs_space, next_state) = match &tt {
             TokenTree::Token(tt, _) => match (state, &tt.kind) {
                 (Dollar, token::Ident(..)) => (false, DollarIdent),
diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
index b4ed8a68a32..fd27e30a67f 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
@@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
         }
 
         if let AttrArgs::Delimited(args) = &normal_attr.item.args
-            && let mut tt_iter = args.tokens.trees()
+            && let mut tt_iter = args.tokens.iter()
             && let Some(TokenTree::Token(
                 Token {
                     kind: TokenKind::Ident(sym::expected, _),
diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
index c8f81413728..7d86bd3e540 100644
--- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
+++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
@@ -82,11 +82,11 @@ fn is_macro_export(attr: &Attribute) -> bool {
 
 fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
     let mut prev_is_dollar = false;
-    let mut cursor = tts.trees();
-    while let Some(curr) = cursor.next() {
+    let mut iter = tts.iter();
+    while let Some(curr) = iter.next() {
         if !prev_is_dollar
             && let Some(span) = is_crate_keyword(curr)
-            && let Some(next) = cursor.look_ahead(0)
+            && let Some(next) = iter.peek()
             && is_token(next, &TokenKind::PathSep)
         {
             return Some(span);
diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs
index b605bc813f1..20e3c8dfb9e 100644
--- a/src/tools/compiletest/src/debuggers.rs
+++ b/src/tools/compiletest/src/debuggers.rs
@@ -1,6 +1,6 @@
 use std::env;
 use std::ffi::OsString;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::process::Command;
 use std::sync::Arc;
 
@@ -141,7 +141,7 @@ pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
 pub(crate) fn analyze_gdb(
     gdb: Option<String>,
     target: &str,
-    android_cross_path: &PathBuf,
+    android_cross_path: &Path,
 ) -> (Option<String>, Option<u32>) {
     #[cfg(not(windows))]
     const GDB_FALLBACK: &str = "gdb";
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index a5a166af33b..d3b4631a212 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -598,10 +598,9 @@ pub fn collect_and_make_tests(config: Arc<Config>) -> Vec<test::TestDescAndFn> {
     let mut collector =
         TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false };
 
-    collect_tests_from_dir(&cx, &mut collector, &cx.config.src_base, &PathBuf::new())
-        .unwrap_or_else(|reason| {
-            panic!("Could not read tests from {}: {reason}", cx.config.src_base.display())
-        });
+    collect_tests_from_dir(&cx, &mut collector, &cx.config.src_base, Path::new("")).unwrap_or_else(
+        |reason| panic!("Could not read tests from {}: {reason}", cx.config.src_base.display()),
+    );
 
     let TestCollector { tests, found_path_stems, poisoned } = collector;
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 8af4325e7b1..cb31b03dd2a 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2560,7 +2560,7 @@ impl<'test> TestCx<'test> {
         })
     }
 
-    fn delete_file(&self, file: &PathBuf) {
+    fn delete_file(&self, file: &Path) {
         if !file.exists() {
             // Deleting a nonexistent file would error.
             return;
diff --git a/src/tools/rustc-perf-wrapper/src/main.rs b/src/tools/rustc-perf-wrapper/src/main.rs
index 951d36b788b..0b4c894e29d 100644
--- a/src/tools/rustc-perf-wrapper/src/main.rs
+++ b/src/tools/rustc-perf-wrapper/src/main.rs
@@ -163,7 +163,7 @@ fn apply_shared_opts(cmd: &mut Command, opts: &SharedOpts) {
     }
 }
 
-fn execute_benchmark(cmd: &mut Command, compiler: &PathBuf) {
+fn execute_benchmark(cmd: &mut Command, compiler: &Path) {
     cmd.arg(compiler);
     println!("Running `rustc-perf` using `{}`", compiler.display());
 
diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs
index 4083d9398f6..ea8ca38cb77 100644
--- a/src/tools/rustfmt/src/macros.rs
+++ b/src/tools/rustfmt/src/macros.rs
@@ -13,7 +13,7 @@ use std::collections::HashMap;
 use std::panic::{AssertUnwindSafe, catch_unwind};
 
 use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind};
-use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{TokenStream, TokenStreamIter, TokenTree};
 use rustc_ast::{ast, ptr};
 use rustc_ast_pretty::pprust;
 use rustc_span::{
@@ -443,7 +443,7 @@ pub(crate) fn rewrite_macro_def(
     }
 
     let ts = def.body.tokens.clone();
-    let mut parser = MacroParser::new(ts.trees());
+    let mut parser = MacroParser::new(ts.iter());
     let parsed_def = match parser.parse() {
         Some(def) => def,
         None => return snippet,
@@ -794,7 +794,7 @@ impl MacroArgParser {
         self.buf.clear();
     }
 
-    fn add_meta_variable(&mut self, iter: &mut RefTokenTreeCursor<'_>) -> Option<()> {
+    fn add_meta_variable(&mut self, iter: &mut TokenStreamIter<'_>) -> Option<()> {
         match iter.next() {
             Some(&TokenTree::Token(
                 Token {
@@ -826,7 +826,7 @@ impl MacroArgParser {
         &mut self,
         inner: Vec<ParsedMacroArg>,
         delim: Delimiter,
-        iter: &mut RefTokenTreeCursor<'_>,
+        iter: &mut TokenStreamIter<'_>,
     ) -> Option<()> {
         let mut buffer = String::new();
         let mut first = true;
@@ -926,7 +926,7 @@ impl MacroArgParser {
 
     /// Returns a collection of parsed macro def's arguments.
     fn parse(mut self, tokens: TokenStream) -> Option<Vec<ParsedMacroArg>> {
-        let mut iter = tokens.trees();
+        let mut iter = tokens.iter();
 
         while let Some(tok) = iter.next() {
             match tok {
@@ -1063,7 +1063,7 @@ fn format_macro_args(
 }
 
 fn span_for_token_stream(token_stream: &TokenStream) -> Option<Span> {
-    token_stream.trees().next().map(|tt| tt.span())
+    token_stream.iter().next().map(|tt| tt.span())
 }
 
 // We should insert a space if the next token is a:
@@ -1179,18 +1179,18 @@ pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> D
 // A very simple parser that just parses a macros 2.0 definition into its branches.
 // Currently we do not attempt to parse any further than that.
 struct MacroParser<'a> {
-    toks: RefTokenTreeCursor<'a>,
+    iter: TokenStreamIter<'a>,
 }
 
 impl<'a> MacroParser<'a> {
-    const fn new(toks: RefTokenTreeCursor<'a>) -> Self {
-        Self { toks }
+    const fn new(iter: TokenStreamIter<'a>) -> Self {
+        Self { iter }
     }
 
     // (`(` ... `)` `=>` `{` ... `}`)*
     fn parse(&mut self) -> Option<Macro> {
         let mut branches = vec![];
-        while self.toks.look_ahead(1).is_some() {
+        while self.iter.peek().is_some() {
             branches.push(self.parse_branch()?);
         }
 
@@ -1199,13 +1199,13 @@ impl<'a> MacroParser<'a> {
 
     // `(` ... `)` `=>` `{` ... `}`
     fn parse_branch(&mut self) -> Option<MacroBranch> {
-        let tok = self.toks.next()?;
+        let tok = self.iter.next()?;
         let (lo, args_paren_kind) = match tok {
             TokenTree::Token(..) => return None,
             &TokenTree::Delimited(delimited_span, _, d, _) => (delimited_span.open.lo(), d),
         };
         let args = TokenStream::new(vec![tok.clone()]);
-        match self.toks.next()? {
+        match self.iter.next()? {
             TokenTree::Token(
                 Token {
                     kind: TokenKind::FatArrow,
@@ -1215,7 +1215,7 @@ impl<'a> MacroParser<'a> {
             ) => {}
             _ => return None,
         }
-        let (mut hi, body, whole_body) = match self.toks.next()? {
+        let (mut hi, body, whole_body) = match self.iter.next()? {
             TokenTree::Token(..) => return None,
             TokenTree::Delimited(delimited_span, ..) => {
                 let data = delimited_span.entire().data();
@@ -1237,10 +1237,10 @@ impl<'a> MacroParser<'a> {
                 span,
             },
             _,
-        )) = self.toks.look_ahead(0)
+        )) = self.iter.peek()
         {
             hi = span.hi();
-            self.toks.next();
+            self.iter.next();
         }
         Some(MacroBranch {
             span: mk_sp(lo, hi),
diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs
index 7271e73db8d..680a35f7e03 100644
--- a/src/tools/rustfmt/src/parse/macros/mod.rs
+++ b/src/tools/rustfmt/src/parse/macros/mod.rs
@@ -4,8 +4,7 @@ use rustc_ast::{ast, ptr};
 use rustc_parse::MACRO_ARGUMENTS;
 use rustc_parse::parser::{ForceCollect, Parser, Recovery};
 use rustc_session::parse::ParseSess;
-use rustc_span::Symbol;
-use rustc_span::symbol::{self, kw};
+use rustc_span::symbol;
 
 use crate::macros::MacroArg;
 use crate::rewrite::RewriteContext;
@@ -82,18 +81,18 @@ pub(crate) struct ParsedMacroArgs {
 }
 
 fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
-    for &keyword in RUST_KW.iter() {
-        if parser.token.is_keyword(keyword)
-            && parser.look_ahead(1, |t| *t == TokenKind::Eof || *t == TokenKind::Comma)
-        {
-            parser.bump();
-            return Some(MacroArg::Keyword(
-                symbol::Ident::with_dummy_span(keyword),
-                parser.prev_token.span,
-            ));
-        }
+    if parser.token.is_any_keyword()
+        && parser.look_ahead(1, |t| *t == TokenKind::Eof || *t == TokenKind::Comma)
+    {
+        let keyword = parser.token.ident().unwrap().0.name;
+        parser.bump();
+        Some(MacroArg::Keyword(
+            symbol::Ident::with_dummy_span(keyword),
+            parser.prev_token.span,
+        ))
+    } else {
+        None
     }
-    None
 }
 
 pub(crate) fn parse_macro_args(
@@ -169,65 +168,3 @@ pub(crate) fn parse_expr(
     let mut parser = build_parser(context, tokens);
     parser.parse_expr().ok()
 }
-
-const RUST_KW: [Symbol; 59] = [
-    kw::PathRoot,
-    kw::DollarCrate,
-    kw::Underscore,
-    kw::As,
-    kw::Box,
-    kw::Break,
-    kw::Const,
-    kw::Continue,
-    kw::Crate,
-    kw::Else,
-    kw::Enum,
-    kw::Extern,
-    kw::False,
-    kw::Fn,
-    kw::For,
-    kw::If,
-    kw::Impl,
-    kw::In,
-    kw::Let,
-    kw::Loop,
-    kw::Match,
-    kw::Mod,
-    kw::Move,
-    kw::Mut,
-    kw::Pub,
-    kw::Ref,
-    kw::Return,
-    kw::SelfLower,
-    kw::SelfUpper,
-    kw::Static,
-    kw::Struct,
-    kw::Super,
-    kw::Trait,
-    kw::True,
-    kw::Type,
-    kw::Unsafe,
-    kw::Use,
-    kw::Where,
-    kw::While,
-    kw::Abstract,
-    kw::Become,
-    kw::Do,
-    kw::Final,
-    kw::Macro,
-    kw::Override,
-    kw::Priv,
-    kw::Typeof,
-    kw::Unsized,
-    kw::Virtual,
-    kw::Yield,
-    kw::Dyn,
-    kw::Async,
-    kw::Try,
-    kw::UnderscoreLifetime,
-    kw::StaticLifetime,
-    kw::Auto,
-    kw::Catch,
-    kw::Default,
-    kw::Union,
-];
diff --git a/tests/assembly/asm/aarch64-el2vmsa.rs b/tests/assembly/asm/aarch64-el2vmsa.rs
index c217f008c07..3652d58d85a 100644
--- a/tests/assembly/asm/aarch64-el2vmsa.rs
+++ b/tests/assembly/asm/aarch64-el2vmsa.rs
@@ -1,18 +1,14 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target aarch64-unknown-linux-gnu
 //@ needs-llvm-components: aarch64
 
-#![feature(no_core, lang_items, rustc_attrs)]
+#![feature(no_core)]
 #![crate_type = "rlib"]
 #![no_core]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
+extern crate minicore;
+use minicore::*;
 
 // CHECK-LABEL: ttbr0_el2:
 #[no_mangle]
diff --git a/tests/assembly/asm/aarch64-modifiers.rs b/tests/assembly/asm/aarch64-modifiers.rs
index a4a41dd96c1..a3956d21a06 100644
--- a/tests/assembly/asm/aarch64-modifiers.rs
+++ b/tests/assembly/asm/aarch64-modifiers.rs
@@ -1,29 +1,17 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: -O -C panic=abort
 //@ compile-flags: --target aarch64-unknown-linux-gnu
 //@ compile-flags: -Zmerge-functions=disabled
 //@ needs-llvm-components: aarch64
 
-#![feature(no_core, lang_items, rustc_attrs)]
+#![feature(no_core)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-impl Copy for i32 {}
+extern crate minicore;
+use minicore::*;
 
 macro_rules! check {
     ($func:ident $reg:ident $code:literal) => {
diff --git a/tests/assembly/asm/aarch64-types.rs b/tests/assembly/asm/aarch64-types.rs
index 22e60cd8159..439385b14b0 100644
--- a/tests/assembly/asm/aarch64-types.rs
+++ b/tests/assembly/asm/aarch64-types.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: aarch64 arm64ec
 //@ assembly-output: emit-asm
 //@ [aarch64] compile-flags: --target aarch64-unknown-linux-gnu
@@ -6,33 +7,15 @@
 //@ [arm64ec] needs-llvm-components: aarch64
 //@ compile-flags: -Zmerge-functions=disabled
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, f16, f128)]
+#![feature(no_core, repr_simd, f16, f128)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 // FIXME(f16_f128): Only needed for FIXME in check! and check_reg!
-#![feature(auto_traits)]
+#![feature(auto_traits, lang_items)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-// Do we really need to use no_core for this?!?
-impl<T: Copy, const N: usize> Copy for [T; N] {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *mut u8;
 
@@ -65,15 +48,6 @@ pub struct f32x4([f32; 4]);
 #[repr(simd)]
 pub struct f64x2([f64; 2]);
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for f16 {}
-impl Copy for i32 {}
-impl Copy for f32 {}
-impl Copy for i64 {}
-impl Copy for f64 {}
-impl Copy for f128 {}
-impl Copy for ptr {}
 impl Copy for i8x8 {}
 impl Copy for i16x4 {}
 impl Copy for i32x2 {}
diff --git a/tests/assembly/asm/arm-modifiers.rs b/tests/assembly/asm/arm-modifiers.rs
index 7d8d7e83870..562b6bed74c 100644
--- a/tests/assembly/asm/arm-modifiers.rs
+++ b/tests/assembly/asm/arm-modifiers.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: -O -C panic=abort
 //@ compile-flags: --target armv7-unknown-linux-gnueabihf
@@ -5,38 +6,17 @@
 //@ compile-flags: -Zmerge-functions=disabled
 //@ needs-llvm-components: arm
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd)]
+#![feature(no_core, repr_simd)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-// Do we really need to use no_core for this?!?
-impl<T: Copy, const N: usize> Copy for [T; N] {}
+extern crate minicore;
+use minicore::*;
 
 #[repr(simd)]
 pub struct f32x4([f32; 4]);
 
-impl Copy for i32 {}
-impl Copy for f32 {}
-impl Copy for f64 {}
 impl Copy for f32x4 {}
 
 macro_rules! check {
diff --git a/tests/assembly/asm/arm-types.rs b/tests/assembly/asm/arm-types.rs
index 9cebb588aaf..fb93f474c20 100644
--- a/tests/assembly/asm/arm-types.rs
+++ b/tests/assembly/asm/arm-types.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: base d32 neon
 //@ assembly-output: emit-asm
 //@ compile-flags: --target armv7-unknown-linux-gnueabihf
@@ -8,31 +9,13 @@
 //@[neon] filecheck-flags: --check-prefix d32
 //@ needs-llvm-components: arm
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, f16)]
+#![feature(no_core, repr_simd, f16)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-// Do we really need to use no_core for this?!?
-impl<T: Copy, const N: usize> Copy for [T; N] {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *mut u8;
 
@@ -61,14 +44,6 @@ pub struct f16x8([f16; 8]);
 #[repr(simd)]
 pub struct f32x4([f32; 4]);
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for f16 {}
-impl Copy for f32 {}
-impl Copy for i64 {}
-impl Copy for f64 {}
-impl Copy for ptr {}
 impl Copy for i8x8 {}
 impl Copy for i16x4 {}
 impl Copy for i32x2 {}
diff --git a/tests/assembly/asm/avr-modifiers.rs b/tests/assembly/asm/avr-modifiers.rs
index e94375f9596..585fdd7b725 100644
--- a/tests/assembly/asm/avr-modifiers.rs
+++ b/tests/assembly/asm/avr-modifiers.rs
@@ -1,34 +1,18 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target avr-unknown-gnu-atmega328
 //@ needs-llvm-components: avr
 
-#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const u64;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for ptr {}
-
 macro_rules! check {
     ($func:ident $hi:literal $lo:literal $reg:tt) => {
         #[no_mangle]
diff --git a/tests/assembly/asm/avr-types.rs b/tests/assembly/asm/avr-types.rs
index 88b16895e8d..25cf3ec3b4b 100644
--- a/tests/assembly/asm/avr-types.rs
+++ b/tests/assembly/asm/avr-types.rs
@@ -1,34 +1,18 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target avr-unknown-gnu-atmega328
 //@ needs-llvm-components: avr
 
-#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const u64;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for ptr {}
-
 macro_rules! check {
     ($func:ident $ty:ident $class:ident) => {
         #[no_mangle]
diff --git a/tests/assembly/asm/bpf-types.rs b/tests/assembly/asm/bpf-types.rs
index 0a9ec7dd52b..07ea7bd5ce0 100644
--- a/tests/assembly/asm/bpf-types.rs
+++ b/tests/assembly/asm/bpf-types.rs
@@ -1,38 +1,18 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target bpfel-unknown-none -C target_feature=+alu32
 //@ needs-llvm-components: bpf
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const u64;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for ptr {}
-
 macro_rules! check {
     ($func:ident $ty:ident $class:ident) => {
         #[no_mangle]
diff --git a/tests/assembly/asm/hexagon-types.rs b/tests/assembly/asm/hexagon-types.rs
index 9389fcf9cba..ce80fa75b35 100644
--- a/tests/assembly/asm/hexagon-types.rs
+++ b/tests/assembly/asm/hexagon-types.rs
@@ -1,38 +1,19 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target hexagon-unknown-linux-musl
 //@ compile-flags: -Zmerge-functions=disabled
 //@ needs-llvm-components: hexagon
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const i32;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for f32 {}
-impl Copy for ptr {}
 extern "C" {
     fn extern_func();
     static extern_static: u8;
diff --git a/tests/assembly/asm/loongarch-type.rs b/tests/assembly/asm/loongarch-type.rs
index c51d35876d9..86d9e03bc93 100644
--- a/tests/assembly/asm/loongarch-type.rs
+++ b/tests/assembly/asm/loongarch-type.rs
@@ -1,40 +1,19 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target loongarch64-unknown-linux-gnu
 //@ compile-flags: -Zmerge-functions=disabled
 //@ needs-llvm-components: loongarch
 
-#![feature(no_core, lang_items, rustc_attrs)]
+#![feature(no_core)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const i32;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for f32 {}
-impl Copy for f64 {}
-impl Copy for ptr {}
 extern "C" {
     fn extern_func();
     static extern_static: u8;
diff --git a/tests/assembly/asm/m68k-types.rs b/tests/assembly/asm/m68k-types.rs
index b3e86b709c3..9e4f6d9a1a9 100644
--- a/tests/assembly/asm/m68k-types.rs
+++ b/tests/assembly/asm/m68k-types.rs
@@ -1,34 +1,18 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target m68k-unknown-linux-gnu
 //@ needs-llvm-components: m68k
 
-#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const u64;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for ptr {}
-
 macro_rules! check {
     ($func:ident $ty:ident $class:ident $mov:literal) => {
         #[no_mangle]
diff --git a/tests/assembly/asm/mips-types.rs b/tests/assembly/asm/mips-types.rs
index f40a28be4a7..00e8ce0b874 100644
--- a/tests/assembly/asm/mips-types.rs
+++ b/tests/assembly/asm/mips-types.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: mips32 mips64
 //@ assembly-output: emit-asm
 //@[mips32] compile-flags: --target mips-unknown-linux-gnu
@@ -6,39 +7,16 @@
 //@[mips64] needs-llvm-components: mips
 //@ compile-flags: -Zmerge-functions=disabled
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const i32;
 
-impl Copy for i8 {}
-impl Copy for u8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for f32 {}
-impl Copy for f64 {}
-impl Copy for ptr {}
 extern "C" {
     fn extern_func();
     static extern_static: u8;
diff --git a/tests/assembly/asm/msp430-types.rs b/tests/assembly/asm/msp430-types.rs
index ae09b8b070d..442dc77999f 100644
--- a/tests/assembly/asm/msp430-types.rs
+++ b/tests/assembly/asm/msp430-types.rs
@@ -1,34 +1,18 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target msp430-none-elf
 //@ needs-llvm-components: msp430
 
-#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const i16;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for ptr {}
-
 macro_rules! check {
     ($func:ident $ty:ident $class:ident) => {
         #[no_mangle]
diff --git a/tests/assembly/asm/nvptx-types.rs b/tests/assembly/asm/nvptx-types.rs
index 0dd3162b4c0..7e8ebd03024 100644
--- a/tests/assembly/asm/nvptx-types.rs
+++ b/tests/assembly/asm/nvptx-types.rs
@@ -1,35 +1,17 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target nvptx64-nvidia-cuda
-//@ compile-flags: --crate-type cdylib
 //@ needs-llvm-components: nvptx
 
-#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
+#![crate_type = "rlib"]
 #![no_core]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *mut u8;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for f32 {}
-impl Copy for i64 {}
-impl Copy for f64 {}
-impl Copy for ptr {}
-
 // NVPTX does not support static variables
 #[no_mangle]
 fn extern_func() {}
diff --git a/tests/assembly/asm/powerpc-types.rs b/tests/assembly/asm/powerpc-types.rs
index aa35c4d8865..4291e4c02f3 100644
--- a/tests/assembly/asm/powerpc-types.rs
+++ b/tests/assembly/asm/powerpc-types.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: powerpc powerpc_altivec powerpc_vsx powerpc64 powerpc64_vsx
 //@ assembly-output: emit-asm
 //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu
@@ -12,11 +13,14 @@
 //@[powerpc64_vsx] needs-llvm-components: powerpc
 //@ compile-flags: -Zmerge-functions=disabled
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)]
+#![feature(no_core, repr_simd, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
+extern crate minicore;
+use minicore::*;
+
 #[cfg_attr(altivec, cfg(not(target_feature = "altivec")))]
 #[cfg_attr(not(altivec), cfg(target_feature = "altivec"))]
 compile_error!("altivec cfg and target feature mismatch");
@@ -24,26 +28,6 @@ compile_error!("altivec cfg and target feature mismatch");
 #[cfg_attr(not(vsx), cfg(target_feature = "vsx"))]
 compile_error!("vsx cfg and target feature mismatch");
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-impl<T: Copy, const N: usize> Copy for [T; N] {}
-
 type ptr = *const i32;
 
 #[repr(simd)]
@@ -59,14 +43,6 @@ pub struct f32x4([f32; 4]);
 #[repr(simd)]
 pub struct f64x2([f64; 2]);
 
-impl Copy for i8 {}
-impl Copy for u8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for f32 {}
-impl Copy for f64 {}
-impl Copy for ptr {}
 impl Copy for i8x16 {}
 impl Copy for i16x8 {}
 impl Copy for i32x4 {}
diff --git a/tests/assembly/asm/riscv-types.rs b/tests/assembly/asm/riscv-types.rs
index 1f5d7d85b0a..724aa154da8 100644
--- a/tests/assembly/asm/riscv-types.rs
+++ b/tests/assembly/asm/riscv-types.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: riscv64 riscv32 riscv64-zfhmin riscv32-zfhmin riscv64-zfh riscv32-zfh
 //@ assembly-output: emit-asm
 
@@ -29,40 +30,16 @@
 //@ compile-flags: -C target-feature=+d
 //@ compile-flags: -Zmerge-functions=disabled
 
-#![feature(no_core, lang_items, rustc_attrs, f16)]
+#![feature(no_core, f16)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *mut u8;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for f16 {}
-impl Copy for i32 {}
-impl Copy for f32 {}
-impl Copy for i64 {}
-impl Copy for f64 {}
-impl Copy for ptr {}
-
 extern "C" {
     fn extern_func();
     static extern_static: u8;
diff --git a/tests/assembly/asm/s390x-types.rs b/tests/assembly/asm/s390x-types.rs
index 3da22d6c77b..e6fe38ecb0d 100644
--- a/tests/assembly/asm/s390x-types.rs
+++ b/tests/assembly/asm/s390x-types.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: s390x s390x_vector
 //@ assembly-output: emit-asm
 //@[s390x] compile-flags: --target s390x-unknown-linux-gnu
@@ -6,31 +7,14 @@
 //@[s390x_vector] needs-llvm-components: systemz
 //@ compile-flags: -Zmerge-functions=disabled
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, f128)]
+#![feature(no_core, repr_simd, f128)]
 #![cfg_attr(s390x_vector, feature(asm_experimental_reg))]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-impl<T: Copy, const N: usize> Copy for [T; N] {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const i32;
 
@@ -47,16 +31,6 @@ pub struct f32x4([f32; 4]);
 #[repr(simd)]
 pub struct f64x2([f64; 2]);
 
-impl Copy for i8 {}
-impl Copy for u8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for i128 {}
-impl Copy for f32 {}
-impl Copy for f64 {}
-impl Copy for f128 {}
-impl Copy for ptr {}
 impl Copy for i8x16 {}
 impl Copy for i16x8 {}
 impl Copy for i32x4 {}
diff --git a/tests/assembly/asm/sparc-types.rs b/tests/assembly/asm/sparc-types.rs
index 2270679e837..49cc377cd95 100644
--- a/tests/assembly/asm/sparc-types.rs
+++ b/tests/assembly/asm/sparc-types.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: sparc sparcv8plus sparc64
 //@ assembly-output: emit-asm
 //@[sparc] compile-flags: --target sparc-unknown-none-elf
@@ -8,40 +9,16 @@
 //@[sparc64] needs-llvm-components: sparc
 //@ compile-flags: -Zmerge-functions=disabled
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *const i32;
 
-impl Copy for i8 {}
-impl Copy for u8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for i64 {}
-impl Copy for f32 {}
-impl Copy for f64 {}
-impl Copy for ptr {}
-
 extern "C" {
     fn extern_func();
     static extern_static: u8;
diff --git a/tests/assembly/asm/wasm-types.rs b/tests/assembly/asm/wasm-types.rs
index fe5ce836bc6..78e555c5317 100644
--- a/tests/assembly/asm/wasm-types.rs
+++ b/tests/assembly/asm/wasm-types.rs
@@ -1,35 +1,17 @@
+//@ add-core-stubs
 //@ assembly-output: emit-asm
 //@ compile-flags: --target wasm32-unknown-unknown
-//@ compile-flags: --crate-type cdylib
 //@ needs-llvm-components: webassembly
 
-#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![feature(no_core, asm_experimental_arch)]
+#![crate_type = "rlib"]
 #![no_core]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *mut u8;
 
-impl Copy for i8 {}
-impl Copy for i16 {}
-impl Copy for i32 {}
-impl Copy for f32 {}
-impl Copy for i64 {}
-impl Copy for f64 {}
-impl Copy for ptr {}
-
 extern "C" {
     fn extern_func();
     static extern_static: u8;
diff --git a/tests/assembly/asm/x86-modifiers.rs b/tests/assembly/asm/x86-modifiers.rs
index 5a48af9205f..53e4b92f84a 100644
--- a/tests/assembly/asm/x86-modifiers.rs
+++ b/tests/assembly/asm/x86-modifiers.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: x86_64 i686
 //@ assembly-output: emit-asm
 //@ compile-flags: -O -C panic=abort
@@ -9,30 +10,13 @@
 //@ compile-flags: -C target-feature=+avx512bw
 //@ compile-flags: -Zmerge-functions=disabled
 
-#![feature(no_core, lang_items, rustc_attrs)]
+#![feature(no_core)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-impl Copy for i32 {}
+extern crate minicore;
+use minicore::*;
 
 macro_rules! check {
     ($func:ident $modifier:literal $reg:ident $mov:literal) => {
diff --git a/tests/assembly/asm/x86-types.rs b/tests/assembly/asm/x86-types.rs
index 567dc7a8245..6120ed0d532 100644
--- a/tests/assembly/asm/x86-types.rs
+++ b/tests/assembly/asm/x86-types.rs
@@ -1,3 +1,4 @@
+//@ add-core-stubs
 //@ revisions: x86_64 i686
 //@ assembly-output: emit-asm
 //@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu
@@ -8,31 +9,13 @@
 //@ compile-flags: -C target-feature=+avx512bw
 //@ compile-flags: -Zmerge-functions=disabled
 
-#![feature(no_core, lang_items, rustc_attrs, repr_simd, f16, f128)]
+#![feature(no_core, repr_simd, f16, f128)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(asm_sub_register, non_camel_case_types)]
 
-#[rustc_builtin_macro]
-macro_rules! asm {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! concat {
-    () => {};
-}
-#[rustc_builtin_macro]
-macro_rules! stringify {
-    () => {};
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-// Do we really need to use no_core for this?!?
-impl<T: Copy, const N: usize> Copy for [T; N] {}
+extern crate minicore;
+use minicore::*;
 
 type ptr = *mut u8;
 
@@ -90,7 +73,6 @@ macro_rules! impl_copy {
 }
 
 impl_copy!(
-    i8 i16 f16 i32 f32 i64 f64 f128 ptr
     i8x16 i16x8 i32x4 i64x2 f16x8 f32x4 f64x2
     i8x32 i16x16 i32x8 i64x4 f16x16 f32x8 f64x4
     i8x64 i16x32 i32x16 i64x8 f16x32 f32x16 f64x8
diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs
index 2fa0c550efb..a68552175c3 100644
--- a/tests/auxiliary/minicore.rs
+++ b/tests/auxiliary/minicore.rs
@@ -14,7 +14,7 @@
 //! <https://github.com/rust-lang/rust/blob/c0b5cc9003f6464c11ae1c0662c6a7e06f6f5cab/compiler/rustc_codegen_cranelift/example/mini_core.rs>.
 // ignore-tidy-linelength
 
-#![feature(no_core, lang_items, rustc_attrs, decl_macro, naked_functions)]
+#![feature(no_core, lang_items, rustc_attrs, decl_macro, naked_functions, f16, f128)]
 #![allow(unused, improper_ctypes_definitions, internal_features)]
 #![feature(asm_experimental_arch)]
 #![no_std]
@@ -40,7 +40,12 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
 pub trait Copy: Sized {}
 
 impl_marker_trait!(
-    Copy => [ bool, char, isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 ]
+    Copy => [
+        bool, char,
+        isize, i8, i16, i32, i64, i128,
+        usize, u8, u16, u32, u64, u128,
+        f16, f32, f64, f128,
+    ]
 );
 impl<'a, T: ?Sized> Copy for &'a T {}
 impl<T: ?Sized> Copy for *const T {}
@@ -88,3 +93,18 @@ pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?)
 pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) {
     /* compiler built-in */
 }
+
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! concat {
+    ($($e:expr),* $(,)?) => {
+        /* compiler built-in */
+    };
+}
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! stringify {
+    ($($t:tt)*) => {
+        /* compiler built-in */
+    };
+}
diff --git a/tests/mir-opt/early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff
new file mode 100644
index 00000000000..b7447ef0c46
--- /dev/null
+++ b/tests/mir-opt/early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff
@@ -0,0 +1,77 @@
+- // MIR for `opt5` before EarlyOtherwiseBranch
++ // MIR for `opt5` after EarlyOtherwiseBranch
+  
+  fn opt5(_1: u32, _2: u32) -> u32 {
+      debug x => _1;
+      debug y => _2;
+      let mut _0: u32;
+      let mut _3: (u32, u32);
+      let mut _4: u32;
+      let mut _5: u32;
++     let mut _6: bool;
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = copy _1;
+          StorageLive(_5);
+          _5 = copy _2;
+          _3 = (move _4, move _5);
+          StorageDead(_5);
+          StorageDead(_4);
+-         switchInt(copy (_3.0: u32)) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1];
++         StorageLive(_6);
++         _6 = Ne(copy (_3.0: u32), copy (_3.1: u32));
++         switchInt(move _6) -> [0: bb6, otherwise: bb1];
+      }
+  
+      bb1: {
++         StorageDead(_6);
+          _0 = const 0_u32;
+-         goto -> bb8;
++         goto -> bb5;
+      }
+  
+      bb2: {
+-         switchInt(copy (_3.1: u32)) -> [1: bb7, otherwise: bb1];
++         _0 = const 6_u32;
++         goto -> bb5;
+      }
+  
+      bb3: {
+-         switchInt(copy (_3.1: u32)) -> [2: bb6, otherwise: bb1];
++         _0 = const 5_u32;
++         goto -> bb5;
+      }
+  
+      bb4: {
+-         switchInt(copy (_3.1: u32)) -> [3: bb5, otherwise: bb1];
++         _0 = const 4_u32;
++         goto -> bb5;
+      }
+  
+      bb5: {
+-         _0 = const 6_u32;
+-         goto -> bb8;
++         StorageDead(_3);
++         return;
+      }
+  
+      bb6: {
+-         _0 = const 5_u32;
+-         goto -> bb8;
+-     }
+- 
+-     bb7: {
+-         _0 = const 4_u32;
+-         goto -> bb8;
+-     }
+- 
+-     bb8: {
+-         StorageDead(_3);
+-         return;
++         StorageDead(_6);
++         switchInt(copy (_3.0: u32)) -> [1: bb4, 2: bb3, 3: bb2, otherwise: bb1];
+      }
+  }
+  
diff --git a/tests/mir-opt/early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff
new file mode 100644
index 00000000000..af16271f8b1
--- /dev/null
+++ b/tests/mir-opt/early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff
@@ -0,0 +1,61 @@
+- // MIR for `opt5_failed` before EarlyOtherwiseBranch
++ // MIR for `opt5_failed` after EarlyOtherwiseBranch
+  
+  fn opt5_failed(_1: u32, _2: u32) -> u32 {
+      debug x => _1;
+      debug y => _2;
+      let mut _0: u32;
+      let mut _3: (u32, u32);
+      let mut _4: u32;
+      let mut _5: u32;
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = copy _1;
+          StorageLive(_5);
+          _5 = copy _2;
+          _3 = (move _4, move _5);
+          StorageDead(_5);
+          StorageDead(_4);
+          switchInt(copy (_3.0: u32)) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1];
+      }
+  
+      bb1: {
+          _0 = const 0_u32;
+          goto -> bb8;
+      }
+  
+      bb2: {
+          switchInt(copy (_3.1: u32)) -> [1: bb7, otherwise: bb1];
+      }
+  
+      bb3: {
+          switchInt(copy (_3.1: u32)) -> [2: bb6, otherwise: bb1];
+      }
+  
+      bb4: {
+          switchInt(copy (_3.1: u32)) -> [2: bb5, otherwise: bb1];
+      }
+  
+      bb5: {
+          _0 = const 6_u32;
+          goto -> bb8;
+      }
+  
+      bb6: {
+          _0 = const 5_u32;
+          goto -> bb8;
+      }
+  
+      bb7: {
+          _0 = const 4_u32;
+          goto -> bb8;
+      }
+  
+      bb8: {
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff
new file mode 100644
index 00000000000..23f14b843b3
--- /dev/null
+++ b/tests/mir-opt/early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff
@@ -0,0 +1,61 @@
+- // MIR for `opt5_failed_type` before EarlyOtherwiseBranch
++ // MIR for `opt5_failed_type` after EarlyOtherwiseBranch
+  
+  fn opt5_failed_type(_1: u32, _2: u64) -> u32 {
+      debug x => _1;
+      debug y => _2;
+      let mut _0: u32;
+      let mut _3: (u32, u64);
+      let mut _4: u32;
+      let mut _5: u64;
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = copy _1;
+          StorageLive(_5);
+          _5 = copy _2;
+          _3 = (move _4, move _5);
+          StorageDead(_5);
+          StorageDead(_4);
+          switchInt(copy (_3.0: u32)) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1];
+      }
+  
+      bb1: {
+          _0 = const 0_u32;
+          goto -> bb8;
+      }
+  
+      bb2: {
+          switchInt(copy (_3.1: u64)) -> [1: bb7, otherwise: bb1];
+      }
+  
+      bb3: {
+          switchInt(copy (_3.1: u64)) -> [2: bb6, otherwise: bb1];
+      }
+  
+      bb4: {
+          switchInt(copy (_3.1: u64)) -> [3: bb5, otherwise: bb1];
+      }
+  
+      bb5: {
+          _0 = const 6_u32;
+          goto -> bb8;
+      }
+  
+      bb6: {
+          _0 = const 5_u32;
+          goto -> bb8;
+      }
+  
+      bb7: {
+          _0 = const 4_u32;
+          goto -> bb8;
+      }
+  
+      bb8: {
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/early_otherwise_branch.rs b/tests/mir-opt/early_otherwise_branch.rs
index 47bd4be295b..382c38ceb3a 100644
--- a/tests/mir-opt/early_otherwise_branch.rs
+++ b/tests/mir-opt/early_otherwise_branch.rs
@@ -78,9 +78,57 @@ fn opt4(x: Option2<u32>, y: Option2<u32>) -> u32 {
     }
 }
 
+// EMIT_MIR early_otherwise_branch.opt5.EarlyOtherwiseBranch.diff
+fn opt5(x: u32, y: u32) -> u32 {
+    // CHECK-LABEL: fn opt5(
+    // CHECK: let mut [[CMP_LOCAL:_.*]]: bool;
+    // CHECK: bb0: {
+    // CHECK: [[CMP_LOCAL]] = Ne(
+    // CHECK: switchInt(move [[CMP_LOCAL]]) -> [
+    // CHECK-NEXT: }
+    match (x, y) {
+        (1, 1) => 4,
+        (2, 2) => 5,
+        (3, 3) => 6,
+        _ => 0,
+    }
+}
+
+// EMIT_MIR early_otherwise_branch.opt5_failed.EarlyOtherwiseBranch.diff
+fn opt5_failed(x: u32, y: u32) -> u32 {
+    // CHECK-LABEL: fn opt5_failed(
+    // CHECK: bb0: {
+    // CHECK-NOT: Ne(
+    // CHECK: switchInt(
+    // CHECK-NEXT: }
+    match (x, y) {
+        (1, 1) => 4,
+        (2, 2) => 5,
+        (3, 2) => 6,
+        _ => 0,
+    }
+}
+
+// EMIT_MIR early_otherwise_branch.opt5_failed_type.EarlyOtherwiseBranch.diff
+fn opt5_failed_type(x: u32, y: u64) -> u32 {
+    // CHECK-LABEL: fn opt5_failed_type(
+    // CHECK: bb0: {
+    // CHECK-NOT: Ne(
+    // CHECK: switchInt(
+    // CHECK-NEXT: }
+    match (x, y) {
+        (1, 1) => 4,
+        (2, 2) => 5,
+        (3, 3) => 6,
+        _ => 0,
+    }
+}
+
 fn main() {
     opt1(None, Some(0));
     opt2(None, Some(0));
     opt3(Option2::None, Option2::Some(false));
     opt4(Option2::None, Option2::Some(0));
+    opt5(0, 0);
+    opt5_failed(0, 0);
 }
diff --git a/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff
new file mode 100644
index 00000000000..b6450e43b09
--- /dev/null
+++ b/tests/mir-opt/early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff
@@ -0,0 +1,126 @@
+- // MIR for `poll` before EarlyOtherwiseBranch
++ // MIR for `poll` after EarlyOtherwiseBranch
+  
+  fn poll(_1: Poll<Result<Option<Vec<u8>>, u8>>) -> () {
+      debug val => _1;
+      let mut _0: ();
+      let mut _2: isize;
+      let mut _3: isize;
+      let mut _4: isize;
+      let _5: std::vec::Vec<u8>;
+      let _6: u8;
+      let mut _7: bool;
+      let mut _8: bool;
+      let mut _9: isize;
+      scope 1 {
+          debug _trailers => _5;
+      }
+      scope 2 {
+          debug _err => _6;
+      }
+  
+      bb0: {
+          _7 = const false;
+          _8 = const false;
+          _7 = const true;
+          _8 = const true;
+          _4 = discriminant(_1);
+          switchInt(copy _4) -> [0: bb2, 1: bb4, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _3 = discriminant(((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>));
+          switchInt(copy _3) -> [0: bb3, 1: bb6, otherwise: bb1];
+      }
+  
+      bb3: {
+          _2 = discriminant(((((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>) as Ok).0: std::option::Option<std::vec::Vec<u8>>));
+          switchInt(copy _2) -> [0: bb5, 1: bb7, otherwise: bb1];
+      }
+  
+      bb4: {
+          _0 = const ();
+          goto -> bb17;
+      }
+  
+      bb5: {
+          _0 = const ();
+          goto -> bb17;
+      }
+  
+      bb6: {
+          StorageLive(_6);
+          _6 = copy ((((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>) as Err).0: u8);
+          _0 = const ();
+          StorageDead(_6);
+          goto -> bb17;
+      }
+  
+      bb7: {
+          StorageLive(_5);
+          _5 = move ((((((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>) as Ok).0: std::option::Option<std::vec::Vec<u8>>) as Some).0: std::vec::Vec<u8>);
+          _0 = const ();
+          drop(_5) -> [return: bb8, unwind: bb20];
+      }
+  
+      bb8: {
+          StorageDead(_5);
+          goto -> bb17;
+      }
+  
+      bb9 (cleanup): {
+          resume;
+      }
+  
+      bb10: {
+          return;
+      }
+  
+      bb11: {
+          switchInt(copy _7) -> [0: bb12, otherwise: bb16];
+      }
+  
+      bb12: {
+          _7 = const false;
+          goto -> bb10;
+      }
+  
+      bb13: {
+          switchInt(copy _8) -> [0: bb14, otherwise: bb15];
+      }
+  
+      bb14: {
+          _8 = const false;
+          goto -> bb12;
+      }
+  
+      bb15: {
+          goto -> bb14;
+      }
+  
+      bb16: {
+          _9 = discriminant(((_1 as Ready).0: std::result::Result<std::option::Option<std::vec::Vec<u8>>, u8>));
+          switchInt(move _9) -> [0: bb13, otherwise: bb12];
+      }
+  
+      bb17: {
+          switchInt(copy _4) -> [0: bb11, otherwise: bb10];
+      }
+  
+      bb18 (cleanup): {
+          switchInt(copy _3) -> [0: bb19, otherwise: bb9];
+      }
+  
+      bb19 (cleanup): {
+          goto -> bb9;
+      }
+  
+      bb20 (cleanup): {
+          switchInt(copy _4) -> [0: bb18, otherwise: bb9];
+      }
+  }
+  
diff --git a/tests/mir-opt/early_otherwise_branch_unwind.rs b/tests/mir-opt/early_otherwise_branch_unwind.rs
new file mode 100644
index 00000000000..cbccf11729a
--- /dev/null
+++ b/tests/mir-opt/early_otherwise_branch_unwind.rs
@@ -0,0 +1,38 @@
+//@ test-mir-pass: EarlyOtherwiseBranch
+//@ compile-flags: -Zmir-enable-passes=+GVN,+SimplifyLocals-after-value-numbering
+//@ needs-unwind
+
+use std::task::Poll;
+
+// We find a matching pattern in the unwind path,
+// and we need to create a cleanup BB for this case to meet the unwind invariants rule.
+// NB: This transform is not happening currently.
+
+// EMIT_MIR early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff
+fn unwind<T>(val: Option<Option<Option<T>>>) {
+    // CHECK-LABEL: fn unwind(
+    // CHECK: drop({{.*}}) -> [return: bb{{.*}}, unwind: [[PARENT_UNWIND_BB:bb.*]]];
+    // CHECK: [[PARENT_UNWIND_BB]] (cleanup): {
+    // CHECK-NEXT: switchInt
+    match val {
+        Some(Some(Some(_v))) => {}
+        Some(Some(None)) => {}
+        Some(None) => {}
+        None => {}
+    }
+}
+
+// From https://github.com/rust-lang/rust/issues/130769#issuecomment-2370443086.
+// EMIT_MIR early_otherwise_branch_unwind.poll.EarlyOtherwiseBranch.diff
+pub fn poll(val: Poll<Result<Option<Vec<u8>>, u8>>) {
+    // CHECK-LABEL: fn poll(
+    // CHECK: drop({{.*}}) -> [return: bb{{.*}}, unwind: [[PARENT_UNWIND_BB:bb.*]]];
+    // CHECK: [[PARENT_UNWIND_BB]] (cleanup): {
+    // CHECK-NEXT: switchInt
+    match val {
+        Poll::Ready(Ok(Some(_trailers))) => {}
+        Poll::Ready(Err(_err)) => {}
+        Poll::Ready(Ok(None)) => {}
+        Poll::Pending => {}
+    }
+}
diff --git a/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff b/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff
new file mode 100644
index 00000000000..2b2c007e082
--- /dev/null
+++ b/tests/mir-opt/early_otherwise_branch_unwind.unwind.EarlyOtherwiseBranch.diff
@@ -0,0 +1,119 @@
+- // MIR for `unwind` before EarlyOtherwiseBranch
++ // MIR for `unwind` after EarlyOtherwiseBranch
+  
+  fn unwind(_1: Option<Option<Option<T>>>) -> () {
+      debug val => _1;
+      let mut _0: ();
+      let mut _2: isize;
+      let mut _3: isize;
+      let mut _4: isize;
+      let _5: T;
+      let mut _6: bool;
+      let mut _7: bool;
+      let mut _8: isize;
+      scope 1 {
+          debug _v => _5;
+      }
+  
+      bb0: {
+          _6 = const false;
+          _7 = const false;
+          _6 = const true;
+          _7 = const true;
+          _4 = discriminant(_1);
+          switchInt(copy _4) -> [0: bb4, 1: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _3 = discriminant(((_1 as Some).0: std::option::Option<std::option::Option<T>>));
+          switchInt(copy _3) -> [0: bb5, 1: bb3, otherwise: bb1];
+      }
+  
+      bb3: {
+          _2 = discriminant(((((_1 as Some).0: std::option::Option<std::option::Option<T>>) as Some).0: std::option::Option<T>));
+          switchInt(copy _2) -> [0: bb6, 1: bb7, otherwise: bb1];
+      }
+  
+      bb4: {
+          _0 = const ();
+          goto -> bb17;
+      }
+  
+      bb5: {
+          _0 = const ();
+          goto -> bb17;
+      }
+  
+      bb6: {
+          _0 = const ();
+          goto -> bb17;
+      }
+  
+      bb7: {
+          StorageLive(_5);
+          _5 = move ((((((_1 as Some).0: std::option::Option<std::option::Option<T>>) as Some).0: std::option::Option<T>) as Some).0: T);
+          _0 = const ();
+          drop(_5) -> [return: bb8, unwind: bb20];
+      }
+  
+      bb8: {
+          StorageDead(_5);
+          goto -> bb17;
+      }
+  
+      bb9 (cleanup): {
+          resume;
+      }
+  
+      bb10: {
+          return;
+      }
+  
+      bb11: {
+          switchInt(copy _6) -> [0: bb12, otherwise: bb16];
+      }
+  
+      bb12: {
+          _6 = const false;
+          goto -> bb10;
+      }
+  
+      bb13: {
+          switchInt(copy _7) -> [0: bb14, otherwise: bb15];
+      }
+  
+      bb14: {
+          _7 = const false;
+          goto -> bb12;
+      }
+  
+      bb15: {
+          goto -> bb14;
+      }
+  
+      bb16: {
+          _8 = discriminant(((_1 as Some).0: std::option::Option<std::option::Option<T>>));
+          switchInt(move _8) -> [1: bb13, otherwise: bb12];
+      }
+  
+      bb17: {
+          switchInt(copy _4) -> [1: bb11, otherwise: bb10];
+      }
+  
+      bb18 (cleanup): {
+          switchInt(copy _3) -> [1: bb19, otherwise: bb9];
+      }
+  
+      bb19 (cleanup): {
+          goto -> bb9;
+      }
+  
+      bb20 (cleanup): {
+          switchInt(copy _4) -> [1: bb18, otherwise: bb9];
+      }
+  }
+  
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed
index 086671e69cb..e2b2c987610 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed
@@ -23,22 +23,22 @@ fn main() {
     assert_type_eq(x, &mut 0u8);
 
     let &Foo(mut x) = &Foo(0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &mut Foo(mut x) = &mut Foo(0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &Foo(ref x) = &Foo(0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, &0u8);
 
     let &mut Foo(ref x) = &mut Foo(0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, &0u8);
 
@@ -55,22 +55,22 @@ fn main() {
     assert_type_eq(x, &0u8);
 
     let &Foo(&x) = &Foo(&0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &Foo(&mut x) = &Foo(&mut 0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &mut Foo(&x) = &mut Foo(&0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let &mut Foo(&mut x) = &mut Foo(&mut 0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
@@ -79,25 +79,25 @@ fn main() {
     }
 
     if let &&&&&Some(&x) = &&&&&Some(&0u8) {
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) {
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) {
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) {
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, &mut 0u8);
     }
@@ -109,20 +109,20 @@ fn main() {
     }
 
     let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 };
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(a, &0u32);
     assert_type_eq(b, 0u32);
 
     let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 };
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(a, 0u32);
     assert_type_eq(b, &&0u32);
     assert_type_eq(c, &&0u32);
 
     if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } =
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) })
     {
@@ -135,7 +135,7 @@ fn main() {
         // The two patterns are the same syntactically, but because they're defined in different
         // editions they don't mean the same thing.
         &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
-            //~^ ERROR: patterns are not allowed to reset the default binding mode
+            //~^ ERROR: this pattern relies on behavior which may change in edition 2024
             assert_type_eq(x, 0u32);
             assert_type_eq(y, 0u32);
         }
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs
index acceafdb7ec..098540adfa2 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs
@@ -23,22 +23,22 @@ fn main() {
     assert_type_eq(x, &mut 0u8);
 
     let Foo(mut x) = &Foo(0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(mut x) = &mut Foo(0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(ref x) = &Foo(0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, &0u8);
 
     let Foo(ref x) = &mut Foo(0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, &0u8);
 
@@ -55,22 +55,22 @@ fn main() {
     assert_type_eq(x, &0u8);
 
     let Foo(&x) = &Foo(&0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(&mut x) = &Foo(&mut 0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(&x) = &mut Foo(&0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
     let Foo(&mut x) = &mut Foo(&mut 0);
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(x, 0u8);
 
@@ -79,25 +79,25 @@ fn main() {
     }
 
     if let Some(&x) = &&&&&Some(&0u8) {
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let Some(&mut x) = &&&&&Some(&mut 0u8) {
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let Some(&x) = &&&&&mut Some(&0u8) {
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, 0u8);
     }
 
     if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         assert_type_eq(x, &mut 0u8);
     }
@@ -109,20 +109,20 @@ fn main() {
     }
 
     let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 };
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(a, &0u32);
     assert_type_eq(b, 0u32);
 
     let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 };
-    //~^ ERROR: patterns are not allowed to reset the default binding mode
+    //~^ ERROR: this pattern relies on behavior which may change in edition 2024
     //~| WARN: this changes meaning in Rust 2024
     assert_type_eq(a, 0u32);
     assert_type_eq(b, &&0u32);
     assert_type_eq(c, &&0u32);
 
     if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } =
-        //~^ ERROR: patterns are not allowed to reset the default binding mode
+        //~^ ERROR: this pattern relies on behavior which may change in edition 2024
         //~| WARN: this changes meaning in Rust 2024
         &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) })
     {
@@ -135,7 +135,7 @@ fn main() {
         // The two patterns are the same syntactically, but because they're defined in different
         // editions they don't mean the same thing.
         (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
-            //~^ ERROR: patterns are not allowed to reset the default binding mode
+            //~^ ERROR: this pattern relies on behavior which may change in edition 2024
             assert_type_eq(x, 0u32);
             assert_type_eq(y, 0u32);
         }
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr
index 91aa987c737..83346b9dd4a 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr
@@ -1,10 +1,8 @@
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:25:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:25:13
    |
 LL |     let Foo(mut x) = &Foo(0);
-   |         -^^^^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&`
+   |             ^^^ requires binding by-value, but the implicit default is by-reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
@@ -13,176 +11,211 @@ note: the lint level is defined here
    |
 LL | #![deny(rust_2024_incompatible_pat)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: make the implied reference pattern explicit
+   |
+LL |     let &Foo(mut x) = &Foo(0);
+   |         +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:30:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:30:13
    |
 LL |     let Foo(mut x) = &mut Foo(0);
-   |         -^^^^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&mut`
+   |             ^^^ requires binding by-value, but the implicit default is by-reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL |     let &mut Foo(mut x) = &mut Foo(0);
+   |         ++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:35:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:35:13
    |
 LL |     let Foo(ref x) = &Foo(0);
-   |         -^^^^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&`
+   |             ^^^ cannot override to bind by-reference when that is the implicit default
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL |     let &Foo(ref x) = &Foo(0);
+   |         +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:40:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:40:13
    |
 LL |     let Foo(ref x) = &mut Foo(0);
-   |         -^^^^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&mut`
+   |             ^^^ cannot override to bind by-reference when that is the implicit default
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL |     let &mut Foo(ref x) = &mut Foo(0);
+   |         ++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:57:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:57:13
    |
 LL |     let Foo(&x) = &Foo(&0);
-   |         -^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&`
+   |             ^ cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL |     let &Foo(&x) = &Foo(&0);
+   |         +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:62:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:62:13
    |
 LL |     let Foo(&mut x) = &Foo(&mut 0);
-   |         -^^^^^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&`
+   |             ^^^^ cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL |     let &Foo(&mut x) = &Foo(&mut 0);
+   |         +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:67:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:67:13
    |
 LL |     let Foo(&x) = &mut Foo(&0);
-   |         -^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&mut`
+   |             ^ cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL |     let &mut Foo(&x) = &mut Foo(&0);
+   |         ++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:72:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:72:13
    |
 LL |     let Foo(&mut x) = &mut Foo(&mut 0);
-   |         -^^^^^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&mut`
+   |             ^^^^ cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL |     let &mut Foo(&mut x) = &mut Foo(&mut 0);
+   |         ++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:81:12
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:81:17
    |
 LL |     if let Some(&x) = &&&&&Some(&0u8) {
-   |            -^^^^^^^
-   |            |
-   |            help: desugar the match ergonomics: `&&&&&`
+   |                 ^ cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference patterns explicit
+   |
+LL |     if let &&&&&Some(&x) = &&&&&Some(&0u8) {
+   |            +++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:87:12
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:87:17
    |
 LL |     if let Some(&mut x) = &&&&&Some(&mut 0u8) {
-   |            -^^^^^^^^^^^
-   |            |
-   |            help: desugar the match ergonomics: `&&&&&`
+   |                 ^^^^ cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference patterns explicit
+   |
+LL |     if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) {
+   |            +++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:93:12
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:93:17
    |
 LL |     if let Some(&x) = &&&&&mut Some(&0u8) {
-   |            -^^^^^^^
-   |            |
-   |            help: desugar the match ergonomics: `&&&&&mut`
+   |                 ^ cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference patterns explicit
+   |
+LL |     if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) {
+   |            ++++++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:99:12
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:99:17
    |
 LL |     if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^
+   |                 ^^^^ cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
-help: desugar the match ergonomics
+help: make the implied reference patterns and variable binding mode explicit
    |
 LL |     if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) {
    |            ++++                ++++      +++++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:111:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:111:21
    |
 LL |     let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 };
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |                     ^^^ requires binding by-value, but the implicit default is by-reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
-help: desugar the match ergonomics
+help: make the implied reference pattern and variable binding modes explicit
    |
 LL |     let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 };
    |         +         +++           +++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:117:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:117:21
    |
 LL |     let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 };
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                     ^      ^^^ cannot override to bind by-reference when that is the implicit default
+   |                     |
+   |                     cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
-help: desugar the match ergonomics
+help: make the implied reference pattern and variable binding mode explicit
    |
 LL |     let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 };
    |         +                +++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:124:12
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:124:24
    |
 LL |     if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } =
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                        ^                 ^ cannot implicitly match against multiple layers of reference
+   |                        |
+   |                        cannot implicitly match against multiple layers of reference
    |
    = warning: this changes meaning in Rust 2024
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
-help: desugar the match ergonomics
+help: make the implied reference patterns and variable binding mode explicit
    |
 LL |     if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } =
    |            +                         +             +     +++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/migration_lint.rs:137:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/migration_lint.rs:137:15
    |
 LL |         (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
-   |         -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&`
+   |               ^^^     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ default binding mode is reset within expansion
+   |               |
+   |               requires binding by-value, but the implicit default is by-reference
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+   = note: this error originates in the macro `migration_lint_macros::mixed_edition_pat` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: make the implied reference pattern explicit
+   |
+LL |         &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
+   |         +
 
 error: aborting due to 16 previous errors
 
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs
index 50b716a1111..5ba554fc6e5 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs
@@ -21,17 +21,17 @@ macro_rules! test_pat_on_type {
 }
 
 test_pat_on_type![(&x,): &(T,)]; //~ ERROR mismatched types
-test_pat_on_type![(&x,): &(&T,)]; //~ ERROR patterns are not allowed to reset the default binding mode
+test_pat_on_type![(&x,): &(&T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
 test_pat_on_type![(&x,): &(&mut T,)]; //~ ERROR mismatched types
 test_pat_on_type![(&mut x,): &(&T,)]; //~ ERROR mismatched types
-test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR patterns are not allowed to reset the default binding mode
+test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
 test_pat_on_type![(&x,): &&mut &(T,)]; //~ ERROR mismatched types
 test_pat_on_type![Foo { f: (&x,) }: Foo]; //~ ERROR mismatched types
 test_pat_on_type![Foo { f: (&x,) }: &mut Foo]; //~ ERROR mismatched types
-test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR patterns are not allowed to reset the default binding mode
-test_pat_on_type![(mut x,): &(T,)]; //~ ERROR patterns are not allowed to reset the default binding mode
-test_pat_on_type![(ref x,): &(T,)]; //~ ERROR patterns are not allowed to reset the default binding mode
-test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR patterns are not allowed to reset the default binding mode
+test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR this pattern relies on behavior which may change in edition 2024
+test_pat_on_type![(mut x,): &(T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
+test_pat_on_type![(ref x,): &(T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
+test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024
 
 fn get<X>() -> X {
     unimplemented!()
@@ -40,6 +40,6 @@ fn get<X>() -> X {
 // Make sure this works even when the underlying type is inferred. This test passes on rust stable.
 fn infer<X: Copy>() -> X {
     match &get() {
-        (&x,) => x, //~ ERROR patterns are not allowed to reset the default binding mode
+        (&x,) => x, //~ ERROR this pattern relies on behavior which may change in edition 2024
     }
 }
diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr
index 92058095f84..affdca1d449 100644
--- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr
+++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr
@@ -99,61 +99,89 @@ LL - test_pat_on_type![Foo { f: (&x,) }: &mut Foo];
 LL + test_pat_on_type![Foo { f: (x,) }: &mut Foo];
    |
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/min_match_ergonomics_fail.rs:24:19
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/min_match_ergonomics_fail.rs:24:20
    |
 LL | test_pat_on_type![(&x,): &(&T,)];
-   |                   -^^^^
-   |                   |
-   |                   help: desugar the match ergonomics: `&`
+   |                    ^ cannot implicitly match against multiple layers of reference
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL | test_pat_on_type![&(&x,): &(&T,)];
+   |                   +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/min_match_ergonomics_fail.rs:27:19
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/min_match_ergonomics_fail.rs:27:20
    |
 LL | test_pat_on_type![(&mut x,): &(&mut T,)];
-   |                   -^^^^^^^^
-   |                   |
-   |                   help: desugar the match ergonomics: `&`
+   |                    ^^^^ cannot implicitly match against multiple layers of reference
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL | test_pat_on_type![&(&mut x,): &(&mut T,)];
+   |                   +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/min_match_ergonomics_fail.rs:31:19
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/min_match_ergonomics_fail.rs:31:28
    |
 LL | test_pat_on_type![Foo { f: &(x,) }: &Foo];
-   |                   -^^^^^^^^^^^^^^^
-   |                   |
-   |                   help: desugar the match ergonomics: `&`
+   |                            ^ cannot implicitly match against multiple layers of reference
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL | test_pat_on_type![&Foo { f: &(x,) }: &Foo];
+   |                   +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/min_match_ergonomics_fail.rs:32:19
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/min_match_ergonomics_fail.rs:32:20
    |
 LL | test_pat_on_type![(mut x,): &(T,)];
-   |                   -^^^^^^^
-   |                   |
-   |                   help: desugar the match ergonomics: `&`
+   |                    ^^^ requires binding by-value, but the implicit default is by-reference
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL | test_pat_on_type![&(mut x,): &(T,)];
+   |                   +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/min_match_ergonomics_fail.rs:33:19
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/min_match_ergonomics_fail.rs:33:20
    |
 LL | test_pat_on_type![(ref x,): &(T,)];
-   |                   -^^^^^^^
-   |                   |
-   |                   help: desugar the match ergonomics: `&`
+   |                    ^^^ cannot override to bind by-reference when that is the implicit default
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL | test_pat_on_type![&(ref x,): &(T,)];
+   |                   +
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/min_match_ergonomics_fail.rs:34:19
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/min_match_ergonomics_fail.rs:34:20
    |
 LL | test_pat_on_type![(ref mut x,): &mut (T,)];
-   |                   -^^^^^^^^^^^
-   |                   |
-   |                   help: desugar the match ergonomics: `&mut`
+   |                    ^^^^^^^ cannot override to bind by-reference when that is the implicit default
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL | test_pat_on_type![&mut (ref mut x,): &mut (T,)];
+   |                   ++++
 
-error: patterns are not allowed to reset the default binding mode in edition 2024
-  --> $DIR/min_match_ergonomics_fail.rs:43:9
+error: this pattern relies on behavior which may change in edition 2024
+  --> $DIR/min_match_ergonomics_fail.rs:43:10
    |
 LL |         (&x,) => x,
-   |         -^^^^
-   |         |
-   |         help: desugar the match ergonomics: `&`
+   |          ^ cannot implicitly match against multiple layers of reference
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
+help: make the implied reference pattern explicit
+   |
+LL |         &(&x,) => x,
+   |         +
 
 error: aborting due to 13 previous errors
 
diff --git a/tests/ui/traits/const-traits/const-cond-for-rpitit.rs b/tests/ui/traits/const-traits/const-cond-for-rpitit.rs
new file mode 100644
index 00000000000..50bf93f9a03
--- /dev/null
+++ b/tests/ui/traits/const-traits/const-cond-for-rpitit.rs
@@ -0,0 +1,22 @@
+//@ compile-flags: -Znext-solver
+//@ check-pass
+
+#![feature(const_trait_impl)]
+#![allow(refining_impl_trait)]
+
+#[const_trait]
+pub trait Foo {
+    fn method(self) -> impl ~const Bar;
+}
+
+#[const_trait]
+pub trait Bar {}
+
+struct A<T>(T);
+impl<T> const Foo for A<T> where A<T>: ~const Bar {
+    fn method(self) -> impl ~const Bar {
+        self
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/traits/const-traits/const-impl-trait.rs b/tests/ui/traits/const-traits/const-impl-trait.rs
index 61b8c9a5bff..d7fe43ef37c 100644
--- a/tests/ui/traits/const-traits/const-impl-trait.rs
+++ b/tests/ui/traits/const-traits/const-impl-trait.rs
@@ -1,15 +1,10 @@
 //@ compile-flags: -Znext-solver
 //@ known-bug: #110395
-//@ failure-status: 101
-//@ dont-check-compiler-stderr
-// Broken until we have `&T: const Deref` impl in stdlib
+
+// Broken until we have `const PartialEq` impl in stdlib
 
 #![allow(incomplete_features)]
-#![feature(
-    const_trait_impl,
-    effects,
-    const_cmp,
-)]
+#![feature(const_trait_impl, const_cmp, const_destruct)]
 
 use std::marker::Destruct;
 
@@ -17,9 +12,9 @@ const fn cmp(a: &impl ~const PartialEq) -> bool {
     a == a
 }
 
-const fn wrap(x: impl ~const PartialEq + ~const Destruct)
-    -> impl ~const PartialEq + ~const Destruct
-{
+const fn wrap(
+    x: impl ~const PartialEq + ~const Destruct,
+) -> impl ~const PartialEq + ~const Destruct {
     x
 }
 
@@ -48,11 +43,15 @@ trait T {}
 struct S;
 impl const T for S {}
 
-const fn rpit() -> impl ~const T { S }
+const fn rpit() -> impl ~const T {
+    S
+}
 
 const fn apit(_: impl ~const T + ~const Destruct) {}
 
-const fn rpit_assoc_bound() -> impl IntoIterator<Item: ~const T> { Some(S) }
+const fn rpit_assoc_bound() -> impl IntoIterator<Item: ~const T> {
+    Some(S)
+}
 
 const fn apit_assoc_bound(_: impl IntoIterator<Item: ~const T> + ~const Destruct) {}
 
diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr
new file mode 100644
index 00000000000..4e320059448
--- /dev/null
+++ b/tests/ui/traits/const-traits/const-impl-trait.stderr
@@ -0,0 +1,187 @@
+error[E0635]: unknown feature `const_cmp`
+  --> $DIR/const-impl-trait.rs:7:30
+   |
+LL | #![feature(const_trait_impl, const_cmp, const_destruct)]
+   |                              ^^^^^^^^^
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:11:23
+   |
+LL | const fn cmp(a: &impl ~const PartialEq) -> bool {
+   |                       ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:11:23
+   |
+LL | const fn cmp(a: &impl ~const PartialEq) -> bool {
+   |                       ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:16:13
+   |
+LL |     x: impl ~const PartialEq + ~const Destruct,
+   |             ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:17:11
+   |
+LL | ) -> impl ~const PartialEq + ~const Destruct {
+   |           ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:17:11
+   |
+LL | ) -> impl ~const PartialEq + ~const Destruct {
+   |           ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:17:11
+   |
+LL | ) -> impl ~const PartialEq + ~const Destruct {
+   |           ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:16:13
+   |
+LL |     x: impl ~const PartialEq + ~const Destruct,
+   |             ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:23:22
+   |
+LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
+   |                      ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:27:22
+   |
+LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy {
+   |                      ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:27:22
+   |
+LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy {
+   |                      ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:23:22
+   |
+LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
+   |                      ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:23:22
+   |
+LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
+   |                      ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:27:22
+   |
+LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy {
+   |                      ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:23:22
+   |
+LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
+   |                      ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `~const` can only be applied to `#[const_trait]` traits
+  --> $DIR/const-impl-trait.rs:23:22
+   |
+LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
+   |                      ^^^^^^ can't be applied to `PartialEq`
+   |
+note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0015]: cannot call non-const operator in constants
+  --> $DIR/const-impl-trait.rs:35:13
+   |
+LL |     assert!(wrap(123) == wrap(123));
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+
+error[E0015]: cannot call non-const operator in constants
+  --> $DIR/const-impl-trait.rs:36:13
+   |
+LL |     assert!(wrap(123) != wrap(456));
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+
+error[E0015]: cannot call non-const operator in constants
+  --> $DIR/const-impl-trait.rs:38:13
+   |
+LL |     assert!(x == x);
+   |             ^^^^^^
+   |
+   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+
+error[E0015]: cannot call non-const operator in constant functions
+  --> $DIR/const-impl-trait.rs:12:5
+   |
+LL |     a == a
+   |     ^^^^^^
+   |
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+
+error: aborting due to 20 previous errors
+
+Some errors have detailed explanations: E0015, E0635.
+For more information about an error, try `rustc --explain E0015`.