Auto merge of #123992 - compiler-errors:no-has-typeck-results, r=jackh726

`has_typeck_results` doesnt need to be a query

self-explanatory
diff --git a/Cargo.lock b/Cargo.lock
index cf5425a..552b446 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5598,6 +5598,7 @@
  "lazy_static",
  "miropt-test-tools",
  "regex",
+ "rustc-hash",
  "semver",
  "termcolor",
  "walkdir",
@@ -6332,9 +6333,9 @@
 
 [[package]]
 name = "windows-bindgen"
-version = "0.55.0"
+version = "0.56.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "073ff8a486ebad239d557809d2cd5fe5e04ee1de29e09c6cd83fb0bae19b8a4c"
+checksum = "a28e3ea6330cf17fdcdce8bf08d0549ce93769dca9bedc6c39c36c8c0e17db46"
 dependencies = [
  "proc-macro2",
  "rayon",
@@ -6355,9 +6356,9 @@
 
 [[package]]
 name = "windows-metadata"
-version = "0.55.0"
+version = "0.56.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b602635050172a1fc57a35040d4d225baefc6098fefd97094919921d95961a7d"
+checksum = "3993f7827fff10c454e3a24847075598c7c08108304b8b07943c2c73d78f3b34"
 
 [[package]]
 name = "windows-sys"
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 519eeed..bddb505 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -568,7 +568,7 @@
             // In a type expression `_` is an inference variable.
             PatKind::Wild => TyKind::Infer,
             // An IDENT pattern with no binding mode would be valid as path to a type. E.g. `u32`.
-            PatKind::Ident(BindingAnnotation::NONE, ident, None) => {
+            PatKind::Ident(BindingMode::NONE, ident, None) => {
                 TyKind::Path(None, Path::from_ident(*ident))
             }
             PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
@@ -675,7 +675,7 @@
     pub fn descr(&self) -> Option<String> {
         match &self.kind {
             PatKind::Wild => Some("_".to_string()),
-            PatKind::Ident(BindingAnnotation::NONE, ident, None) => Some(format!("{ident}")),
+            PatKind::Ident(BindingMode::NONE, ident, None) => Some(format!("{ident}")),
             PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())),
             _ => None,
         }
@@ -707,14 +707,25 @@
     No,
 }
 
-/// Explicit binding annotations given in the HIR for a binding. Note
-/// that this is not the final binding *mode* that we infer after type
-/// inference.
+impl ByRef {
+    pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
+        if let ByRef::Yes(old_mutbl) = &mut self {
+            *old_mutbl = cmp::min(*old_mutbl, mutbl);
+        }
+        self
+    }
+}
+
+/// The mode of a binding (`mut`, `ref mut`, etc).
+/// Used for both the explicit binding annotations given in the HIR for a binding
+/// and the final binding mode that we infer after type inference/match ergonomics.
+/// `.0` is the by-reference mode (`ref`, `ref mut`, or by value),
+/// `.1` is the mutability of the binding.
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 #[derive(Encodable, Decodable, HashStable_Generic)]
-pub struct BindingAnnotation(pub ByRef, pub Mutability);
+pub struct BindingMode(pub ByRef, pub Mutability);
 
-impl BindingAnnotation {
+impl BindingMode {
     pub const NONE: Self = Self(ByRef::No, Mutability::Not);
     pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not);
     pub const MUT: Self = Self(ByRef::No, Mutability::Mut);
@@ -732,13 +743,6 @@
             Self::MUT_REF_MUT => "mut ref mut ",
         }
     }
-
-    pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
-        if let ByRef::Yes(old_mutbl) = &mut self.0 {
-            *old_mutbl = cmp::min(*old_mutbl, mutbl);
-        }
-        self
-    }
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
@@ -769,7 +773,7 @@
     /// or a unit struct/variant pattern, or a const pattern (in the last two cases the third
     /// field must be `None`). Disambiguation cannot be done with parser alone, so it happens
     /// during name resolution.
-    Ident(BindingAnnotation, Ident, Option<P<Pat>>),
+    Ident(BindingMode, Ident, Option<P<Pat>>),
 
     /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
     Struct(Option<P<QSelf>>, Path, ThinVec<PatField>, PatFieldsRest),
@@ -2382,7 +2386,7 @@
 impl Param {
     /// Attempts to cast parameter to `ExplicitSelf`.
     pub fn to_self(&self) -> Option<ExplicitSelf> {
-        if let PatKind::Ident(BindingAnnotation(ByRef::No, mutbl), ident, _) = self.pat.kind {
+        if let PatKind::Ident(BindingMode(ByRef::No, mutbl), ident, _) = self.pat.kind {
             if ident.name == kw::SelfLower {
                 return match self.ty.kind {
                     TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
@@ -2434,7 +2438,7 @@
             attrs,
             pat: P(Pat {
                 id: DUMMY_NODE_ID,
-                kind: PatKind::Ident(BindingAnnotation(ByRef::No, mutbl), eself_ident, None),
+                kind: PatKind::Ident(BindingMode(ByRef::No, mutbl), eself_ident, None),
                 span,
                 tokens: None,
             }),
@@ -3363,7 +3367,7 @@
 pub type ForeignItem = Item<ForeignItemKind>;
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 5b41ac8..dcdd44c 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -1023,7 +1023,7 @@
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index f3249f3..880f92b 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -768,7 +768,7 @@
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index e26a65c..f4d5e71 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -178,7 +178,7 @@
         let pat_id = self.lower_node_id(pat_node_id);
         let pat = self.arena.alloc(hir::Pat {
             hir_id: pat_id,
-            kind: hir::PatKind::Binding(hir::BindingAnnotation::NONE, pat_id, Ident::empty(), None),
+            kind: hir::PatKind::Binding(hir::BindingMode::NONE, pat_id, Ident::empty(), None),
             span: ty.span,
             default_binding_modes: false,
         });
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 66841c0..2305cc0 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -643,7 +643,7 @@
                 let (pat, task_context_hid) = self.pat_ident_binding_mode(
                     span,
                     Ident::with_dummy_span(sym::_task_context),
-                    hir::BindingAnnotation::MUT,
+                    hir::BindingMode::MUT,
                 );
                 let param = hir::Param {
                     hir_id: self.next_id(),
@@ -805,11 +805,8 @@
         // debuggers and debugger extensions expect it to be called `__awaitee`. They use
         // this name to identify what is being awaited by a suspended async functions.
         let awaitee_ident = Ident::with_dummy_span(sym::__awaitee);
-        let (awaitee_pat, awaitee_pat_hid) = self.pat_ident_binding_mode(
-            gen_future_span,
-            awaitee_ident,
-            hir::BindingAnnotation::MUT,
-        );
+        let (awaitee_pat, awaitee_pat_hid) =
+            self.pat_ident_binding_mode(gen_future_span, awaitee_ident, hir::BindingMode::MUT);
 
         let task_context_ident = Ident::with_dummy_span(sym::_task_context);
 
@@ -1648,7 +1645,7 @@
         // `mut iter`
         let iter = Ident::with_dummy_span(sym::iter);
         let (iter_pat, iter_pat_nid) =
-            self.pat_ident_binding_mode(head_span, iter, hir::BindingAnnotation::MUT);
+            self.pat_ident_binding_mode(head_span, iter, hir::BindingMode::MUT);
 
         let match_expr = {
             let iter = self.expr_ident(head_span, iter, iter_pat_nid);
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index d2c3b8b..ca4604c 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -20,10 +20,126 @@
         let mut fmt = Cow::Borrowed(fmt);
         if self.tcx.sess.opts.unstable_opts.flatten_format_args {
             fmt = flatten_format_args(fmt);
-            fmt = inline_literals(fmt);
+            fmt = self.inline_literals(fmt);
         }
         expand_format_args(self, sp, &fmt, allow_const)
     }
+
+    /// Try to convert a literal into an interned string
+    fn try_inline_lit(&self, lit: token::Lit) -> Option<Symbol> {
+        match LitKind::from_token_lit(lit) {
+            Ok(LitKind::Str(s, _)) => Some(s),
+            Ok(LitKind::Int(n, ty)) => {
+                match ty {
+                    // unsuffixed integer literals are assumed to be i32's
+                    LitIntType::Unsuffixed => {
+                        (n <= i32::MAX as u128).then_some(Symbol::intern(&n.to_string()))
+                    }
+                    LitIntType::Signed(int_ty) => {
+                        let max_literal = self.int_ty_max(int_ty);
+                        (n <= max_literal).then_some(Symbol::intern(&n.to_string()))
+                    }
+                    LitIntType::Unsigned(uint_ty) => {
+                        let max_literal = self.uint_ty_max(uint_ty);
+                        (n <= max_literal).then_some(Symbol::intern(&n.to_string()))
+                    }
+                }
+            }
+            _ => None,
+        }
+    }
+
+    /// Get the maximum value of int_ty. It is platform-dependent due to the byte size of isize
+    fn int_ty_max(&self, int_ty: IntTy) -> u128 {
+        match int_ty {
+            IntTy::Isize => self.tcx.data_layout.pointer_size.signed_int_max() as u128,
+            IntTy::I8 => i8::MAX as u128,
+            IntTy::I16 => i16::MAX as u128,
+            IntTy::I32 => i32::MAX as u128,
+            IntTy::I64 => i64::MAX as u128,
+            IntTy::I128 => i128::MAX as u128,
+        }
+    }
+
+    /// Get the maximum value of uint_ty. It is platform-dependent due to the byte size of usize
+    fn uint_ty_max(&self, uint_ty: UintTy) -> u128 {
+        match uint_ty {
+            UintTy::Usize => self.tcx.data_layout.pointer_size.unsigned_int_max(),
+            UintTy::U8 => u8::MAX as u128,
+            UintTy::U16 => u16::MAX as u128,
+            UintTy::U32 => u32::MAX as u128,
+            UintTy::U64 => u64::MAX as u128,
+            UintTy::U128 => u128::MAX as u128,
+        }
+    }
+
+    /// Inline literals into the format string.
+    ///
+    /// Turns
+    ///
+    /// `format_args!("Hello, {}! {} {}", "World", 123, x)`
+    ///
+    /// into
+    ///
+    /// `format_args!("Hello, World! 123 {}", x)`.
+    fn inline_literals<'fmt>(&self, mut fmt: Cow<'fmt, FormatArgs>) -> Cow<'fmt, FormatArgs> {
+        let mut was_inlined = vec![false; fmt.arguments.all_args().len()];
+        let mut inlined_anything = false;
+
+        for i in 0..fmt.template.len() {
+            let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
+            let Ok(arg_index) = placeholder.argument.index else { continue };
+
+            let mut literal = None;
+
+            if let FormatTrait::Display = placeholder.format_trait
+                && placeholder.format_options == Default::default()
+                && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
+                && let ExprKind::Lit(lit) = arg.kind
+            {
+                literal = self.try_inline_lit(lit);
+            }
+
+            if let Some(literal) = literal {
+                // Now we need to mutate the outer FormatArgs.
+                // If this is the first time, this clones the outer FormatArgs.
+                let fmt = fmt.to_mut();
+                // Replace the placeholder with the literal.
+                fmt.template[i] = FormatArgsPiece::Literal(literal);
+                was_inlined[arg_index] = true;
+                inlined_anything = true;
+            }
+        }
+
+        // Remove the arguments that were inlined.
+        if inlined_anything {
+            let fmt = fmt.to_mut();
+
+            let mut remove = was_inlined;
+
+            // Don't remove anything that's still used.
+            for_all_argument_indexes(&mut fmt.template, |index| remove[*index] = false);
+
+            // Drop all the arguments that are marked for removal.
+            let mut remove_it = remove.iter();
+            fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&true));
+
+            // Calculate the mapping of old to new indexes for the remaining arguments.
+            let index_map: Vec<usize> = remove
+                .into_iter()
+                .scan(0, |i, remove| {
+                    let mapped = *i;
+                    *i += !remove as usize;
+                    Some(mapped)
+                })
+                .collect();
+
+            // Correct the indexes that refer to arguments that have shifted position.
+            for_all_argument_indexes(&mut fmt.template, |index| *index = index_map[*index]);
+        }
+
+        fmt
+    }
 }
 
 /// Flattens nested `format_args!()` into one.
@@ -103,82 +219,6 @@
     fmt
 }
 
-/// Inline literals into the format string.
-///
-/// Turns
-///
-/// `format_args!("Hello, {}! {} {}", "World", 123, x)`
-///
-/// into
-///
-/// `format_args!("Hello, World! 123 {}", x)`.
-fn inline_literals(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
-    let mut was_inlined = vec![false; fmt.arguments.all_args().len()];
-    let mut inlined_anything = false;
-
-    for i in 0..fmt.template.len() {
-        let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
-        let Ok(arg_index) = placeholder.argument.index else { continue };
-
-        let mut literal = None;
-
-        if let FormatTrait::Display = placeholder.format_trait
-            && placeholder.format_options == Default::default()
-            && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
-            && let ExprKind::Lit(lit) = arg.kind
-        {
-            if let token::LitKind::Str | token::LitKind::StrRaw(_) = lit.kind
-                && let Ok(LitKind::Str(s, _)) = LitKind::from_token_lit(lit)
-            {
-                literal = Some(s);
-            } else if let token::LitKind::Integer = lit.kind
-                && let Ok(LitKind::Int(n, _)) = LitKind::from_token_lit(lit)
-            {
-                literal = Some(Symbol::intern(&n.to_string()));
-            }
-        }
-
-        if let Some(literal) = literal {
-            // Now we need to mutate the outer FormatArgs.
-            // If this is the first time, this clones the outer FormatArgs.
-            let fmt = fmt.to_mut();
-            // Replace the placeholder with the literal.
-            fmt.template[i] = FormatArgsPiece::Literal(literal);
-            was_inlined[arg_index] = true;
-            inlined_anything = true;
-        }
-    }
-
-    // Remove the arguments that were inlined.
-    if inlined_anything {
-        let fmt = fmt.to_mut();
-
-        let mut remove = was_inlined;
-
-        // Don't remove anything that's still used.
-        for_all_argument_indexes(&mut fmt.template, |index| remove[*index] = false);
-
-        // Drop all the arguments that are marked for removal.
-        let mut remove_it = remove.iter();
-        fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&true));
-
-        // Calculate the mapping of old to new indexes for the remaining arguments.
-        let index_map: Vec<usize> = remove
-            .into_iter()
-            .scan(0, |i, remove| {
-                let mapped = *i;
-                *i += !remove as usize;
-                Some(mapped)
-            })
-            .collect();
-
-        // Correct the indexes that refer to arguments that have shifted position.
-        for_all_argument_indexes(&mut fmt.template, |index| *index = index_map[*index]);
-    }
-
-    fmt
-}
-
 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 enum ArgumentType {
     Format(FormatTrait),
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index abfea60..e4c633a 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1179,9 +1179,7 @@
             // Check if this is a binding pattern, if so, we can optimize and avoid adding a
             // `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
             let (ident, is_simple_parameter) = match parameter.pat.kind {
-                hir::PatKind::Binding(hir::BindingAnnotation(ByRef::No, _), _, ident, _) => {
-                    (ident, true)
-                }
+                hir::PatKind::Binding(hir::BindingMode(ByRef::No, _), _, ident, _) => (ident, true),
                 // For `ref mut` or wildcard arguments, we can't reuse the binding, but
                 // we can keep the same name for the parameter.
                 // This lets rustdoc render it correctly in documentation.
@@ -1244,7 +1242,7 @@
                 // because the user may have specified a `ref mut` binding in the next
                 // statement.
                 let (move_pat, move_id) =
-                    self.pat_ident_binding_mode(desugared_span, ident, hir::BindingAnnotation::MUT);
+                    self.pat_ident_binding_mode(desugared_span, ident, hir::BindingMode::MUT);
                 let move_expr = self.expr_ident(desugared_span, ident, new_parameter_id);
                 let move_stmt = self.stmt_let_pat(
                     None,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index d6e6246..c5b5acf 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1895,7 +1895,7 @@
             implicit_self: decl.inputs.get(0).map_or(hir::ImplicitSelfKind::None, |arg| {
                 let is_mutable_pat = matches!(
                     arg.pat.kind,
-                    PatKind::Ident(hir::BindingAnnotation(_, Mutability::Mut), ..)
+                    PatKind::Ident(hir::BindingMode(_, Mutability::Mut), ..)
                 );
 
                 match &arg.ty.kind {
@@ -2478,18 +2478,18 @@
     }
 
     fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, HirId) {
-        self.pat_ident_binding_mode(span, ident, hir::BindingAnnotation::NONE)
+        self.pat_ident_binding_mode(span, ident, hir::BindingMode::NONE)
     }
 
     fn pat_ident_mut(&mut self, span: Span, ident: Ident) -> (hir::Pat<'hir>, HirId) {
-        self.pat_ident_binding_mode_mut(span, ident, hir::BindingAnnotation::NONE)
+        self.pat_ident_binding_mode_mut(span, ident, hir::BindingMode::NONE)
     }
 
     fn pat_ident_binding_mode(
         &mut self,
         span: Span,
         ident: Ident,
-        bm: hir::BindingAnnotation,
+        bm: hir::BindingMode,
     ) -> (&'hir hir::Pat<'hir>, HirId) {
         let (pat, hir_id) = self.pat_ident_binding_mode_mut(span, ident, bm);
         (self.arena.alloc(pat), hir_id)
@@ -2499,7 +2499,7 @@
         &mut self,
         span: Span,
         ident: Ident,
-        bm: hir::BindingAnnotation,
+        bm: hir::BindingMode,
     ) -> (hir::Pat<'hir>, HirId) {
         let hir_id = self.next_id();
 
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index 8631d90..118a732 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -243,7 +243,7 @@
     fn lower_pat_ident(
         &mut self,
         p: &Pat,
-        annotation: BindingAnnotation,
+        annotation: BindingMode,
         ident: Ident,
         lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
     ) -> hir::PatKind<'hir> {
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 495e90e..4e3d560 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -276,8 +276,8 @@
     fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option<Ident>, bool)) {
         for Param { pat, .. } in &decl.inputs {
             match pat.kind {
-                PatKind::Ident(BindingAnnotation::NONE, _, None) | PatKind::Wild => {}
-                PatKind::Ident(BindingAnnotation::MUT, ident, None) => {
+                PatKind::Ident(BindingMode::NONE, _, None) | PatKind::Wild => {}
+                PatKind::Ident(BindingMode::MUT, ident, None) => {
                     report_err(pat.span, Some(ident), true)
                 }
                 _ => report_err(pat.span, None, false),
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 242335f..2c17682 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -3,11 +3,12 @@
 //! Note that HIR pretty printing is layered on top of this crate.
 
 mod expr;
+mod fixup;
 mod item;
 
 use crate::pp::Breaks::{Consistent, Inconsistent};
 use crate::pp::{self, Breaks};
-use crate::pprust::state::expr::FixupContext;
+use crate::pprust::state::fixup::FixupContext;
 use ast::TraitBoundModifiers;
 use rustc_ast::attr::AttrIdGenerator;
 use rustc_ast::ptr::P;
@@ -15,9 +16,8 @@
 use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
 use rustc_ast::util::classify;
 use rustc_ast::util::comments::{Comment, CommentStyle};
-use rustc_ast::util::parser;
 use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind};
-use rustc_ast::{attr, BindingAnnotation, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term};
+use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term};
 use rustc_ast::{GenericArg, GenericBound, SelfKind};
 use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
@@ -1252,22 +1252,14 @@
             ast::StmtKind::Item(item) => self.print_item(item),
             ast::StmtKind::Expr(expr) => {
                 self.space_if_not_bol();
-                self.print_expr_outer_attr_style(
-                    expr,
-                    false,
-                    FixupContext { stmt: true, ..FixupContext::default() },
-                );
+                self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
                 if classify::expr_requires_semi_to_be_stmt(expr) {
                     self.word(";");
                 }
             }
             ast::StmtKind::Semi(expr) => {
                 self.space_if_not_bol();
-                self.print_expr_outer_attr_style(
-                    expr,
-                    false,
-                    FixupContext { stmt: true, ..FixupContext::default() },
-                );
+                self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
                 self.word(";");
             }
             ast::StmtKind::Empty => {
@@ -1319,11 +1311,7 @@
                 ast::StmtKind::Expr(expr) if i == blk.stmts.len() - 1 => {
                     self.maybe_print_comment(st.span.lo());
                     self.space_if_not_bol();
-                    self.print_expr_outer_attr_style(
-                        expr,
-                        false,
-                        FixupContext { stmt: true, ..FixupContext::default() },
-                    );
+                    self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
                     self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
                 }
                 _ => self.print_stmt(st),
@@ -1367,8 +1355,7 @@
         self.word_space("=");
         self.print_expr_cond_paren(
             expr,
-            fixup.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr)
-                || parser::needs_par_as_let_scrutinee(expr.precedence().order()),
+            fixup.needs_par_as_let_scrutinee(expr),
             FixupContext::default(),
         );
     }
@@ -1558,7 +1545,7 @@
         match &pat.kind {
             PatKind::Wild => self.word("_"),
             PatKind::Never => self.word("!"),
-            PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => {
+            PatKind::Ident(BindingMode(by_ref, mutbl), ident, sub) => {
                 if mutbl.is_mut() {
                     self.word_nbsp("mut");
                 }
@@ -1654,7 +1641,7 @@
                 if mutbl.is_mut() {
                     self.word("mut ");
                 }
-                if let PatKind::Ident(ast::BindingAnnotation::MUT, ..) = inner.kind {
+                if let PatKind::Ident(ast::BindingMode::MUT, ..) = inner.kind {
                     self.popen();
                     self.print_pat(inner);
                     self.pclose();
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 6eff704..4cbdc9f 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -1,10 +1,10 @@
 use crate::pp::Breaks::Inconsistent;
+use crate::pprust::state::fixup::FixupContext;
 use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
 use ast::{ForLoopKind, MatchKind};
 use itertools::{Itertools, Position};
 use rustc_ast::ptr::P;
 use rustc_ast::token;
-use rustc_ast::util::classify;
 use rustc_ast::util::literal::escape_byte_str_symbol;
 use rustc_ast::util::parser::{self, AssocOp, Fixity};
 use rustc_ast::{self as ast, BlockCheckMode};
@@ -14,78 +14,6 @@
 };
 use std::fmt::Write;
 
-#[derive(Copy, Clone, Debug)]
-pub(crate) struct FixupContext {
-    /// Print expression such that it can be parsed back as a statement
-    /// consisting of the original expression.
-    ///
-    /// The effect of this is for binary operators in statement position to set
-    /// `leftmost_subexpression_in_stmt` when printing their left-hand operand.
-    ///
-    /// ```ignore (illustrative)
-    /// (match x {}) - 1;  // match needs parens when LHS of binary operator
-    ///
-    /// match x {};  // not when its own statement
-    /// ```
-    pub stmt: bool,
-
-    /// This is the difference between:
-    ///
-    /// ```ignore (illustrative)
-    /// (match x {}) - 1;  // subexpression needs parens
-    ///
-    /// let _ = match x {} - 1;  // no parens
-    /// ```
-    ///
-    /// There are 3 distinguishable contexts in which `print_expr` might be
-    /// called with the expression `$match` as its argument, where `$match`
-    /// represents an expression of kind `ExprKind::Match`:
-    ///
-    ///   - stmt=false leftmost_subexpression_in_stmt=false
-    ///
-    ///     Example: `let _ = $match - 1;`
-    ///
-    ///     No parentheses required.
-    ///
-    ///   - stmt=false leftmost_subexpression_in_stmt=true
-    ///
-    ///     Example: `$match - 1;`
-    ///
-    ///     Must parenthesize `($match)`, otherwise parsing back the output as a
-    ///     statement would terminate the statement after the closing brace of
-    ///     the match, parsing `-1;` as a separate statement.
-    ///
-    ///   - stmt=true leftmost_subexpression_in_stmt=false
-    ///
-    ///     Example: `$match;`
-    ///
-    ///     No parentheses required.
-    pub leftmost_subexpression_in_stmt: bool,
-
-    /// This is the difference between:
-    ///
-    /// ```ignore (illustrative)
-    /// if let _ = (Struct {}) {}  // needs parens
-    ///
-    /// match () {
-    ///     () if let _ = Struct {} => {}  // no parens
-    /// }
-    /// ```
-    pub parenthesize_exterior_struct_lit: bool,
-}
-
-/// The default amount of fixing is minimal fixing. Fixups should be turned on
-/// in a targeted fashion where needed.
-impl Default for FixupContext {
-    fn default() -> Self {
-        FixupContext {
-            stmt: false,
-            leftmost_subexpression_in_stmt: false,
-            parenthesize_exterior_struct_lit: false,
-        }
-    }
-}
-
 impl<'a> State<'a> {
     fn print_else(&mut self, els: Option<&ast::Expr>) {
         if let Some(_else) = els {
@@ -136,9 +64,7 @@
     /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
     /// `if cond { ... }`.
     fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
-        let fixup =
-            FixupContext { parenthesize_exterior_struct_lit: true, ..FixupContext::default() };
-        self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), fixup)
+        self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), FixupContext::new_cond())
     }
 
     /// Does `expr` need parentheses when printed in a condition position?
@@ -310,15 +236,7 @@
         // because the latter is valid syntax but with the incorrect meaning.
         // It's a match-expression followed by tuple-expression, not a function
         // call.
-        self.print_expr_maybe_paren(
-            func,
-            prec,
-            FixupContext {
-                stmt: false,
-                leftmost_subexpression_in_stmt: fixup.stmt || fixup.leftmost_subexpression_in_stmt,
-                ..fixup
-            },
-        );
+        self.print_expr_maybe_paren(func, prec, fixup.leftmost_subexpression());
 
         self.print_call_post(args)
     }
@@ -387,33 +305,17 @@
             _ => left_prec,
         };
 
-        self.print_expr_maybe_paren(
-            lhs,
-            left_prec,
-            FixupContext {
-                stmt: false,
-                leftmost_subexpression_in_stmt: fixup.stmt || fixup.leftmost_subexpression_in_stmt,
-                ..fixup
-            },
-        );
+        self.print_expr_maybe_paren(lhs, left_prec, fixup.leftmost_subexpression());
 
         self.space();
         self.word_space(op.node.as_str());
 
-        self.print_expr_maybe_paren(
-            rhs,
-            right_prec,
-            FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
-        );
+        self.print_expr_maybe_paren(rhs, right_prec, fixup.subsequent_subexpression());
     }
 
     fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
         self.word(op.as_str());
-        self.print_expr_maybe_paren(
-            expr,
-            parser::PREC_PREFIX,
-            FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
-        );
+        self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression());
     }
 
     fn print_expr_addr_of(
@@ -431,11 +333,7 @@
                 self.print_mutability(mutability, true);
             }
         }
-        self.print_expr_maybe_paren(
-            expr,
-            parser::PREC_PREFIX,
-            FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
-        );
+        self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression());
     }
 
     pub(super) fn print_expr(&mut self, expr: &ast::Expr, fixup: FixupContext) {
@@ -470,8 +368,7 @@
         //
         // Same applies to a small set of other expression kinds which eagerly
         // terminate a statement which opens with them.
-        let needs_par =
-            fixup.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr);
+        let needs_par = fixup.would_cause_statement_boundary(expr);
         if needs_par {
             self.popen();
             fixup = FixupContext::default();
@@ -519,16 +416,7 @@
             }
             ast::ExprKind::Cast(expr, ty) => {
                 let prec = AssocOp::As.precedence() as i8;
-                self.print_expr_maybe_paren(
-                    expr,
-                    prec,
-                    FixupContext {
-                        stmt: false,
-                        leftmost_subexpression_in_stmt: fixup.stmt
-                            || fixup.leftmost_subexpression_in_stmt,
-                        ..fixup
-                    },
-                );
+                self.print_expr_maybe_paren(expr, prec, fixup.leftmost_subexpression());
                 self.space();
                 self.word_space("as");
                 self.print_type(ty);
@@ -660,70 +548,34 @@
                 self.print_block_with_attrs(blk, attrs);
             }
             ast::ExprKind::Await(expr, _) => {
-                // Same fixups as ExprKind::MethodCall.
                 self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
                 self.word(".await");
             }
             ast::ExprKind::Assign(lhs, rhs, _) => {
-                // Same fixups as ExprKind::Binary.
                 let prec = AssocOp::Assign.precedence() as i8;
-                self.print_expr_maybe_paren(
-                    lhs,
-                    prec + 1,
-                    FixupContext {
-                        stmt: false,
-                        leftmost_subexpression_in_stmt: fixup.stmt
-                            || fixup.leftmost_subexpression_in_stmt,
-                        ..fixup
-                    },
-                );
+                self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression());
                 self.space();
                 self.word_space("=");
-                self.print_expr_maybe_paren(
-                    rhs,
-                    prec,
-                    FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
-                );
+                self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression());
             }
             ast::ExprKind::AssignOp(op, lhs, rhs) => {
-                // Same fixups as ExprKind::Binary.
                 let prec = AssocOp::Assign.precedence() as i8;
-                self.print_expr_maybe_paren(
-                    lhs,
-                    prec + 1,
-                    FixupContext {
-                        stmt: false,
-                        leftmost_subexpression_in_stmt: fixup.stmt
-                            || fixup.leftmost_subexpression_in_stmt,
-                        ..fixup
-                    },
-                );
+                self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression());
                 self.space();
                 self.word(op.node.as_str());
                 self.word_space("=");
-                self.print_expr_maybe_paren(
-                    rhs,
-                    prec,
-                    FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
-                );
+                self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression());
             }
             ast::ExprKind::Field(expr, ident) => {
-                // Same fixups as ExprKind::MethodCall.
                 self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
                 self.word(".");
                 self.print_ident(*ident);
             }
             ast::ExprKind::Index(expr, index, _) => {
-                // Same fixups as ExprKind::Call.
                 self.print_expr_maybe_paren(
                     expr,
                     parser::PREC_POSTFIX,
-                    FixupContext {
-                        stmt: false,
-                        leftmost_subexpression_in_stmt: fixup.stmt
-                            || fixup.leftmost_subexpression_in_stmt,
-                        ..fixup
-                    },
+                    fixup.leftmost_subexpression(),
                 );
                 self.word("[");
                 self.print_expr(index, FixupContext::default());
@@ -736,31 +588,14 @@
                 // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
                 let fake_prec = AssocOp::LOr.precedence() as i8;
                 if let Some(e) = start {
-                    self.print_expr_maybe_paren(
-                        e,
-                        fake_prec,
-                        FixupContext {
-                            stmt: false,
-                            leftmost_subexpression_in_stmt: fixup.stmt
-                                || fixup.leftmost_subexpression_in_stmt,
-                            ..fixup
-                        },
-                    );
+                    self.print_expr_maybe_paren(e, fake_prec, fixup.leftmost_subexpression());
                 }
                 match limits {
                     ast::RangeLimits::HalfOpen => self.word(".."),
                     ast::RangeLimits::Closed => self.word("..="),
                 }
                 if let Some(e) = end {
-                    self.print_expr_maybe_paren(
-                        e,
-                        fake_prec,
-                        FixupContext {
-                            stmt: false,
-                            leftmost_subexpression_in_stmt: false,
-                            ..fixup
-                        },
-                    );
+                    self.print_expr_maybe_paren(e, fake_prec, fixup.subsequent_subexpression());
                 }
             }
             ast::ExprKind::Underscore => self.word("_"),
@@ -777,11 +612,7 @@
                     self.print_expr_maybe_paren(
                         expr,
                         parser::PREC_JUMP,
-                        FixupContext {
-                            stmt: false,
-                            leftmost_subexpression_in_stmt: false,
-                            ..fixup
-                        },
+                        fixup.subsequent_subexpression(),
                     );
                 }
             }
@@ -799,11 +630,7 @@
                     self.print_expr_maybe_paren(
                         expr,
                         parser::PREC_JUMP,
-                        FixupContext {
-                            stmt: false,
-                            leftmost_subexpression_in_stmt: false,
-                            ..fixup
-                        },
+                        fixup.subsequent_subexpression(),
                     );
                 }
             }
@@ -816,11 +643,7 @@
                     self.print_expr_maybe_paren(
                         expr,
                         parser::PREC_JUMP,
-                        FixupContext {
-                            stmt: false,
-                            leftmost_subexpression_in_stmt: false,
-                            ..fixup
-                        },
+                        fixup.subsequent_subexpression(),
                     );
                 }
             }
@@ -830,7 +653,7 @@
                 self.print_expr_maybe_paren(
                     result,
                     parser::PREC_JUMP,
-                    FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
+                    fixup.subsequent_subexpression(),
                 );
             }
             ast::ExprKind::InlineAsm(a) => {
@@ -884,16 +707,11 @@
                     self.print_expr_maybe_paren(
                         expr,
                         parser::PREC_JUMP,
-                        FixupContext {
-                            stmt: false,
-                            leftmost_subexpression_in_stmt: false,
-                            ..fixup
-                        },
+                        fixup.subsequent_subexpression(),
                     );
                 }
             }
             ast::ExprKind::Try(e) => {
-                // Same fixups as ExprKind::MethodCall.
                 self.print_expr_maybe_paren(e, parser::PREC_POSTFIX, fixup);
                 self.word("?")
             }
@@ -961,7 +779,7 @@
                 }
                 _ => {
                     self.end(); // Close the ibox for the pattern.
-                    self.print_expr(body, FixupContext { stmt: true, ..FixupContext::default() });
+                    self.print_expr(body, FixupContext::new_stmt());
                     self.word(",");
                 }
             }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
new file mode 100644
index 0000000..d21cb82
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
@@ -0,0 +1,149 @@
+use rustc_ast::util::{classify, parser};
+use rustc_ast::Expr;
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct FixupContext {
+    /// Print expression such that it can be parsed back as a statement
+    /// consisting of the original expression.
+    ///
+    /// The effect of this is for binary operators in statement position to set
+    /// `leftmost_subexpression_in_stmt` when printing their left-hand operand.
+    ///
+    /// ```ignore (illustrative)
+    /// (match x {}) - 1;  // match needs parens when LHS of binary operator
+    ///
+    /// match x {};  // not when its own statement
+    /// ```
+    stmt: bool,
+
+    /// This is the difference between:
+    ///
+    /// ```ignore (illustrative)
+    /// (match x {}) - 1;  // subexpression needs parens
+    ///
+    /// let _ = match x {} - 1;  // no parens
+    /// ```
+    ///
+    /// There are 3 distinguishable contexts in which `print_expr` might be
+    /// called with the expression `$match` as its argument, where `$match`
+    /// represents an expression of kind `ExprKind::Match`:
+    ///
+    ///   - stmt=false leftmost_subexpression_in_stmt=false
+    ///
+    ///     Example: `let _ = $match - 1;`
+    ///
+    ///     No parentheses required.
+    ///
+    ///   - stmt=false leftmost_subexpression_in_stmt=true
+    ///
+    ///     Example: `$match - 1;`
+    ///
+    ///     Must parenthesize `($match)`, otherwise parsing back the output as a
+    ///     statement would terminate the statement after the closing brace of
+    ///     the match, parsing `-1;` as a separate statement.
+    ///
+    ///   - stmt=true leftmost_subexpression_in_stmt=false
+    ///
+    ///     Example: `$match;`
+    ///
+    ///     No parentheses required.
+    leftmost_subexpression_in_stmt: bool,
+
+    /// This is the difference between:
+    ///
+    /// ```ignore (illustrative)
+    /// if let _ = (Struct {}) {}  // needs parens
+    ///
+    /// match () {
+    ///     () if let _ = Struct {} => {}  // no parens
+    /// }
+    /// ```
+    parenthesize_exterior_struct_lit: bool,
+}
+
+/// The default amount of fixing is minimal fixing. Fixups should be turned on
+/// in a targeted fashion where needed.
+impl Default for FixupContext {
+    fn default() -> Self {
+        FixupContext {
+            stmt: false,
+            leftmost_subexpression_in_stmt: false,
+            parenthesize_exterior_struct_lit: false,
+        }
+    }
+}
+
+impl FixupContext {
+    /// Create the initial fixup for printing an expression in statement
+    /// position.
+    ///
+    /// This is currently also used for printing an expression as a match-arm,
+    /// but this is incorrect and leads to over-parenthesizing.
+    pub fn new_stmt() -> Self {
+        FixupContext { stmt: true, ..FixupContext::default() }
+    }
+
+    /// Create the initial fixup for printing an expression as the "condition"
+    /// of an `if` or `while`. There are a few other positions which are
+    /// grammatically equivalent and also use this, such as the iterator
+    /// expression in `for` and the scrutinee in `match`.
+    pub fn new_cond() -> Self {
+        FixupContext { parenthesize_exterior_struct_lit: true, ..FixupContext::default() }
+    }
+
+    /// Transform this fixup into the one that should apply when printing the
+    /// leftmost subexpression of the current expression.
+    ///
+    /// The leftmost subexpression is any subexpression that has the same first
+    /// token as the current expression, but has a different last token.
+    ///
+    /// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a
+    /// leftmost subexpression.
+    ///
+    /// Not every expression has a leftmost subexpression. For example neither
+    /// `-$a` nor `[$a]` have one.
+    pub fn leftmost_subexpression(self) -> Self {
+        FixupContext {
+            stmt: false,
+            leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt,
+            ..self
+        }
+    }
+
+    /// Transform this fixup into the one that should apply when printing any
+    /// subexpression that is neither a leftmost subexpression nor surrounded in
+    /// delimiters.
+    ///
+    /// This is for any subexpression that has a different first token than the
+    /// current expression, and is not surrounded by a paren/bracket/brace. For
+    /// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or
+    /// `$a.f($b)`.
+    pub fn subsequent_subexpression(self) -> Self {
+        FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..self }
+    }
+
+    /// Determine whether parentheses are needed around the given expression to
+    /// head off an unintended statement boundary.
+    ///
+    /// The documentation on `FixupContext::leftmost_subexpression_in_stmt` has
+    /// examples.
+    pub fn would_cause_statement_boundary(self, expr: &Expr) -> bool {
+        self.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr)
+    }
+
+    /// Determine whether parentheses are needed around the given `let`
+    /// scrutinee.
+    ///
+    /// In `if let _ = $e {}`, some examples of `$e` that would need parentheses
+    /// are:
+    ///
+    ///   - `Struct {}.f()`, because otherwise the `{` would be misinterpreted
+    ///     as the opening of the if's then-block.
+    ///
+    ///   - `true && false`, because otherwise this would be misinterpreted as a
+    ///     "let chain".
+    pub fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool {
+        self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr)
+            || parser::needs_par_as_let_scrutinee(expr.precedence().order())
+    }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 13f27c1..10886aa 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -1,5 +1,5 @@
 use crate::pp::Breaks::Inconsistent;
-use crate::pprust::state::expr::FixupContext;
+use crate::pprust::state::fixup::FixupContext;
 use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
 
 use ast::StaticItem;
diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs
index fada69c..dd87a5c 100644
--- a/compiler/rustc_attr/src/lib.rs
+++ b/compiler/rustc_attr/src/lib.rs
@@ -7,7 +7,6 @@
 #![allow(internal_features)]
 #![feature(rustdoc_internals)]
 #![doc(rust_logo)]
-#![feature(generic_nonzero)]
 #![feature(let_chains)]
 
 #[macro_use]
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 8ccf88e..61cd085 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -377,7 +377,7 @@
                 if p.span == self.expr_span {
                     self.pat = Some(p);
                 }
-                if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind {
+                if let hir::PatKind::Binding(hir::BindingMode::NONE, _, i, sub) = p.kind {
                     if i.span == self.expr_span || p.span == self.expr_span {
                         self.pat = Some(p);
                     }
@@ -1320,7 +1320,7 @@
             .into_iter()
             .map(|err| match err.obligation.predicate.kind().skip_binder() {
                 PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
-                    match predicate.self_ty().kind() {
+                    match *predicate.self_ty().kind() {
                         ty::Param(param_ty) => Ok((
                             generics.type_param(param_ty, tcx),
                             predicate.trait_ref.print_only_trait_path().to_string(),
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index dbea317..a505924 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1070,7 +1070,7 @@
                     // LL | blk();
                     //    | ----- this value implements `FnOnce`, which causes it to be moved when called
                     // ```
-                    if let ty::Param(param_ty) = self_ty.kind()
+                    if let ty::Param(param_ty) = *self_ty.kind()
                         && let generics = self.infcx.tcx.generics_of(self.mir_def_id())
                         && let param = generics.type_param(param_ty, self.infcx.tcx)
                         && let Some(hir_generics) = self
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 602a84c..8b31421 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -5,7 +5,7 @@
 use hir::{ExprKind, Param};
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::{self as hir, BindingAnnotation, ByRef, Node};
+use rustc_hir::{self as hir, BindingMode, ByRef, Node};
 use rustc_infer::traits;
 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
 use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt};
@@ -303,7 +303,7 @@
                 {
                     match *decl.local_info() {
                         LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
-                            binding_mode: BindingAnnotation(ByRef::No, Mutability::Not),
+                            binding_mode: BindingMode(ByRef::No, Mutability::Not),
                             opt_ty_info: Some(sp),
                             opt_match_place: _,
                             pat_span: _,
@@ -398,7 +398,7 @@
                 let upvar_hir_id = captured_place.get_root_variable();
 
                 if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
-                    && let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, upvar_ident, _) =
+                    && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
                         pat.kind
                 {
                     if upvar_ident.name == kw::SelfLower {
@@ -729,7 +729,7 @@
         debug!("local_decl: {:?}", local_decl);
         let pat_span = match *local_decl.local_info() {
             LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
-                binding_mode: BindingAnnotation(ByRef::No, Mutability::Not),
+                binding_mode: BindingMode(ByRef::No, Mutability::Not),
                 opt_ty_info: _,
                 opt_match_place: _,
                 pat_span,
@@ -1086,7 +1086,7 @@
             }
 
             LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
-                binding_mode: BindingAnnotation(ByRef::No, _),
+                binding_mode: BindingMode(ByRef::No, _),
                 opt_ty_info,
                 ..
             })) => {
@@ -1154,7 +1154,7 @@
             }
 
             LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
-                binding_mode: BindingAnnotation(ByRef::Yes(_), _),
+                binding_mode: BindingMode(ByRef::Yes(_), _),
                 ..
             })) => {
                 let pattern_span: Span = local_decl.source_info.span;
@@ -1356,7 +1356,7 @@
     match *local_decl.local_info() {
         // Check if mutably borrowing a mutable reference.
         LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
-            binding_mode: BindingAnnotation(ByRef::No, Mutability::Not),
+            binding_mode: BindingMode(ByRef::No, Mutability::Not),
             ..
         })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
         LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 7fbf4c4..a69b644 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -1315,7 +1315,8 @@
                     }
                     AggregateKind::Adt(..)
                     | AggregateKind::Array(..)
-                    | AggregateKind::Tuple { .. } => (),
+                    | AggregateKind::Tuple { .. }
+                    | AggregateKind::RawPtr(..) => (),
                 }
 
                 for operand in operands {
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 0600a10..61fa846 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1921,7 +1921,7 @@
                 }
             }
             AggregateKind::Array(ty) => Ok(ty),
-            AggregateKind::Tuple => {
+            AggregateKind::Tuple | AggregateKind::RawPtr(..) => {
                 unreachable!("This should have been covered in check_rvalues");
             }
         }
@@ -2518,6 +2518,7 @@
                 AggregateKind::Closure(_, _) => None,
                 AggregateKind::Coroutine(_, _) => None,
                 AggregateKind::CoroutineClosure(_, _) => None,
+                AggregateKind::RawPtr(_, _) => None,
             },
         }
     }
@@ -2539,6 +2540,10 @@
             return;
         }
 
+        if let AggregateKind::RawPtr(..) = aggregate_kind {
+            bug!("RawPtr should only be in runtime MIR");
+        }
+
         for (i, operand) in operands.iter_enumerated() {
             let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) {
                 Ok(field_ty) => field_ty,
@@ -2757,7 +2762,7 @@
                 ),
             ),
 
-            AggregateKind::Array(_) | AggregateKind::Tuple => {
+            AggregateKind::Array(_) | AggregateKind::Tuple | AggregateKind::RawPtr(..) => {
                 (CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty())
             }
         };
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index f73106c..85d54e9 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -180,7 +180,7 @@
 use crate::{deriving, errors};
 use rustc_ast::ptr::P;
 use rustc_ast::{
-    self as ast, BindingAnnotation, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
+    self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
     Mutability, PatKind, TyKind, VariantData,
 };
 use rustc_attr as attr;
@@ -1479,11 +1479,7 @@
                             struct_field.ident,
                             cx.pat(
                                 path.span,
-                                PatKind::Ident(
-                                    BindingAnnotation(by_ref, Mutability::Not),
-                                    path,
-                                    None,
-                                ),
+                                PatKind::Ident(BindingMode(by_ref, Mutability::Not), path, None),
                             ),
                         )
                     });
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
index 8b0b912..efa4be7 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
@@ -1,4 +1,13 @@
-#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)]
+#![feature(
+    no_core,
+    lang_items,
+    never_type,
+    linkage,
+    extern_types,
+    thread_local,
+    repr_simd,
+    raw_ref_op
+)]
 #![no_core]
 #![allow(dead_code, non_camel_case_types, internal_features)]
 
@@ -112,9 +121,7 @@
 
 static mut NUM: u8 = 6 * 7;
 
-// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
-#[allow(static_mut_refs)]
-static NUM_REF: &'static u8 = unsafe { &NUM };
+static NUM_REF: &'static u8 = unsafe { &*&raw const NUM };
 
 unsafe fn zeroed<T>() -> T {
     let mut uninit = MaybeUninit { uninit: () };
diff --git a/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch b/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch
new file mode 100644
index 0000000..21f5ee9
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch
@@ -0,0 +1,37 @@
+From 0d741cf82c3c908616abd39dc84ebf7d8702e0c3 Mon Sep 17 00:00:00 2001
+From: Chris Denton <chris@chrisdenton.dev>
+Date: Tue, 16 Apr 2024 15:51:34 +0000
+Subject: [PATCH] Revert use raw-dylib for Windows futex APIs
+
+---
+ library/std/src/sys/pal/windows/c.rs | 14 +-------------
+ 1 file changed, 1 insertion(+), 13 deletions(-)
+
+diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs
+index 9d58ce05f01..1c828bac4b6 100644
+--- a/library/std/src/sys/pal/windows/c.rs
++++ b/library/std/src/sys/pal/windows/c.rs
+@@ -357,19 +357,7 @@ pub fn GetTempPath2W(bufferlength: u32, buffer: PWSTR) -> u32 {
+ }
+ 
+ #[cfg(not(target_vendor = "win7"))]
+-// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library.
+-#[cfg_attr(
+-    target_arch = "x86",
+-    link(
+-        name = "api-ms-win-core-synch-l1-2-0",
+-        kind = "raw-dylib",
+-        import_name_type = "undecorated"
+-    )
+-)]
+-#[cfg_attr(
+-    not(target_arch = "x86"),
+-    link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib")
+-)]
++#[link(name = "synchronization")]
+ extern "system" {
+     pub fn WaitOnAddress(
+         address: *const c_void,
+-- 
+2.42.0.windows.2
+
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index f074214..f428c4c 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -813,6 +813,19 @@
                     );
                     lval.write_cvalue(fx, val);
                 }
+                Rvalue::Aggregate(ref kind, ref operands)
+                    if matches!(**kind, AggregateKind::RawPtr(..)) =>
+                {
+                    let ty = to_place_and_rval.1.ty(&fx.mir.local_decls, fx.tcx);
+                    let layout = fx.layout_of(fx.monomorphize(ty));
+                    let [data, meta] = &*operands.raw else {
+                        bug!("RawPtr fields: {operands:?}");
+                    };
+                    let data = codegen_operand(fx, data);
+                    let meta = codegen_operand(fx, meta);
+                    let ptr_val = CValue::pointer_from_data_and_meta(data, meta, layout);
+                    lval.write_cvalue(fx, ptr_val);
+                }
                 Rvalue::Aggregate(ref kind, ref operands) => {
                     let (variant_index, variant_dest, active_field_index) = match **kind {
                         mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 635ed6c..cb05c17 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -110,7 +110,7 @@
                 if fx.clif_type(layout.ty).is_some() {
                     return CValue::const_val(fx, layout, int);
                 } else {
-                    let raw_val = int.size().truncate(int.to_bits(int.size()).unwrap());
+                    let raw_val = int.size().truncate(int.assert_bits(int.size()));
                     let val = match int.size().bytes() {
                         1 => fx.bcx.ins().iconst(types::I8, raw_val as i64),
                         2 => fx.bcx.ins().iconst(types::I16, raw_val as i64),
@@ -491,27 +491,24 @@
                                         return None;
                                     }
                                     let scalar_int = mir_operand_get_const_val(fx, operand)?;
-                                    let scalar_int = match fx
-                                        .layout_of(*ty)
-                                        .size
-                                        .cmp(&scalar_int.size())
-                                    {
-                                        Ordering::Equal => scalar_int,
-                                        Ordering::Less => match ty.kind() {
-                                            ty::Uint(_) => ScalarInt::try_from_uint(
-                                                scalar_int.try_to_uint(scalar_int.size()).unwrap(),
-                                                fx.layout_of(*ty).size,
-                                            )
-                                            .unwrap(),
-                                            ty::Int(_) => ScalarInt::try_from_int(
-                                                scalar_int.try_to_int(scalar_int.size()).unwrap(),
-                                                fx.layout_of(*ty).size,
-                                            )
-                                            .unwrap(),
-                                            _ => unreachable!(),
-                                        },
-                                        Ordering::Greater => return None,
-                                    };
+                                    let scalar_int =
+                                        match fx.layout_of(*ty).size.cmp(&scalar_int.size()) {
+                                            Ordering::Equal => scalar_int,
+                                            Ordering::Less => match ty.kind() {
+                                                ty::Uint(_) => ScalarInt::try_from_uint(
+                                                    scalar_int.assert_uint(scalar_int.size()),
+                                                    fx.layout_of(*ty).size,
+                                                )
+                                                .unwrap(),
+                                                ty::Int(_) => ScalarInt::try_from_int(
+                                                    scalar_int.assert_int(scalar_int.size()),
+                                                    fx.layout_of(*ty).size,
+                                                )
+                                                .unwrap(),
+                                                _ => unreachable!(),
+                                            },
+                                            Ordering::Greater => return None,
+                                        };
                                     computed_scalar_int = Some(scalar_int);
                                 }
                                 Rvalue::Use(operand) => {
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index fc5b88a..8d52fd9 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -94,6 +94,23 @@
         CValue(CValueInner::ByValPair(value, extra), layout)
     }
 
+    /// For `AggregateKind::RawPtr`, create a pointer from its parts.
+    ///
+    /// Panics if the `layout` is not a raw pointer.
+    pub(crate) fn pointer_from_data_and_meta(
+        data: CValue<'tcx>,
+        meta: CValue<'tcx>,
+        layout: TyAndLayout<'tcx>,
+    ) -> CValue<'tcx> {
+        assert!(layout.ty.is_unsafe_ptr());
+        let inner = match (data.0, meta.0) {
+            (CValueInner::ByVal(p), CValueInner::ByVal(m)) => CValueInner::ByValPair(p, m),
+            (p @ CValueInner::ByVal(_), CValueInner::ByRef(..)) if meta.1.is_zst() => p,
+            _ => bug!("RawPtr operands {data:?} {meta:?}"),
+        };
+        CValue(inner, layout)
+    }
+
     pub(crate) fn layout(&self) -> TyAndLayout<'tcx> {
         self.1
     }
@@ -326,7 +343,7 @@
 
         let val = match layout.ty.kind() {
             ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => {
-                let const_val = const_val.to_bits(layout.size).unwrap();
+                let const_val = const_val.assert_bits(layout.size);
                 let lsb = fx.bcx.ins().iconst(types::I64, const_val as u64 as i64);
                 let msb = fx.bcx.ins().iconst(types::I64, (const_val >> 64) as u64 as i64);
                 fx.bcx.ins().iconcat(lsb, msb)
@@ -338,7 +355,7 @@
             | ty::Ref(..)
             | ty::RawPtr(..)
             | ty::FnPtr(..) => {
-                let raw_val = const_val.size().truncate(const_val.to_bits(layout.size).unwrap());
+                let raw_val = const_val.size().truncate(const_val.assert_bits(layout.size));
                 fx.bcx.ins().iconst(clif_ty, raw_val as i64)
             }
             ty::Float(FloatTy::F32) => {
diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
index add7788..5a7ddc4 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
@@ -2,7 +2,7 @@
 
 #![feature(
     no_core, unboxed_closures, start, lang_items, never_type, linkage,
-    extern_types, thread_local
+    extern_types, thread_local, raw_ref_op
 )]
 #![no_core]
 #![allow(dead_code, internal_features, non_camel_case_types)]
@@ -99,9 +99,7 @@
 
 static mut NUM: u8 = 6 * 7;
 
-// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
-#[allow(static_mut_refs)]
-static NUM_REF: &'static u8 = unsafe { &NUM };
+static NUM_REF: &'static u8 = unsafe { &* &raw const NUM };
 
 macro_rules! assert {
     ($e:expr) => {
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index 6253816..d353704 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -31,7 +31,7 @@
 use rustc_target::abi::{
     self, call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout, WrappingRange,
 };
-use rustc_target::spec::{HasTargetSpec, Target};
+use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi};
 
 use crate::common::{type_is_pointer, SignType, TypeReflection};
 use crate::context::CodegenCx;
@@ -2352,6 +2352,12 @@
     }
 }
 
+impl<'tcx> HasWasmCAbiOpt for Builder<'_, '_, 'tcx> {
+    fn wasm_c_abi_opt(&self) -> WasmCAbi {
+        self.cx.wasm_c_abi_opt()
+    }
+}
+
 pub trait ToGccComp {
     fn to_gcc_comparison(&self) -> ComparisonOp;
 }
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs
index 9e6cf3e..16a85b4 100644
--- a/compiler/rustc_codegen_gcc/src/context.rs
+++ b/compiler/rustc_codegen_gcc/src/context.rs
@@ -20,7 +20,7 @@
 use rustc_target::abi::{
     call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx,
 };
-use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
+use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, TlsModel, WasmCAbi};
 
 use crate::callee::get_fn;
 use crate::common::SignType;
@@ -557,6 +557,12 @@
     }
 }
 
+impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> {
+    fn wasm_c_abi_opt(&self) -> WasmCAbi {
+        self.tcx.sess.opts.unstable_opts.wasm_c_abi
+    }
+}
+
 impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
     type LayoutOfResult = TyAndLayout<'tcx>;
 
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index 0619000..d4a3e39 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -415,6 +415,7 @@
                 members.as_ptr() as *const &_,
                 true,
                 kind,
+                self.sess.target.arch == "arm64ec",
             );
             let ret = if r.into_result().is_err() {
                 let err = llvm::LLVMRustGetLastError();
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 160f361..4c2bdb2 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -17,7 +17,7 @@
 use rustc_hir::def_id::DefId;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::ty::layout::{
-    FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
+    FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout,
 };
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
 use rustc_sanitizers::{cfi, kcfi};
@@ -1702,4 +1702,128 @@
         };
         kcfi_bundle
     }
+
+    pub(crate) fn mcdc_parameters(
+        &mut self,
+        fn_name: &'ll Value,
+        hash: &'ll Value,
+        bitmap_bytes: &'ll Value,
+    ) -> &'ll Value {
+        debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
+
+        assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
+
+        let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) };
+        let llty = self.cx.type_func(
+            &[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()],
+            self.cx.type_void(),
+        );
+        let args = &[fn_name, hash, bitmap_bytes];
+        let args = self.check_call("call", llty, llfn, args);
+
+        unsafe {
+            let _ = llvm::LLVMRustBuildCall(
+                self.llbuilder,
+                llty,
+                llfn,
+                args.as_ptr() as *const &llvm::Value,
+                args.len() as c_uint,
+                [].as_ptr(),
+                0 as c_uint,
+            );
+            // Create condition bitmap named `mcdc.addr`.
+            let mut bx = Builder::with_cx(self.cx);
+            bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
+            let cond_bitmap = {
+                let alloca =
+                    llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), c"mcdc.addr".as_ptr());
+                llvm::LLVMSetAlignment(alloca, 4);
+                alloca
+            };
+            bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
+            cond_bitmap
+        }
+    }
+
+    pub(crate) fn mcdc_tvbitmap_update(
+        &mut self,
+        fn_name: &'ll Value,
+        hash: &'ll Value,
+        bitmap_bytes: &'ll Value,
+        bitmap_index: &'ll Value,
+        mcdc_temp: &'ll Value,
+    ) {
+        debug!(
+            "mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})",
+            fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp
+        );
+        assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
+
+        let llfn =
+            unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) };
+        let llty = self.cx.type_func(
+            &[
+                self.cx.type_ptr(),
+                self.cx.type_i64(),
+                self.cx.type_i32(),
+                self.cx.type_i32(),
+                self.cx.type_ptr(),
+            ],
+            self.cx.type_void(),
+        );
+        let args = &[fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp];
+        let args = self.check_call("call", llty, llfn, args);
+        unsafe {
+            let _ = llvm::LLVMRustBuildCall(
+                self.llbuilder,
+                llty,
+                llfn,
+                args.as_ptr() as *const &llvm::Value,
+                args.len() as c_uint,
+                [].as_ptr(),
+                0 as c_uint,
+            );
+        }
+        let i32_align = self.tcx().data_layout.i32_align.abi;
+        self.store(self.const_i32(0), mcdc_temp, i32_align);
+    }
+
+    pub(crate) fn mcdc_condbitmap_update(
+        &mut self,
+        fn_name: &'ll Value,
+        hash: &'ll Value,
+        cond_loc: &'ll Value,
+        mcdc_temp: &'ll Value,
+        bool_value: &'ll Value,
+    ) {
+        debug!(
+            "mcdc_condbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})",
+            fn_name, hash, cond_loc, mcdc_temp, bool_value
+        );
+        assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
+        let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(self.cx().llmod) };
+        let llty = self.cx.type_func(
+            &[
+                self.cx.type_ptr(),
+                self.cx.type_i64(),
+                self.cx.type_i32(),
+                self.cx.type_ptr(),
+                self.cx.type_i1(),
+            ],
+            self.cx.type_void(),
+        );
+        let args = &[fn_name, hash, cond_loc, mcdc_temp, bool_value];
+        self.check_call("call", llty, llfn, args);
+        unsafe {
+            let _ = llvm::LLVMRustBuildCall(
+                self.llbuilder,
+                llty,
+                llfn,
+                args.as_ptr() as *const &llvm::Value,
+                args.len() as c_uint,
+                [].as_ptr(),
+                0 as c_uint,
+            );
+        }
+    }
 }
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index a62dfe1..9e85c2d 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -260,7 +260,8 @@
 
     #[instrument(level = "debug", skip(self, llty))]
     pub(crate) fn get_static_inner(&self, def_id: DefId, llty: &'ll Type) -> &'ll Value {
-        if let Some(&g) = self.instances.borrow().get(&Instance::mono(self.tcx, def_id)) {
+        let instance = Instance::mono(self.tcx, def_id);
+        if let Some(&g) = self.instances.borrow().get(&instance) {
             trace!("used cached value");
             return g;
         }
@@ -273,7 +274,7 @@
                  statics defined in the same CGU, but did not for `{def_id:?}`"
         );
 
-        let sym = self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name;
+        let sym = self.tcx.symbol_name(instance).name;
         let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
 
         debug!(?sym, ?fn_attrs);
@@ -363,7 +364,7 @@
             }
         }
 
-        self.instances.borrow_mut().insert(Instance::mono(self.tcx, def_id), g);
+        self.instances.borrow_mut().insert(instance, g);
         g
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
index 2af2814..12a846a 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
@@ -1,4 +1,6 @@
-use rustc_middle::mir::coverage::{CodeRegion, CounterId, CovTerm, ExpressionId, MappingKind};
+use rustc_middle::mir::coverage::{
+    CodeRegion, ConditionInfo, CounterId, CovTerm, DecisionInfo, ExpressionId, MappingKind,
+};
 
 /// Must match the layout of `LLVMRustCounterKind`.
 #[derive(Copy, Clone, Debug)]
@@ -99,6 +101,86 @@
     /// associated with two counters, each representing the number of times the
     /// expression evaluates to true or false.
     BranchRegion = 4,
+
+    /// A DecisionRegion represents a top-level boolean expression and is
+    /// associated with a variable length bitmap index and condition number.
+    MCDCDecisionRegion = 5,
+
+    /// A Branch Region can be extended to include IDs to facilitate MC/DC.
+    MCDCBranchRegion = 6,
+}
+
+pub mod mcdc {
+    use rustc_middle::mir::coverage::{ConditionInfo, DecisionInfo};
+
+    /// Must match the layout of `LLVMRustMCDCDecisionParameters`.
+    #[repr(C)]
+    #[derive(Clone, Copy, Debug, Default)]
+    pub struct DecisionParameters {
+        bitmap_idx: u32,
+        conditions_num: u16,
+    }
+
+    // ConditionId in llvm is `unsigned int` at 18 while `int16_t` at [19](https://github.com/llvm/llvm-project/pull/81257)
+    type LLVMConditionId = i16;
+
+    /// Must match the layout of `LLVMRustMCDCBranchParameters`.
+    #[repr(C)]
+    #[derive(Clone, Copy, Debug, Default)]
+    pub struct BranchParameters {
+        condition_id: LLVMConditionId,
+        condition_ids: [LLVMConditionId; 2],
+    }
+
+    #[repr(C)]
+    #[derive(Clone, Copy, Debug)]
+    pub enum ParameterTag {
+        None = 0,
+        Decision = 1,
+        Branch = 2,
+    }
+    /// Same layout with `LLVMRustMCDCParameters`
+    #[repr(C)]
+    #[derive(Clone, Copy, Debug)]
+    pub struct Parameters {
+        tag: ParameterTag,
+        decision_params: DecisionParameters,
+        branch_params: BranchParameters,
+    }
+
+    impl Parameters {
+        pub fn none() -> Self {
+            Self {
+                tag: ParameterTag::None,
+                decision_params: Default::default(),
+                branch_params: Default::default(),
+            }
+        }
+        pub fn decision(decision_params: DecisionParameters) -> Self {
+            Self { tag: ParameterTag::Decision, decision_params, branch_params: Default::default() }
+        }
+        pub fn branch(branch_params: BranchParameters) -> Self {
+            Self { tag: ParameterTag::Branch, decision_params: Default::default(), branch_params }
+        }
+    }
+
+    impl From<ConditionInfo> for BranchParameters {
+        fn from(value: ConditionInfo) -> Self {
+            Self {
+                condition_id: value.condition_id.as_u32() as LLVMConditionId,
+                condition_ids: [
+                    value.false_next_id.as_u32() as LLVMConditionId,
+                    value.true_next_id.as_u32() as LLVMConditionId,
+                ],
+            }
+        }
+    }
+
+    impl From<DecisionInfo> for DecisionParameters {
+        fn from(value: DecisionInfo) -> Self {
+            Self { bitmap_idx: value.bitmap_idx, conditions_num: value.conditions_num }
+        }
+    }
 }
 
 /// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the
@@ -122,6 +204,7 @@
     /// for the false branch of the region.
     false_counter: Counter,
 
+    mcdc_params: mcdc::Parameters,
     /// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the
     /// file_id is an index into a function-specific `virtual_file_mapping` array of indexes
     /// that, in turn, are used to look up the filename for this region.
@@ -173,6 +256,26 @@
                 end_line,
                 end_col,
             ),
+            MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
+                Self::mcdc_branch_region(
+                    Counter::from_term(true_term),
+                    Counter::from_term(false_term),
+                    mcdc_params,
+                    local_file_id,
+                    start_line,
+                    start_col,
+                    end_line,
+                    end_col,
+                )
+            }
+            MappingKind::MCDCDecision(decision_info) => Self::decision_region(
+                decision_info,
+                local_file_id,
+                start_line,
+                start_col,
+                end_line,
+                end_col,
+            ),
         }
     }
 
@@ -187,6 +290,7 @@
         Self {
             counter,
             false_counter: Counter::ZERO,
+            mcdc_params: mcdc::Parameters::none(),
             file_id,
             expanded_file_id: 0,
             start_line,
@@ -209,6 +313,7 @@
         Self {
             counter,
             false_counter,
+            mcdc_params: mcdc::Parameters::none(),
             file_id,
             expanded_file_id: 0,
             start_line,
@@ -219,6 +324,54 @@
         }
     }
 
+    pub(crate) fn mcdc_branch_region(
+        counter: Counter,
+        false_counter: Counter,
+        condition_info: ConditionInfo,
+        file_id: u32,
+        start_line: u32,
+        start_col: u32,
+        end_line: u32,
+        end_col: u32,
+    ) -> Self {
+        Self {
+            counter,
+            false_counter,
+            mcdc_params: mcdc::Parameters::branch(condition_info.into()),
+            file_id,
+            expanded_file_id: 0,
+            start_line,
+            start_col,
+            end_line,
+            end_col,
+            kind: RegionKind::MCDCBranchRegion,
+        }
+    }
+
+    pub(crate) fn decision_region(
+        decision_info: DecisionInfo,
+        file_id: u32,
+        start_line: u32,
+        start_col: u32,
+        end_line: u32,
+        end_col: u32,
+    ) -> Self {
+        let mcdc_params = mcdc::Parameters::decision(decision_info.into());
+
+        Self {
+            counter: Counter::ZERO,
+            false_counter: Counter::ZERO,
+            mcdc_params,
+            file_id,
+            expanded_file_id: 0,
+            start_line,
+            start_col,
+            end_line,
+            end_col,
+            kind: RegionKind::MCDCDecisionRegion,
+        }
+    }
+
     // This function might be used in the future; the LLVM API is still evolving, as is coverage
     // support.
     #[allow(dead_code)]
@@ -233,6 +386,7 @@
         Self {
             counter: Counter::ZERO,
             false_counter: Counter::ZERO,
+            mcdc_params: mcdc::Parameters::none(),
             file_id,
             expanded_file_id,
             start_line,
@@ -256,6 +410,7 @@
         Self {
             counter: Counter::ZERO,
             false_counter: Counter::ZERO,
+            mcdc_params: mcdc::Parameters::none(),
             file_id,
             expanded_file_id: 0,
             start_line,
@@ -280,6 +435,7 @@
         Self {
             counter,
             false_counter: Counter::ZERO,
+            mcdc_params: mcdc::Parameters::none(),
             file_id,
             expanded_file_id: 0,
             start_line,
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 140566e..085ce15 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -13,7 +13,7 @@
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_llvm::RustString;
 use rustc_middle::bug;
-use rustc_middle::mir::coverage::CoverageKind;
+use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo};
 use rustc_middle::ty::layout::HasTyCtxt;
 use rustc_middle::ty::Instance;
 use rustc_target::abi::Align;
@@ -30,6 +30,7 @@
     pub(crate) function_coverage_map:
         RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
     pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
+    pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
 }
 
 impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
@@ -37,6 +38,7 @@
         Self {
             function_coverage_map: Default::default(),
             pgo_func_name_var_map: Default::default(),
+            mcdc_condition_bitmap_map: Default::default(),
         }
     }
 
@@ -45,6 +47,12 @@
     ) -> FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> {
         self.function_coverage_map.replace(FxIndexMap::default())
     }
+
+    /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap.
+    /// This value is named `mcdc.addr` (same as clang) and is a 32-bit integer.
+    fn try_get_mcdc_condition_bitmap(&self, instance: &Instance<'tcx>) -> Option<&'ll llvm::Value> {
+        self.mcdc_condition_bitmap_map.borrow().get(instance).copied()
+    }
 }
 
 // These methods used to be part of trait `CoverageInfoMethods`, which no longer
@@ -90,6 +98,10 @@
             return;
         };
 
+        if function_coverage_info.mcdc_bitmap_bytes > 0 {
+            ensure_mcdc_parameters(bx, instance, function_coverage_info);
+        }
+
         let Some(coverage_context) = bx.coverage_context() else { return };
         let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
         let func_coverage = coverage_map
@@ -131,10 +143,66 @@
             CoverageKind::ExpressionUsed { id } => {
                 func_coverage.mark_expression_id_seen(id);
             }
+            CoverageKind::CondBitmapUpdate { id, value, .. } => {
+                drop(coverage_map);
+                assert_ne!(
+                    id.as_u32(),
+                    0,
+                    "ConditionId of evaluated conditions should never be zero"
+                );
+                let cond_bitmap = coverage_context
+                    .try_get_mcdc_condition_bitmap(&instance)
+                    .expect("mcdc cond bitmap should have been allocated for updating");
+                let cond_loc = bx.const_i32(id.as_u32() as i32 - 1);
+                let bool_value = bx.const_bool(value);
+                let fn_name = bx.get_pgo_func_name_var(instance);
+                let hash = bx.const_u64(function_coverage_info.function_source_hash);
+                bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value);
+            }
+            CoverageKind::TestVectorBitmapUpdate { bitmap_idx } => {
+                drop(coverage_map);
+                let cond_bitmap = coverage_context
+                                    .try_get_mcdc_condition_bitmap(&instance)
+                                    .expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
+                let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes;
+                assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range");
+                assert!(
+                    bitmap_bytes <= function_coverage_info.mcdc_bitmap_bytes,
+                    "bitmap length disagreement: query says {bitmap_bytes} but function info only has {}",
+                    function_coverage_info.mcdc_bitmap_bytes
+                );
+
+                let fn_name = bx.get_pgo_func_name_var(instance);
+                let hash = bx.const_u64(function_coverage_info.function_source_hash);
+                let bitmap_bytes = bx.const_u32(bitmap_bytes);
+                let bitmap_index = bx.const_u32(bitmap_idx);
+                bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_bytes, bitmap_index, cond_bitmap);
+            }
         }
     }
 }
 
+fn ensure_mcdc_parameters<'ll, 'tcx>(
+    bx: &mut Builder<'_, 'll, 'tcx>,
+    instance: Instance<'tcx>,
+    function_coverage_info: &FunctionCoverageInfo,
+) {
+    let Some(cx) = bx.coverage_context() else { return };
+    if cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance) {
+        return;
+    }
+
+    let fn_name = bx.get_pgo_func_name_var(instance);
+    let hash = bx.const_u64(function_coverage_info.function_source_hash);
+    let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
+    let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes);
+    bx.coverage_context()
+        .expect("already checked above")
+        .mcdc_condition_bitmap_map
+        .borrow_mut()
+        .insert(instance, cond_bitmap);
+}
+
 /// Calls llvm::createPGOFuncNameVar() with the given function instance's
 /// mangled function name. The LLVM API returns an llvm::GlobalVariable
 /// containing the function name, with the specific variable name and linkage
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 5509baa..a10dc61 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1631,6 +1631,10 @@
 
     // Miscellaneous instructions
     pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value;
+    pub fn LLVMRustGetInstrProfMCDCParametersIntrinsic(M: &Module) -> &Value;
+    pub fn LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(M: &Module) -> &Value;
+    pub fn LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(M: &Module) -> &Value;
+
     pub fn LLVMRustBuildCall<'a>(
         B: &Builder<'a>,
         Ty: &'a Type,
@@ -2303,6 +2307,7 @@
         Members: *const &RustArchiveMember<'_>,
         WriteSymbtab: bool,
         Kind: ArchiveKind,
+        isEC: bool,
     ) -> LLVMRustResult;
     pub fn LLVMRustArchiveMemberNew<'a>(
         Filename: *const c_char,
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index c9e62e5..5552b38 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -270,9 +270,10 @@
             "sve2-bitperm",
             TargetFeatureFoldStrength::EnableOnly("neon"),
         ),
-        // The unaligned-scalar-mem feature was renamed to fast-unaligned-access.
-        ("riscv32" | "riscv64", "fast-unaligned-access") if get_version().0 <= 17 => {
-            LLVMFeature::new("unaligned-scalar-mem")
+        // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called
+        // `fast-unaligned-access`. In LLVM 19, it was split back out.
+        ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => {
+            LLVMFeature::new("fast-unaligned-access")
         }
         // For LLVM 18, enable the evex512 target feature if a avx512 target feature is enabled.
         ("x86", s) if get_version().0 >= 18 && s.starts_with("avx512") => {
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index fad6f43..85fcc4f 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -902,52 +902,45 @@
         }
     }
 
-    fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {
-        match strip {
-            Strip::None => {
-                // This will cause the Microsoft linker to generate a PDB file
-                // from the CodeView line tables in the object files.
-                self.cmd.arg("/DEBUG");
+    fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {
+        // This will cause the Microsoft linker to generate a PDB file
+        // from the CodeView line tables in the object files.
+        self.cmd.arg("/DEBUG");
 
-                // Default to emitting only the file name of the PDB file into
-                // the binary instead of the full path. Emitting the full path
-                // may leak private information (such as user names).
-                // See https://github.com/rust-lang/rust/issues/87825.
-                //
-                // This default behavior can be overridden by explicitly passing
-                // `-Clink-arg=/PDBALTPATH:...` to rustc.
-                self.cmd.arg("/PDBALTPATH:%_PDB%");
+        // Default to emitting only the file name of the PDB file into
+        // the binary instead of the full path. Emitting the full path
+        // may leak private information (such as user names).
+        // See https://github.com/rust-lang/rust/issues/87825.
+        //
+        // This default behavior can be overridden by explicitly passing
+        // `-Clink-arg=/PDBALTPATH:...` to rustc.
+        self.cmd.arg("/PDBALTPATH:%_PDB%");
 
-                // This will cause the Microsoft linker to embed .natvis info into the PDB file
-                let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc");
-                if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {
-                    for entry in natvis_dir {
-                        match entry {
-                            Ok(entry) => {
-                                let path = entry.path();
-                                if path.extension() == Some("natvis".as_ref()) {
-                                    let mut arg = OsString::from("/NATVIS:");
-                                    arg.push(path);
-                                    self.cmd.arg(arg);
-                                }
-                            }
-                            Err(error) => {
-                                self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error });
-                            }
+        // This will cause the Microsoft linker to embed .natvis info into the PDB file
+        let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc");
+        if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {
+            for entry in natvis_dir {
+                match entry {
+                    Ok(entry) => {
+                        let path = entry.path();
+                        if path.extension() == Some("natvis".as_ref()) {
+                            let mut arg = OsString::from("/NATVIS:");
+                            arg.push(path);
+                            self.cmd.arg(arg);
                         }
                     }
+                    Err(error) => {
+                        self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error });
+                    }
                 }
+            }
+        }
 
-                // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
-                for path in natvis_debugger_visualizers {
-                    let mut arg = OsString::from("/NATVIS:");
-                    arg.push(path);
-                    self.cmd.arg(arg);
-                }
-            }
-            Strip::Debuginfo | Strip::Symbols => {
-                self.cmd.arg("/DEBUG:NONE");
-            }
+        // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
+        for path in natvis_debugger_visualizers {
+            let mut arg = OsString::from("/NATVIS:");
+            arg.push(path);
+            self.cmd.arg(arg);
         }
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index b19f521..cce3f0e 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -363,6 +363,24 @@
                         },
                     ));
                 }
+                MonoItem::Fn(Instance {
+                    def: InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty)),
+                    args,
+                }) => {
+                    // A little sanity-check
+                    debug_assert_eq!(
+                        args.non_erasable_generics(tcx, def_id).skip(1).next(),
+                        Some(GenericArgKind::Type(ty))
+                    );
+                    symbols.push((
+                        ExportedSymbol::AsyncDropGlueCtorShim(ty),
+                        SymbolExportInfo {
+                            level: SymbolExportLevel::Rust,
+                            kind: SymbolExportKind::Text,
+                            used: false,
+                        },
+                    ));
+                }
                 _ => {
                     // Any other symbols don't qualify for sharing
                 }
@@ -385,6 +403,7 @@
     let mut instances: DefIdMap<UnordMap<_, _>> = Default::default();
 
     let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn();
+    let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn();
 
     for &cnum in cnums.iter() {
         for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
@@ -399,6 +418,18 @@
                         continue;
                     }
                 }
+                ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
+                    if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id {
+                        (
+                            async_drop_in_place_fn_def_id,
+                            tcx.mk_args(&[tcx.lifetimes.re_erased.into(), ty.into()]),
+                        )
+                    } else {
+                        // `drop_in_place` in place does not exist, don't try
+                        // to use it.
+                        continue;
+                    }
+                }
                 ExportedSymbol::NonGeneric(..)
                 | ExportedSymbol::ThreadLocalShim(..)
                 | ExportedSymbol::NoDefId(..) => {
@@ -534,6 +565,13 @@
             Instance::resolve_drop_in_place(tcx, ty),
             instantiating_crate,
         ),
+        ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
+            rustc_symbol_mangling::symbol_name_for_instance_in_crate(
+                tcx,
+                Instance::resolve_async_drop_in_place(tcx, ty),
+                instantiating_crate,
+            )
+        }
         ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(),
     }
 }
@@ -582,6 +620,9 @@
         // DropGlue always use the Rust calling convention and thus follow the target's default
         // symbol decoration scheme.
         ExportedSymbol::DropGlue(..) => None,
+        // AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the
+        // target's default symbol decoration scheme.
+        ExportedSymbol::AsyncDropGlueCtorShim(..) => None,
         // NoDefId always follow the target's default symbol decoration scheme.
         ExportedSymbol::NoDefId(..) => None,
         // ThreadLocalShim always follow the target's default symbol decoration scheme.
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 452398e..64c13fa 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -835,7 +835,10 @@
 
         let def = instance.map(|i| i.def);
 
-        if let Some(ty::InstanceDef::DropGlue(_, None)) = def {
+        if let Some(
+            ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None),
+        ) = def
+        {
             // Empty drop glue; a no-op.
             let target = target.unwrap();
             return helper.funclet_br(self, bx, target, mergeable_succ);
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 6725a6d..7823d4c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -9,7 +9,7 @@
 
 use rustc_hir as hir;
 use rustc_middle::mir;
-use rustc_middle::mir::Operand;
+use rustc_middle::mir::{AggregateKind, Operand};
 use rustc_middle::ty::cast::{CastTy, IntTy};
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
@@ -720,6 +720,24 @@
                 OperandRef { val: OperandValue::Immediate(static_), layout }
             }
             mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
+            mir::Rvalue::Aggregate(box mir::AggregateKind::RawPtr(..), ref fields) => {
+                let ty = rvalue.ty(self.mir, self.cx.tcx());
+                let layout = self.cx.layout_of(self.monomorphize(ty));
+                let [data, meta] = &*fields.raw else {
+                    bug!("RawPtr fields: {fields:?}");
+                };
+                let data = self.codegen_operand(bx, data);
+                let meta = self.codegen_operand(bx, meta);
+                match (data.val, meta.val) {
+                    (p @ OperandValue::Immediate(_), OperandValue::ZeroSized) => {
+                        OperandRef { val: p, layout }
+                    }
+                    (OperandValue::Immediate(p), OperandValue::Immediate(m)) => {
+                        OperandRef { val: OperandValue::Pair(p, m), layout }
+                    }
+                    _ => bug!("RawPtr operands {data:?} {meta:?}"),
+                }
+            }
             mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => {
                 // According to `rvalue_creates_operand`, only ZST
                 // aggregate rvalues are allowed to be operands.
@@ -1032,6 +1050,8 @@
             mir::Rvalue::ThreadLocalRef(_) |
             mir::Rvalue::Use(..) => // (*)
                 true,
+            // This always produces a `ty::RawPtr`, so will be Immediate or Pair
+            mir::Rvalue::Aggregate(box AggregateKind::RawPtr(..), ..) => true,
             mir::Rvalue::Repeat(..) |
             mir::Rvalue::Aggregate(..) => {
                 let ty = rvalue.ty(self.mir, self.cx.tcx());
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 4283ebc..40afd9f 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -10,20 +10,21 @@
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
 use rustc_target::abi::{self, Abi};
 
 use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter};
 use crate::const_eval::CheckAlignment;
-use crate::errors;
 use crate::errors::ConstEvalError;
-use crate::interpret::eval_nullary_intrinsic;
+use crate::errors::{self, DanglingPtrInFinal};
 use crate::interpret::{
     create_static_alloc, intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate,
     InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
     StackPopCleanup,
 };
+use crate::interpret::{eval_nullary_intrinsic, InternResult};
 use crate::CTRL_C_RECEIVED;
 
 // Returns a pointer to where the result lives
@@ -89,11 +90,35 @@
     }
 
     // Intern the result
-    intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
+    let intern_result = intern_const_alloc_recursive(ecx, intern_kind, &ret);
 
     // Since evaluation had no errors, validate the resulting constant.
     const_validate_mplace(&ecx, &ret, cid)?;
 
+    // Only report this after validation, as validaiton produces much better diagnostics.
+    // FIXME: ensure validation always reports this and stop making interning care about it.
+
+    match intern_result {
+        Ok(()) => {}
+        Err(InternResult::FoundDanglingPointer) => {
+            return Err(ecx
+                .tcx
+                .dcx()
+                .emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })
+                .into());
+        }
+        Err(InternResult::FoundBadMutablePointer) => {
+            // only report mutable pointers if there were no dangling pointers
+            let err_diag = errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind };
+            ecx.tcx.emit_node_span_lint(
+                lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE,
+                ecx.best_lint_scope(),
+                err_diag.span,
+                err_diag,
+            )
+        }
+    }
+
     Ok(R::make_result(ret, ecx))
 }
 
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index 704f597..caacc6f 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -295,8 +295,7 @@
                         &niche_start_val,
                     )?
                     .to_scalar()
-                    .try_to_int()
-                    .unwrap();
+                    .assert_int();
                 Ok(Some((tag, tag_field)))
             }
         }
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index d0f0190..d416827 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -16,19 +16,17 @@
 use hir::def::DefKind;
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
-use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult};
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::layout::TyAndLayout;
-use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::sym;
 
 use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy};
 use crate::const_eval;
-use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal, NestedStaticInThreadLocal};
+use crate::errors::NestedStaticInThreadLocal;
 
 pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
         'mir,
@@ -134,6 +132,12 @@
     Promoted,
 }
 
+#[derive(Debug)]
+pub enum InternResult {
+    FoundBadMutablePointer,
+    FoundDanglingPointer,
+}
+
 /// Intern `ret` and everything it references.
 ///
 /// This *cannot raise an interpreter error*. Doing so is left to validation, which
@@ -149,7 +153,7 @@
     ecx: &mut InterpCx<'mir, 'tcx, M>,
     intern_kind: InternKind,
     ret: &MPlaceTy<'tcx>,
-) -> Result<(), ErrorGuaranteed> {
+) -> Result<(), InternResult> {
     // We are interning recursively, and for mutability we are distinguishing the "root" allocation
     // that we are starting in, and all other allocations that we are encountering recursively.
     let (base_mutability, inner_mutability, is_static) = match intern_kind {
@@ -201,7 +205,7 @@
     // Whether we encountered a bad mutable pointer.
     // We want to first report "dangling" and then "mutable", so we need to delay reporting these
     // errors.
-    let mut found_bad_mutable_pointer = false;
+    let mut result = Ok(());
 
     // Keep interning as long as there are things to intern.
     // We show errors if there are dangling pointers, or mutable pointers in immutable contexts
@@ -251,7 +255,10 @@
             // on the promotion analysis not screwing up to ensure that it is sound to intern
             // promoteds as immutable.
             trace!("found bad mutable pointer");
-            found_bad_mutable_pointer = true;
+            // Prefer dangling pointer errors over mutable pointer errors
+            if result.is_ok() {
+                result = Err(InternResult::FoundBadMutablePointer);
+            }
         }
         if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
             // Already interned.
@@ -269,21 +276,15 @@
         // pointers before deciding which allocations can be made immutable; but for now we are
         // okay with losing some potential for immutability here. This can anyway only affect
         // `static mut`.
-        todo.extend(intern_shallow(ecx, alloc_id, inner_mutability).map_err(|()| {
-            ecx.tcx.dcx().emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })
-        })?);
+        match intern_shallow(ecx, alloc_id, inner_mutability) {
+            Ok(nested) => todo.extend(nested),
+            Err(()) => {
+                ecx.tcx.dcx().delayed_bug("found dangling pointer during const interning");
+                result = Err(InternResult::FoundDanglingPointer);
+            }
+        }
     }
-    if found_bad_mutable_pointer {
-        let err_diag = MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind };
-        ecx.tcx.emit_node_span_lint(
-            lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE,
-            ecx.best_lint_scope(),
-            err_diag.span,
-            err_diag,
-        )
-    }
-
-    Ok(())
+    result
 }
 
 /// Intern `ret`. This function assumes that `ret` references no other allocation.
diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs
index 474d35b..7ede90a 100644
--- a/compiler/rustc_const_eval/src/interpret/mod.rs
+++ b/compiler/rustc_const_eval/src/interpret/mod.rs
@@ -23,6 +23,7 @@
 pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup};
 pub use self::intern::{
     intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind,
+    InternResult,
 };
 pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
 pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index c120154..718c91b 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -6,9 +6,10 @@
 use either::{Either, Left, Right};
 
 use rustc_hir::def::Namespace;
+use rustc_middle::mir::interpret::ScalarSizeMismatch;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
-use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
+use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt};
 use rustc_middle::{mir, ty};
 use rustc_target::abi::{self, Abi, HasDataLayout, Size};
 
@@ -211,6 +212,12 @@
     }
 
     #[inline]
+    pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self {
+        assert_eq!(s.size(), layout.size);
+        Self::from_scalar(Scalar::from(s), layout)
+    }
+
+    #[inline]
     pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
         Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout))
     }
@@ -223,7 +230,6 @@
     pub fn try_from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
         Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout))
     }
-
     #[inline]
     pub fn from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Self {
         Self::from_scalar(Scalar::from_int(i, layout.size), layout)
@@ -242,6 +248,20 @@
         Self::from_scalar(Scalar::from_i8(c as i8), layout)
     }
 
+    /// Return the immediate as a `ScalarInt`. Ensures that it has the size that the layout of the
+    /// immediate indicates.
+    #[inline]
+    pub fn to_scalar_int(&self) -> InterpResult<'tcx, ScalarInt> {
+        let s = self.to_scalar().to_scalar_int()?;
+        if s.size() != self.layout.size {
+            throw_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
+                target_size: self.layout.size.bytes(),
+                data_size: s.size().bytes(),
+            }));
+        }
+        Ok(s)
+    }
+
     #[inline]
     pub fn to_const_int(self) -> ConstInt {
         assert!(self.layout.ty.is_integral());
@@ -792,7 +812,7 @@
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 5665bb4..9af755e 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -2,7 +2,7 @@
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
-use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty};
 use rustc_span::symbol::sym;
 use rustc_target::abi::Abi;
 
@@ -146,14 +146,20 @@
     fn binary_int_op(
         &self,
         bin_op: mir::BinOp,
-        // passing in raw bits
-        l: u128,
-        left_layout: TyAndLayout<'tcx>,
-        r: u128,
-        right_layout: TyAndLayout<'tcx>,
+        left: &ImmTy<'tcx, M::Provenance>,
+        right: &ImmTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
         use rustc_middle::mir::BinOp::*;
 
+        // This checks the size, so that we can just assert it below.
+        let l = left.to_scalar_int()?;
+        let r = right.to_scalar_int()?;
+        // Prepare to convert the values to signed or unsigned form.
+        let l_signed = || l.assert_int(left.layout.size);
+        let l_unsigned = || l.assert_uint(left.layout.size);
+        let r_signed = || r.assert_int(right.layout.size);
+        let r_unsigned = || r.assert_uint(right.layout.size);
+
         let throw_ub_on_overflow = match bin_op {
             AddUnchecked => Some(sym::unchecked_add),
             SubUnchecked => Some(sym::unchecked_sub),
@@ -165,69 +171,72 @@
 
         // Shift ops can have an RHS with a different numeric type.
         if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
-            let size = left_layout.size.bits();
+            let size = left.layout.size.bits();
             // The shift offset is implicitly masked to the type size. (This is the one MIR operator
             // that does *not* directly map to a single LLVM operation.) Compute how much we
             // actually shift and whether there was an overflow due to shifting too much.
-            let (shift_amount, overflow) = if right_layout.abi.is_signed() {
-                let shift_amount = self.sign_extend(r, right_layout) as i128;
+            let (shift_amount, overflow) = if right.layout.abi.is_signed() {
+                let shift_amount = r_signed();
                 let overflow = shift_amount < 0 || shift_amount >= i128::from(size);
+                // Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result
+                // of the `as` will be equal modulo `size` (since it is a power of two).
                 let masked_amount = (shift_amount as u128) % u128::from(size);
-                debug_assert_eq!(overflow, shift_amount != (masked_amount as i128));
+                assert_eq!(overflow, shift_amount != (masked_amount as i128));
                 (masked_amount, overflow)
             } else {
-                let shift_amount = r;
+                let shift_amount = r_unsigned();
                 let masked_amount = shift_amount % u128::from(size);
                 (masked_amount, shift_amount != masked_amount)
             };
             let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit
             // Compute the shifted result.
-            let result = if left_layout.abi.is_signed() {
-                let l = self.sign_extend(l, left_layout) as i128;
+            let result = if left.layout.abi.is_signed() {
+                let l = l_signed();
                 let result = match bin_op {
                     Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
                     Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
                     _ => bug!(),
                 };
-                result as u128
+                ScalarInt::truncate_from_int(result, left.layout.size).0
             } else {
-                match bin_op {
+                let l = l_unsigned();
+                let result = match bin_op {
                     Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
                     Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
                     _ => bug!(),
-                }
+                };
+                ScalarInt::truncate_from_uint(result, left.layout.size).0
             };
-            let truncated = self.truncate(result, left_layout);
 
             if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
                 throw_ub_custom!(
                     fluent::const_eval_overflow_shift,
-                    val = if right_layout.abi.is_signed() {
-                        (self.sign_extend(r, right_layout) as i128).to_string()
+                    val = if right.layout.abi.is_signed() {
+                        r_signed().to_string()
                     } else {
-                        r.to_string()
+                        r_unsigned().to_string()
                     },
                     name = intrinsic_name
                 );
             }
 
-            return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
+            return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
         }
 
         // For the remaining ops, the types must be the same on both sides
-        if left_layout.ty != right_layout.ty {
+        if left.layout.ty != right.layout.ty {
             span_bug!(
                 self.cur_span(),
                 "invalid asymmetric binary op {bin_op:?}: {l:?} ({l_ty}), {r:?} ({r_ty})",
-                l_ty = left_layout.ty,
-                r_ty = right_layout.ty,
+                l_ty = left.layout.ty,
+                r_ty = right.layout.ty,
             )
         }
 
-        let size = left_layout.size;
+        let size = left.layout.size;
 
         // Operations that need special treatment for signed integers
-        if left_layout.abi.is_signed() {
+        if left.layout.abi.is_signed() {
             let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
                 Lt => Some(i128::lt),
                 Le => Some(i128::le),
@@ -236,18 +245,14 @@
                 _ => None,
             };
             if let Some(op) = op {
-                let l = self.sign_extend(l, left_layout) as i128;
-                let r = self.sign_extend(r, right_layout) as i128;
-                return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false));
+                return Ok((ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx), false));
             }
             if bin_op == Cmp {
-                let l = self.sign_extend(l, left_layout) as i128;
-                let r = self.sign_extend(r, right_layout) as i128;
-                return Ok(self.three_way_compare(l, r));
+                return Ok(self.three_way_compare(l_signed(), r_signed()));
             }
             let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
-                Div if r == 0 => throw_ub!(DivisionByZero),
-                Rem if r == 0 => throw_ub!(RemainderByZero),
+                Div if r.is_null() => throw_ub!(DivisionByZero),
+                Rem if r.is_null() => throw_ub!(RemainderByZero),
                 Div => Some(i128::overflowing_div),
                 Rem => Some(i128::overflowing_rem),
                 Add | AddUnchecked => Some(i128::overflowing_add),
@@ -256,8 +261,8 @@
                 _ => None,
             };
             if let Some(op) = op {
-                let l = self.sign_extend(l, left_layout) as i128;
-                let r = self.sign_extend(r, right_layout) as i128;
+                let l = l_signed();
+                let r = r_signed();
 
                 // We need a special check for overflowing Rem and Div since they are *UB*
                 // on overflow, which can happen with "int_min $OP -1".
@@ -272,17 +277,19 @@
                 }
 
                 let (result, oflo) = op(l, r);
-                // This may be out-of-bounds for the result type, so we have to truncate ourselves.
+                // This may be out-of-bounds for the result type, so we have to truncate.
                 // If that truncation loses any information, we have an overflow.
-                let result = result as u128;
-                let truncated = self.truncate(result, left_layout);
-                let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
+                let (result, lossy) = ScalarInt::truncate_from_int(result, left.layout.size);
+                let overflow = oflo || lossy;
                 if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
                     throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
                 }
-                return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
+                return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
             }
         }
+        // From here on it's okay to treat everything as unsigned.
+        let l = l_unsigned();
+        let r = r_unsigned();
 
         if bin_op == Cmp {
             return Ok(self.three_way_compare(l, r));
@@ -297,12 +304,12 @@
             Gt => ImmTy::from_bool(l > r, *self.tcx),
             Ge => ImmTy::from_bool(l >= r, *self.tcx),
 
-            BitOr => ImmTy::from_uint(l | r, left_layout),
-            BitAnd => ImmTy::from_uint(l & r, left_layout),
-            BitXor => ImmTy::from_uint(l ^ r, left_layout),
+            BitOr => ImmTy::from_uint(l | r, left.layout),
+            BitAnd => ImmTy::from_uint(l & r, left.layout),
+            BitXor => ImmTy::from_uint(l ^ r, left.layout),
 
             Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
-                assert!(!left_layout.abi.is_signed());
+                assert!(!left.layout.abi.is_signed());
                 let op: fn(u128, u128) -> (u128, bool) = match bin_op {
                     Add | AddUnchecked => u128::overflowing_add,
                     Sub | SubUnchecked => u128::overflowing_sub,
@@ -316,21 +323,21 @@
                 let (result, oflo) = op(l, r);
                 // Truncate to target type.
                 // If that truncation loses any information, we have an overflow.
-                let truncated = self.truncate(result, left_layout);
-                let overflow = oflo || truncated != result;
+                let (result, lossy) = ScalarInt::truncate_from_uint(result, left.layout.size);
+                let overflow = oflo || lossy;
                 if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
                     throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
                 }
-                return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
+                return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
             }
 
             _ => span_bug!(
                 self.cur_span(),
                 "invalid binary op {:?}: {:?}, {:?} (both {})",
                 bin_op,
-                l,
-                r,
-                right_layout.ty,
+                left,
+                right,
+                right.layout.ty,
             ),
         };
 
@@ -427,9 +434,7 @@
                     right.layout.ty
                 );
 
-                let l = left.to_scalar().to_bits(left.layout.size)?;
-                let r = right.to_scalar().to_bits(right.layout.size)?;
-                self.binary_int_op(bin_op, l, left.layout, r, right.layout)
+                self.binary_int_op(bin_op, left, right)
             }
             _ if left.layout.ty.is_any_ptr() => {
                 // The RHS type must be a `pointer` *or an integer type* (for `Offset`).
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 1549edd..8364a5a 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -1058,7 +1058,7 @@
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index c3f26da..b29034e 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -9,7 +9,9 @@
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
 
-use super::{ImmTy, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar};
+use super::{
+    ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar,
+};
 use crate::util;
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
@@ -303,6 +305,27 @@
                 let variant_dest = self.project_downcast(dest, variant_index)?;
                 (variant_index, variant_dest, active_field_index)
             }
+            mir::AggregateKind::RawPtr(..) => {
+                // Pointers don't have "fields" in the normal sense, so the
+                // projection-based code below would either fail in projection
+                // or in type mismatches. Instead, build an `Immediate` from
+                // the parts and write that to the destination.
+                let [data, meta] = &operands.raw else {
+                    bug!("{kind:?} should have 2 operands, had {operands:?}");
+                };
+                let data = self.eval_operand(data, None)?;
+                let data = self.read_pointer(&data)?;
+                let meta = self.eval_operand(meta, None)?;
+                let meta = if meta.layout.is_zst() {
+                    MemPlaceMeta::None
+                } else {
+                    MemPlaceMeta::Meta(self.read_scalar(&meta)?)
+                };
+                let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self);
+                let ptr = ImmTy::from_immediate(ptr_imm, dest.layout);
+                self.copy_op(&ptr, dest)?;
+                return Ok(());
+            }
             _ => (FIRST_VARIANT, dest.clone(), None),
         };
         if active_field_index.is_some() {
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index c0e27e8..908c4da 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -558,6 +558,7 @@
             | ty::InstanceDef::CloneShim(..)
             | ty::InstanceDef::FnPtrAddrShim(..)
             | ty::InstanceDef::ThreadLocalShim(..)
+            | ty::InstanceDef::AsyncDropGlueCtorShim(..)
             | ty::InstanceDef::Item(_) => {
                 // We need MIR for this fn
                 let Some((body, instance)) = M::find_mir_or_eval_fn(
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 9911c59..b8a1733 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -449,67 +449,41 @@
             // `!` is a ZST and we want to validate it.
             if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
                 let mut skip_recursive_check = false;
-                // Let's see what kind of memory this points to.
-                // `unwrap` since dangling pointers have already been handled.
-                let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id).unwrap();
-                let alloc_actual_mutbl = match alloc_kind {
-                    GlobalAlloc::Static(did) => {
-                        // Special handling for pointers to statics (irrespective of their type).
-                        assert!(!self.ecx.tcx.is_thread_local_static(did));
-                        assert!(self.ecx.tcx.is_static(did));
-                        // Mode-specific checks
-                        match self.ctfe_mode {
-                            Some(
-                                CtfeValidationMode::Static { .. }
-                                | CtfeValidationMode::Promoted { .. },
-                            ) => {
-                                // We skip recursively checking other statics. These statics must be sound by
-                                // themselves, and the only way to get broken statics here is by using
-                                // unsafe code.
-                                // The reasons we don't check other statics is twofold. For one, in all
-                                // sound cases, the static was already validated on its own, and second, we
-                                // trigger cycle errors if we try to compute the value of the other static
-                                // and that static refers back to us (potentially through a promoted).
-                                // This could miss some UB, but that's fine.
-                                skip_recursive_check = true;
-                            }
-                            Some(CtfeValidationMode::Const { .. }) => {
-                                // We can't recursively validate `extern static`, so we better reject them.
-                                if self.ecx.tcx.is_foreign_item(did) {
-                                    throw_validation_failure!(self.path, ConstRefToExtern);
-                                }
-                            }
-                            None => {}
+                let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
+                if let GlobalAlloc::Static(did) = self.ecx.tcx.global_alloc(alloc_id) {
+                    let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() };
+                    // Special handling for pointers to statics (irrespective of their type).
+                    assert!(!self.ecx.tcx.is_thread_local_static(did));
+                    assert!(self.ecx.tcx.is_static(did));
+                    // Mode-specific checks
+                    match self.ctfe_mode {
+                        Some(
+                            CtfeValidationMode::Static { .. } | CtfeValidationMode::Promoted { .. },
+                        ) => {
+                            // We skip recursively checking other statics. These statics must be sound by
+                            // themselves, and the only way to get broken statics here is by using
+                            // unsafe code.
+                            // The reasons we don't check other statics is twofold. For one, in all
+                            // sound cases, the static was already validated on its own, and second, we
+                            // trigger cycle errors if we try to compute the value of the other static
+                            // and that static refers back to us (potentially through a promoted).
+                            // This could miss some UB, but that's fine.
+                            // We still walk nested allocations, as they are fundamentally part of this validation run.
+                            // This means we will also recurse into nested statics of *other*
+                            // statics, even though we do not recurse into other statics directly.
+                            // That's somewhat inconsistent but harmless.
+                            skip_recursive_check = !nested;
                         }
-                        // Return alloc mutability. For "root" statics we look at the type to account for interior
-                        // mutability; for nested statics we have no type and directly use the annotated mutability.
-                        let DefKind::Static { mutability, nested } = self.ecx.tcx.def_kind(did)
-                        else {
-                            bug!()
-                        };
-                        match (mutability, nested) {
-                            (Mutability::Mut, _) => Mutability::Mut,
-                            (Mutability::Not, true) => Mutability::Not,
-                            (Mutability::Not, false)
-                                if !self
-                                    .ecx
-                                    .tcx
-                                    .type_of(did)
-                                    .no_bound_vars()
-                                    .expect("statics should not have generic parameters")
-                                    .is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all()) =>
-                            {
-                                Mutability::Mut
+                        Some(CtfeValidationMode::Const { .. }) => {
+                            // We can't recursively validate `extern static`, so we better reject them.
+                            if self.ecx.tcx.is_foreign_item(did) {
+                                throw_validation_failure!(self.path, ConstRefToExtern);
                             }
-                            (Mutability::Not, false) => Mutability::Not,
                         }
+                        None => {}
                     }
-                    GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
-                    GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {
-                        // These are immutable, we better don't allow mutable pointers here.
-                        Mutability::Not
-                    }
-                };
+                }
+
                 // Mutability check.
                 // If this allocation has size zero, there is no actual mutability here.
                 let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id);
@@ -708,20 +682,61 @@
     fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool {
         if let Some(mplace) = op.as_mplace_or_imm().left() {
             if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
-                let mutability = match self.ecx.tcx.global_alloc(alloc_id) {
-                    GlobalAlloc::Static(_) => {
-                        self.ecx.memory.alloc_map.get(alloc_id).unwrap().1.mutability
-                    }
-                    GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
-                    _ => span_bug!(self.ecx.tcx.span, "not a memory allocation"),
-                };
-                return mutability == Mutability::Mut;
+                return mutability(self.ecx, alloc_id).is_mut();
             }
         }
         false
     }
 }
 
+/// Returns whether the allocation is mutable, and whether it's actually a static.
+/// For "root" statics we look at the type to account for interior
+/// mutability; for nested statics we have no type and directly use the annotated mutability.
+fn mutability<'mir, 'tcx: 'mir>(
+    ecx: &InterpCx<'mir, 'tcx, impl Machine<'mir, 'tcx>>,
+    alloc_id: AllocId,
+) -> Mutability {
+    // Let's see what kind of memory this points to.
+    // We're not using `try_global_alloc` since dangling pointers have already been handled.
+    match ecx.tcx.global_alloc(alloc_id) {
+        GlobalAlloc::Static(did) => {
+            let DefKind::Static { mutability, nested } = ecx.tcx.def_kind(did) else { bug!() };
+            if nested {
+                assert!(
+                    ecx.memory.alloc_map.get(alloc_id).is_none(),
+                    "allocations of nested statics are already interned: {alloc_id:?}, {did:?}"
+                );
+                // Nested statics in a `static` are never interior mutable,
+                // so just use the declared mutability.
+                mutability
+            } else {
+                let mutability = match mutability {
+                    Mutability::Not
+                        if !ecx
+                            .tcx
+                            .type_of(did)
+                            .no_bound_vars()
+                            .expect("statics should not have generic parameters")
+                            .is_freeze(*ecx.tcx, ty::ParamEnv::reveal_all()) =>
+                    {
+                        Mutability::Mut
+                    }
+                    _ => mutability,
+                };
+                if let Some((_, alloc)) = ecx.memory.alloc_map.get(alloc_id) {
+                    assert_eq!(alloc.mutability, mutability);
+                }
+                mutability
+            }
+        }
+        GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
+        GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {
+            // These are immutable, we better don't allow mutable pointers here.
+            Mutability::Not
+        }
+    }
+}
+
 impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     for ValidityVisitor<'rt, 'mir, 'tcx, M>
 {
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 50420aa..d27d427 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -11,7 +11,6 @@
 #![feature(assert_matches)]
 #![feature(box_patterns)]
 #![feature(decl_macro)]
-#![feature(generic_nonzero)]
 #![feature(let_chains)]
 #![feature(slice_ptr_get)]
 #![feature(strict_provenance)]
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index a499e4b..bf5592c 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -923,6 +923,47 @@
                         }
                     }
                 }
+                AggregateKind::RawPtr(pointee_ty, mutability) => {
+                    if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
+                        // It would probably be fine to support this in earlier phases,
+                        // but at the time of writing it's only ever introduced from intrinsic lowering,
+                        // so earlier things just `bug!` on it.
+                        self.fail(location, "RawPtr should be in runtime MIR only");
+                    }
+
+                    if fields.len() != 2 {
+                        self.fail(location, "raw pointer aggregate must have 2 fields");
+                    } else {
+                        let data_ptr_ty = fields.raw[0].ty(self.body, self.tcx);
+                        let metadata_ty = fields.raw[1].ty(self.body, self.tcx);
+                        if let ty::RawPtr(in_pointee, in_mut) = data_ptr_ty.kind() {
+                            if *in_mut != mutability {
+                                self.fail(location, "input and output mutability must match");
+                            }
+
+                            // FIXME: check `Thin` instead of `Sized`
+                            if !in_pointee.is_sized(self.tcx, self.param_env) {
+                                self.fail(location, "input pointer must be thin");
+                            }
+                        } else {
+                            self.fail(
+                                location,
+                                "first operand to raw pointer aggregate must be a raw pointer",
+                            );
+                        }
+
+                        // FIXME: Check metadata more generally
+                        if pointee_ty.is_slice() {
+                            if !self.mir_assign_valid_types(metadata_ty, self.tcx.types.usize) {
+                                self.fail(location, "slice metadata must be usize");
+                            }
+                        } else if pointee_ty.is_sized(self.tcx, self.param_env) {
+                            if metadata_ty != self.tcx.types.unit {
+                                self.fail(location, "metadata for pointer-to-thin must be unit");
+                            }
+                        }
+                    }
+                }
             },
             Rvalue::Ref(_, BorrowKind::Fake, _) => {
                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
index 7a77f2c..78d05a6 100644
--- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
@@ -70,21 +70,21 @@
 }
 
 /// A "depth-first search" iterator for a directed graph.
-pub struct DepthFirstSearch<'graph, G>
+pub struct DepthFirstSearch<G>
 where
-    G: ?Sized + DirectedGraph + Successors,
+    G: DirectedGraph + Successors,
 {
-    graph: &'graph G,
+    graph: G,
     stack: Vec<G::Node>,
     visited: BitSet<G::Node>,
 }
 
-impl<'graph, G> DepthFirstSearch<'graph, G>
+impl<G> DepthFirstSearch<G>
 where
-    G: ?Sized + DirectedGraph + Successors,
+    G: DirectedGraph + Successors,
 {
-    pub fn new(graph: &'graph G) -> Self {
-        Self { graph, stack: vec![], visited: BitSet::new_empty(graph.num_nodes()) }
+    pub fn new(graph: G) -> Self {
+        Self { stack: vec![], visited: BitSet::new_empty(graph.num_nodes()), graph }
     }
 
     /// Version of `push_start_node` that is convenient for chained
@@ -125,9 +125,9 @@
     }
 }
 
-impl<G> std::fmt::Debug for DepthFirstSearch<'_, G>
+impl<G> std::fmt::Debug for DepthFirstSearch<G>
 where
-    G: ?Sized + DirectedGraph + Successors,
+    G: DirectedGraph + Successors,
 {
     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         let mut f = fmt.debug_set();
@@ -138,9 +138,9 @@
     }
 }
 
-impl<G> Iterator for DepthFirstSearch<'_, G>
+impl<G> Iterator for DepthFirstSearch<G>
 where
-    G: ?Sized + DirectedGraph + Successors,
+    G: DirectedGraph + Successors,
 {
     type Item = G::Node;
 
diff --git a/compiler/rustc_data_structures/src/graph/mod.rs b/compiler/rustc_data_structures/src/graph/mod.rs
index 3ae3023..103ddd9 100644
--- a/compiler/rustc_data_structures/src/graph/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/mod.rs
@@ -46,9 +46,35 @@
         .is_some()
 }
 
-pub fn depth_first_search<G>(graph: &G, from: G::Node) -> iterate::DepthFirstSearch<'_, G>
+pub fn depth_first_search<G>(graph: G, from: G::Node) -> iterate::DepthFirstSearch<G>
 where
-    G: ?Sized + Successors,
+    G: Successors,
 {
     iterate::DepthFirstSearch::new(graph).with_start_node(from)
 }
+
+pub fn depth_first_search_as_undirected<G>(
+    graph: G,
+    from: G::Node,
+) -> iterate::DepthFirstSearch<impl Successors<Node = G::Node>>
+where
+    G: Successors + Predecessors,
+{
+    struct AsUndirected<G>(G);
+
+    impl<G: DirectedGraph> DirectedGraph for AsUndirected<G> {
+        type Node = G::Node;
+
+        fn num_nodes(&self) -> usize {
+            self.0.num_nodes()
+        }
+    }
+
+    impl<G: Successors + Predecessors> Successors for AsUndirected<G> {
+        fn successors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
+            self.0.successors(node).chain(self.0.predecessors(node))
+        }
+    }
+
+    iterate::DepthFirstSearch::new(AsUndirected(graph)).with_start_node(from)
+}
diff --git a/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs
index 26c8646..120244c 100644
--- a/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs
@@ -1,99 +1,235 @@
-use crate::graph::{DirectedGraph, NumEdges, Successors};
+use crate::graph::{DirectedGraph, NumEdges, Predecessors, Successors};
 use rustc_index::{Idx, IndexVec};
 
 #[cfg(test)]
 mod tests;
 
-pub struct VecGraph<N: Idx> {
-    /// Maps from a given node to an index where the set of successors
-    /// for that node starts. The index indexes into the `edges`
-    /// vector. To find the range for a given node, we look up the
-    /// start for that node and then the start for the next node
-    /// (i.e., with an index 1 higher) and get the range between the
-    /// two. This vector always has an extra entry so that this works
-    /// even for the max element.
+/// A directed graph, efficient for cases where node indices are pre-existing.
+///
+/// If `BR` is true, the graph will store back-references, allowing you to get predecessors.
+pub struct VecGraph<N: Idx, const BR: bool = false> {
+    // This is basically a `HashMap<N, (Vec<N>, If<BR, Vec<N>>)>` -- a map from a node index, to
+    // a list of targets of outgoing edges and (if enabled) a list of sources of incoming edges.
+    //
+    // However, it is condensed into two arrays as an optimization.
+    //
+    // `node_starts[n]` is the start of the list of targets of outgoing edges for node `n`.
+    // So you can get node's successors with `edge_targets[node_starts[n]..node_starts[n + 1]]`.
+    //
+    // If `BR` is true (back references are enabled), then `node_starts[n + edge_count]` is the
+    // start of the list of *sources* of incoming edges. You can get predecessors of a node
+    // similarly to its successors but offsetting by `edge_count`. `edge_count` is
+    // `edge_targets.len()/2` (again, in case BR is true) because half of the vec is back refs.
+    //
+    // All of this might be confusing, so here is an example graph and its representation:
+    //
+    //       n3 ----+
+    //        ^     |                           (if BR = true)
+    //        |     v     outgoing edges        incoming edges
+    // n0 -> n1 -> n2     ______________      __________________
+    //                   /              \    /                  \
+    //  node indices[1]:  n0, n1, n2, n3,     n0, n1, n2, n3,       n/a
+    //      vec indices:  n0, n1, n2, n3,     n4, n5, n6, n7,       n8
+    //      node_starts:  [0,  1,  3,  4       4,  4,  5,  7,        8]
+    //                     |   |   |   |       |   |   |   |         |
+    //                     |   |   +---+       +---+   |   +---+     |
+    //                     |   |       |           |   |       |     |
+    //                     v   v       v           v   v       v     v
+    //     edge_targets: [n1, n2, n3, n2          n0, n1, n3, n1]
+    //                   /    \____/   |           |  \____/    \
+    //             n0->n1     /        |           |       \     n3<-n1
+    //                       /    n3->n2 [2]  n1<-n0 [2]    \
+    //         n1->n2, n1->n3                                n2<-n1, n2<-n3
+    //
+    // The incoming edges are basically stored in the same way as outgoing edges, but offset and
+    // the graph they store is the inverse of the original. Last index in the `node_starts` array
+    // always points to one-past-the-end, so that we don't need to bound check `node_starts[n + 1]`
+    //
+    // [1]: "node indices" are the indices a user of `VecGraph` might use,
+    //      note that they are different from "vec indices",
+    //      which are the real indices you need to index `node_starts`
+    //
+    // [2]: Note that even though n2 also points to here,
+    //      the next index also points here, so n2 has no
+    //      successors (`edge_targets[3..3] = []`).
+    //      Similarly with n0 and incoming edges
+    //
+    // If this is still confusing... then sorry :(
+    //
+    /// Indices into `edge_targets` that signify a start of list of edges.
     node_starts: IndexVec<N, usize>,
 
+    /// Targets (or sources for back refs) of edges
     edge_targets: Vec<N>,
 }
 
-impl<N: Idx + Ord> VecGraph<N> {
+impl<N: Idx + Ord, const BR: bool> VecGraph<N, BR> {
     pub fn new(num_nodes: usize, mut edge_pairs: Vec<(N, N)>) -> Self {
+        let num_edges = edge_pairs.len();
+
+        let nodes_cap = match BR {
+            // +1 for special entry at the end, pointing one past the end of `edge_targets`
+            false => num_nodes + 1,
+            // *2 for back references
+            true => (num_nodes * 2) + 1,
+        };
+
+        let edges_cap = match BR {
+            false => num_edges,
+            // *2 for back references
+            true => num_edges * 2,
+        };
+
+        let mut node_starts = IndexVec::with_capacity(nodes_cap);
+        let mut edge_targets = Vec::with_capacity(edges_cap);
+
         // Sort the edges by the source -- this is important.
         edge_pairs.sort();
 
-        let num_edges = edge_pairs.len();
+        // Fill forward references
+        create_index(
+            num_nodes,
+            &mut edge_pairs.iter().map(|&(src, _)| src),
+            &mut edge_pairs.iter().map(|&(_, tgt)| tgt),
+            &mut edge_targets,
+            &mut node_starts,
+        );
 
-        // Store the *target* of each edge into `edge_targets`.
-        let edge_targets: Vec<N> = edge_pairs.iter().map(|&(_, target)| target).collect();
+        // Fill back references
+        if BR {
+            // Pop the special "last" entry, it will be replaced by first back ref
+            node_starts.pop();
 
-        // Create the *edge starts* array. We are iterating over the
-        // (sorted) edge pairs. We maintain the invariant that the
-        // length of the `node_starts` array is enough to store the
-        // current source node -- so when we see that the source node
-        // for an edge is greater than the current length, we grow the
-        // edge-starts array by just enough.
-        let mut node_starts = IndexVec::with_capacity(num_edges);
-        for (index, &(source, _)) in edge_pairs.iter().enumerate() {
-            // If we have a list like `[(0, x), (2, y)]`:
-            //
-            // - Start out with `node_starts` of `[]`
-            // - Iterate to `(0, x)` at index 0:
-            //   - Push one entry because `node_starts.len()` (0) is <= the source (0)
-            //   - Leaving us with `node_starts` of `[0]`
-            // - Iterate to `(2, y)` at index 1:
-            //   - Push one entry because `node_starts.len()` (1) is <= the source (2)
-            //   - Push one entry because `node_starts.len()` (2) is <= the source (2)
-            //   - Leaving us with `node_starts` of `[0, 1, 1]`
-            // - Loop terminates
-            while node_starts.len() <= source.index() {
-                node_starts.push(index);
-            }
+            // Re-sort the edges so that they are sorted by target
+            edge_pairs.sort_by_key(|&(src, tgt)| (tgt, src));
+
+            create_index(
+                // Back essentially double the number of nodes
+                num_nodes * 2,
+                // NB: the source/target are switched here too
+                // NB: we double the key index, so that we can later use *2 to get the back references
+                &mut edge_pairs.iter().map(|&(_, tgt)| N::new(tgt.index() + num_nodes)),
+                &mut edge_pairs.iter().map(|&(src, _)| src),
+                &mut edge_targets,
+                &mut node_starts,
+            );
         }
 
-        // Pad out the `node_starts` array so that it has `num_nodes +
-        // 1` entries. Continuing our example above, if `num_nodes` is
-        // be `3`, we would push one more index: `[0, 1, 1, 2]`.
-        //
-        // Interpretation of that vector:
-        //
-        // [0, 1, 1, 2]
-        //        ---- range for N=2
-        //     ---- range for N=1
-        //  ---- range for N=0
-        while node_starts.len() <= num_nodes {
-            node_starts.push(edge_targets.len());
-        }
-
-        assert_eq!(node_starts.len(), num_nodes + 1);
-
         Self { node_starts, edge_targets }
     }
 
     /// Gets the successors for `source` as a slice.
     pub fn successors(&self, source: N) -> &[N] {
+        assert!(source.index() < self.num_nodes());
+
         let start_index = self.node_starts[source];
         let end_index = self.node_starts[source.plus(1)];
         &self.edge_targets[start_index..end_index]
     }
 }
 
-impl<N: Idx> DirectedGraph for VecGraph<N> {
+impl<N: Idx + Ord> VecGraph<N, true> {
+    /// Gets the predecessors for `target` as a slice.
+    pub fn predecessors(&self, target: N) -> &[N] {
+        assert!(target.index() < self.num_nodes());
+
+        let target = N::new(target.index() + self.num_nodes());
+
+        let start_index = self.node_starts[target];
+        let end_index = self.node_starts[target.plus(1)];
+        &self.edge_targets[start_index..end_index]
+    }
+}
+
+/// Creates/initializes the index for the [`VecGraph`]. A helper for [`VecGraph::new`].
+///
+/// - `num_nodes` is the target number of nodes in the graph
+/// - `sorted_edge_sources` are the edge sources, sorted
+/// - `associated_edge_targets` are the edge *targets* in the same order as sources
+/// - `edge_targets` is the vec of targets to be extended
+/// - `node_starts` is the index to be filled
+fn create_index<N: Idx + Ord>(
+    num_nodes: usize,
+    sorted_edge_sources: &mut dyn Iterator<Item = N>,
+    associated_edge_targets: &mut dyn Iterator<Item = N>,
+    edge_targets: &mut Vec<N>,
+    node_starts: &mut IndexVec<N, usize>,
+) {
+    let offset = edge_targets.len();
+
+    // Store the *target* of each edge into `edge_targets`.
+    edge_targets.extend(associated_edge_targets);
+
+    // Create the *edge starts* array. We are iterating over the
+    // (sorted) edge pairs. We maintain the invariant that the
+    // length of the `node_starts` array is enough to store the
+    // current source node -- so when we see that the source node
+    // for an edge is greater than the current length, we grow the
+    // edge-starts array by just enough.
+    for (index, source) in sorted_edge_sources.enumerate() {
+        // If we have a list like `[(0, x), (2, y)]`:
+        //
+        // - Start out with `node_starts` of `[]`
+        // - Iterate to `(0, x)` at index 0:
+        //   - Push one entry because `node_starts.len()` (0) is <= the source (0)
+        //   - Leaving us with `node_starts` of `[0]`
+        // - Iterate to `(2, y)` at index 1:
+        //   - Push one entry because `node_starts.len()` (1) is <= the source (2)
+        //   - Push one entry because `node_starts.len()` (2) is <= the source (2)
+        //   - Leaving us with `node_starts` of `[0, 1, 1]`
+        // - Loop terminates
+        while node_starts.len() <= source.index() {
+            node_starts.push(index + offset);
+        }
+    }
+
+    // Pad out the `node_starts` array so that it has `num_nodes +
+    // 1` entries. Continuing our example above, if `num_nodes` is
+    // be `3`, we would push one more index: `[0, 1, 1, 2]`.
+    //
+    // Interpretation of that vector:
+    //
+    // [0, 1, 1, 2]
+    //        ---- range for N=2
+    //     ---- range for N=1
+    //  ---- range for N=0
+    while node_starts.len() <= num_nodes {
+        node_starts.push(edge_targets.len());
+    }
+
+    assert_eq!(node_starts.len(), num_nodes + 1);
+}
+
+impl<N: Idx, const BR: bool> DirectedGraph for VecGraph<N, BR> {
     type Node = N;
 
     fn num_nodes(&self) -> usize {
-        self.node_starts.len() - 1
+        match BR {
+            false => self.node_starts.len() - 1,
+            // If back refs are enabled, half of the array is said back refs
+            true => (self.node_starts.len() - 1) / 2,
+        }
     }
 }
 
-impl<N: Idx> NumEdges for VecGraph<N> {
+impl<N: Idx, const BR: bool> NumEdges for VecGraph<N, BR> {
     fn num_edges(&self) -> usize {
-        self.edge_targets.len()
+        match BR {
+            false => self.edge_targets.len(),
+            // If back refs are enabled, half of the array is reversed edges for them
+            true => self.edge_targets.len() / 2,
+        }
     }
 }
 
-impl<N: Idx + Ord> Successors for VecGraph<N> {
+impl<N: Idx + Ord, const BR: bool> Successors for VecGraph<N, BR> {
     fn successors(&self, node: N) -> impl Iterator<Item = Self::Node> {
         self.successors(node).iter().cloned()
     }
 }
+
+impl<N: Idx + Ord> Predecessors for VecGraph<N, true> {
+    fn predecessors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
+        self.predecessors(node).iter().cloned()
+    }
+}
diff --git a/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs b/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs
index 87c8d25..a077d9d 100644
--- a/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs
+++ b/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs
@@ -18,10 +18,18 @@
     VecGraph::new(7, vec![(0, 1), (1, 2), (1, 3), (3, 4), (5, 1)])
 }
 
+fn create_graph_with_back_refs() -> VecGraph<usize, true> {
+    // Same as above
+    VecGraph::new(7, vec![(0, 1), (1, 2), (1, 3), (3, 4), (5, 1)])
+}
+
 #[test]
 fn num_nodes() {
     let graph = create_graph();
     assert_eq!(graph.num_nodes(), 7);
+
+    let graph = create_graph_with_back_refs();
+    assert_eq!(graph.num_nodes(), 7);
 }
 
 #[test]
@@ -34,6 +42,27 @@
     assert_eq!(graph.successors(4), &[] as &[usize]);
     assert_eq!(graph.successors(5), &[1]);
     assert_eq!(graph.successors(6), &[] as &[usize]);
+
+    let graph = create_graph_with_back_refs();
+    assert_eq!(graph.successors(0), &[1]);
+    assert_eq!(graph.successors(1), &[2, 3]);
+    assert_eq!(graph.successors(2), &[] as &[usize]);
+    assert_eq!(graph.successors(3), &[4]);
+    assert_eq!(graph.successors(4), &[] as &[usize]);
+    assert_eq!(graph.successors(5), &[1]);
+    assert_eq!(graph.successors(6), &[] as &[usize]);
+}
+
+#[test]
+fn predecessors() {
+    let graph = create_graph_with_back_refs();
+    assert_eq!(graph.predecessors(0), &[]);
+    assert_eq!(graph.predecessors(1), &[0, 5]);
+    assert_eq!(graph.predecessors(2), &[1]);
+    assert_eq!(graph.predecessors(3), &[1]);
+    assert_eq!(graph.predecessors(4), &[3]);
+    assert_eq!(graph.predecessors(5), &[]);
+    assert_eq!(graph.predecessors(6), &[]);
 }
 
 #[test]
@@ -41,4 +70,8 @@
     let graph = create_graph();
     let dfs: Vec<_> = graph::depth_first_search(&graph, 0).collect();
     assert_eq!(dfs, vec![0, 1, 3, 4, 2]);
+
+    let graph = create_graph_with_back_refs();
+    let dfs: Vec<_> = graph::depth_first_search(&graph, 0).collect();
+    assert_eq!(dfs, vec![0, 1, 3, 4, 2]);
 }
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index b82a9a9..2b799d6 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -20,7 +20,6 @@
 #![feature(cfg_match)]
 #![feature(core_intrinsics)]
 #![feature(extend_one)]
-#![feature(generic_nonzero)]
 #![feature(hash_raw_entry)]
 #![feature(hasher_prefixfree_extras)]
 #![feature(lazy_cell)]
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 5d345e7..a17e0da 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -730,7 +730,7 @@
     } }
 
     #[rustc_lint_diagnostics]
-    fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
+    pub fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
         self.sub_with_highlights(Level::Note, msg, MultiSpan::new());
         self
     }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index b4107bd..e3363e8 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -15,7 +15,6 @@
 #![feature(box_patterns)]
 #![feature(error_reporter)]
 #![feature(extract_if)]
-#![feature(generic_nonzero)]
 #![feature(let_chains)]
 #![feature(negative_impls)]
 #![feature(never_type)]
@@ -102,9 +101,9 @@
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
 // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16);
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16);
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index cdcf67b..83f1205 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -202,7 +202,7 @@
         ex: P<ast::Expr>,
     ) -> ast::Stmt {
         let pat = if mutbl {
-            self.pat_ident_binding_mode(sp, ident, ast::BindingAnnotation::MUT)
+            self.pat_ident_binding_mode(sp, ident, ast::BindingMode::MUT)
         } else {
             self.pat_ident(sp, ident)
         };
@@ -490,14 +490,14 @@
         self.pat(span, PatKind::Lit(expr))
     }
     pub fn pat_ident(&self, span: Span, ident: Ident) -> P<ast::Pat> {
-        self.pat_ident_binding_mode(span, ident, ast::BindingAnnotation::NONE)
+        self.pat_ident_binding_mode(span, ident, ast::BindingMode::NONE)
     }
 
     pub fn pat_ident_binding_mode(
         &self,
         span: Span,
         ident: Ident,
-        ann: ast::BindingAnnotation,
+        ann: ast::BindingMode,
     ) -> P<ast::Pat> {
         let pat = PatKind::Ident(ann, ident.with_span_pos(span), None);
         self.pat(span, pat)
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index 9fff00f..ffb50f4 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -266,7 +266,7 @@
 }
 
 // This type is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 rustc_data_structures::static_assert_size!(MatcherPos, 16);
 
 impl MatcherPos {
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index cb28bb4..36ef8fe 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -12,7 +12,6 @@
 //! symbol to the `accepted` or `removed` modules respectively.
 
 #![allow(internal_features)]
-#![feature(generic_nonzero)]
 #![feature(rustdoc_internals)]
 #![doc(rust_logo)]
 #![feature(lazy_cell)]
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index b39056d..e4f8d77 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -7,7 +7,7 @@
 use rustc_ast as ast;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, TraitObjectSyntax, UintTy};
-pub use rustc_ast::{BinOp, BinOpKind, BindingAnnotation, BorrowKind, ByRef, CaptureBy};
+pub use rustc_ast::{BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy};
 pub use rustc_ast::{ImplPolarity, IsAuto, Movability, Mutability, UnOp};
 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_data_structures::fingerprint::Fingerprint;
@@ -1151,7 +1151,7 @@
     /// The `HirId` is the canonical ID for the variable being bound,
     /// (e.g., in `Ok(x) | Err(x)`, both `x` use the same canonical ID),
     /// which is the pattern ID of the first `x`.
-    Binding(BindingAnnotation, HirId, Ident, Option<&'hir Pat<'hir>>),
+    Binding(BindingMode, HirId, Ident, Option<&'hir Pat<'hir>>),
 
     /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
     /// The `bool` is `true` in the presence of a `..`.
@@ -3786,7 +3786,7 @@
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     // tidy-alphabetical-start
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 2a796ca..879476f 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -162,6 +162,18 @@
     Drop,                    sym::drop,                drop_trait,                 Target::Trait,          GenericRequirement::None;
     Destruct,                sym::destruct,            destruct_trait,             Target::Trait,          GenericRequirement::None;
 
+    AsyncDrop,               sym::async_drop,          async_drop_trait,           Target::Trait,          GenericRequirement::Exact(0);
+    AsyncDestruct,           sym::async_destruct,      async_destruct_trait,       Target::Trait,          GenericRequirement::Exact(0);
+    AsyncDropInPlace,        sym::async_drop_in_place, async_drop_in_place_fn,     Target::Fn,             GenericRequirement::Exact(1);
+    SurfaceAsyncDropInPlace, sym::surface_async_drop_in_place, surface_async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
+    AsyncDropSurfaceDropInPlace, sym::async_drop_surface_drop_in_place, async_drop_surface_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
+    AsyncDropSlice,          sym::async_drop_slice,    async_drop_slice_fn,        Target::Fn,             GenericRequirement::Exact(1);
+    AsyncDropChain,          sym::async_drop_chain,    async_drop_chain_fn,        Target::Fn,             GenericRequirement::Exact(2);
+    AsyncDropNoop,           sym::async_drop_noop,     async_drop_noop_fn,         Target::Fn,             GenericRequirement::Exact(0);
+    AsyncDropFuse,           sym::async_drop_fuse,     async_drop_fuse_fn,         Target::Fn,             GenericRequirement::Exact(1);
+    AsyncDropDefer,          sym::async_drop_defer,    async_drop_defer_fn,        Target::Fn,             GenericRequirement::Exact(1);
+    AsyncDropEither,         sym::async_drop_either,   async_drop_either_fn,       Target::Fn,             GenericRequirement::Exact(3);
+
     CoerceUnsized,           sym::coerce_unsized,      coerce_unsized_trait,       Target::Trait,          GenericRequirement::Minimum(1);
     DispatchFromDyn,         sym::dispatch_from_dyn,   dispatch_from_dyn_trait,    Target::Trait,          GenericRequirement::Minimum(1);
 
@@ -281,6 +293,7 @@
 
     ExchangeMalloc,          sym::exchange_malloc,     exchange_malloc_fn,         Target::Fn,             GenericRequirement::None;
     DropInPlace,             sym::drop_in_place,       drop_in_place_fn,           Target::Fn,             GenericRequirement::Minimum(1);
+    FallbackSurfaceDrop,     sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn,             GenericRequirement::None;
     AllocLayout,             sym::alloc_layout,        alloc_layout,               Target::Struct,         GenericRequirement::None;
 
     Start,                   sym::start,               start_fn,                   Target::Fn,             GenericRequirement::Exact(1);
diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs
index 1eaab3d..9991b02 100644
--- a/compiler/rustc_hir/src/pat_util.rs
+++ b/compiler/rustc_hir/src/pat_util.rs
@@ -1,6 +1,6 @@
 use crate::def::{CtorOf, DefKind, Res};
 use crate::def_id::{DefId, DefIdSet};
-use crate::hir::{self, BindingAnnotation, ByRef, HirId, PatKind};
+use crate::hir::{self, BindingMode, ByRef, HirId, PatKind};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
@@ -60,7 +60,7 @@
 impl hir::Pat<'_> {
     /// Call `f` on every "binding" in a pattern, e.g., on `a` in
     /// `match foo() { Some(a) => (), None => () }`
-    pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) {
+    pub fn each_binding(&self, mut f: impl FnMut(hir::BindingMode, HirId, Span, Ident)) {
         self.walk_always(|p| {
             if let PatKind::Binding(binding_mode, _, ident, _) = p.kind {
                 f(binding_mode, p.hir_id, p.span, ident);
@@ -74,10 +74,7 @@
     /// When encountering an or-pattern `p_0 | ... | p_n` only the first non-never pattern will be
     /// visited. If they're all never patterns we visit nothing, which is ok since a never pattern
     /// cannot have bindings.
-    pub fn each_binding_or_first(
-        &self,
-        f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident),
-    ) {
+    pub fn each_binding_or_first(&self, f: &mut impl FnMut(hir::BindingMode, HirId, Span, Ident)) {
         self.walk(|p| match &p.kind {
             PatKind::Or(ps) => {
                 for p in *ps {
@@ -98,7 +95,7 @@
 
     pub fn simple_ident(&self) -> Option<Ident> {
         match self.kind {
-            PatKind::Binding(BindingAnnotation(ByRef::No, _), _, ident, None) => Some(ident),
+            PatKind::Binding(BindingMode(ByRef::No, _), _, ident, None) => Some(ident),
             _ => None,
         }
     }
@@ -135,8 +132,8 @@
     pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
         let mut result = None;
         self.each_binding(|annotation, _, _, _| match annotation {
-            hir::BindingAnnotation::REF if result.is_none() => result = Some(hir::Mutability::Not),
-            hir::BindingAnnotation::REF_MUT => result = Some(hir::Mutability::Mut),
+            hir::BindingMode::REF if result.is_none() => result = Some(hir::Mutability::Not),
+            hir::BindingMode::REF_MUT => result = Some(hir::Mutability::Mut),
             _ => {}
         });
         result
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 0ff78eb..06b5ee2 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -351,7 +351,7 @@
         *[normal] the {$param_def_kind} `{$param_name}` is defined here
     }
 
-hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope
+hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope in `use<...>`
     .label = {$kind} parameter is implicitly captured by this `impl Trait`
     .note = currently, all {$kind} parameters are required to be mentioned in the precise captures list
 
@@ -405,6 +405,10 @@
     `Self` is not valid in the self type of an impl block
     .note = replace `Self` with a different type
 
+hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>`
+    .label = `Self` type parameter is implicitly captured by this `impl Trait`
+    .note = currently, all type parameters are required to be mentioned in the precise captures list
+
 hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code
     .help = add `#![feature(simd_ffi)]` to the crate attributes to enable
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 3881e24..a43a67d 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -492,6 +492,7 @@
     };
 
     let mut expected_captures = UnordSet::default();
+    let mut shadowed_captures = UnordSet::default();
     let mut seen_params = UnordMap::default();
     let mut prev_non_lifetime_param = None;
     for arg in precise_capturing_args {
@@ -530,6 +531,21 @@
         match tcx.named_bound_var(hir_id) {
             Some(ResolvedArg::EarlyBound(def_id)) => {
                 expected_captures.insert(def_id);
+
+                // Make sure we allow capturing these lifetimes through `Self` and
+                // `T::Assoc` projection syntax, too. These will occur when we only
+                // see lifetimes are captured after hir-lowering -- this aligns with
+                // the cases that were stabilized with the `impl_trait_projection`
+                // feature -- see <https://github.com/rust-lang/rust/pull/115659>.
+                if let DefKind::LifetimeParam = tcx.def_kind(def_id)
+                    && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
+                    | ty::ReLateParam(ty::LateParamRegion {
+                        bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
+                        ..
+                    }) = *tcx.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local())
+                {
+                    shadowed_captures.insert(def_id);
+                }
             }
             _ => {
                 tcx.dcx().span_delayed_bug(
@@ -555,39 +571,61 @@
                 );
                 continue;
             }
+            // If a param is shadowed by a early-bound (duplicated) lifetime, then
+            // it may or may not be captured as invariant, depending on if it shows
+            // up through `Self` or `T::Assoc` syntax.
+            if shadowed_captures.contains(&param.def_id) {
+                continue;
+            }
 
             match param.kind {
                 ty::GenericParamDefKind::Lifetime => {
+                    let use_span = tcx.def_span(param.def_id);
+                    let opaque_span = tcx.def_span(opaque_def_id);
                     // Check if the lifetime param was captured but isn't named in the precise captures list.
                     if variances[param.index as usize] == ty::Invariant {
-                        let param_span =
-                            if let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
+                        if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id))
+                            && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
                             | ty::ReLateParam(ty::LateParamRegion {
                                 bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
                                 ..
                             }) = *tcx
                                 .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
-                            {
-                                Some(tcx.def_span(def_id))
-                            } else {
-                                None
-                            };
-                        // FIXME(precise_capturing): Structured suggestion for this would be useful
-                        tcx.dcx().emit_err(errors::LifetimeNotCaptured {
-                            use_span: tcx.def_span(param.def_id),
-                            param_span,
-                            opaque_span: tcx.def_span(opaque_def_id),
-                        });
+                        {
+                            tcx.dcx().emit_err(errors::LifetimeNotCaptured {
+                                opaque_span,
+                                use_span,
+                                param_span: tcx.def_span(def_id),
+                            });
+                        } else {
+                            // If the `use_span` is actually just the param itself, then we must
+                            // have not duplicated the lifetime but captured the original.
+                            // The "effective" `use_span` will be the span of the opaque itself,
+                            // and the param span will be the def span of the param.
+                            tcx.dcx().emit_err(errors::LifetimeNotCaptured {
+                                opaque_span,
+                                use_span: opaque_span,
+                                param_span: use_span,
+                            });
+                        }
                         continue;
                     }
                 }
                 ty::GenericParamDefKind::Type { .. } => {
-                    // FIXME(precise_capturing): Structured suggestion for this would be useful
-                    tcx.dcx().emit_err(errors::ParamNotCaptured {
-                        param_span: tcx.def_span(param.def_id),
-                        opaque_span: tcx.def_span(opaque_def_id),
-                        kind: "type",
-                    });
+                    if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) {
+                        // FIXME(precise_capturing): Structured suggestion for this would be useful
+                        tcx.dcx().emit_err(errors::SelfTyNotCaptured {
+                            trait_span: tcx.def_span(param.def_id),
+                            opaque_span: tcx.def_span(opaque_def_id),
+                        });
+                    } else {
+                        // FIXME(precise_capturing): Structured suggestion for this would be useful
+                        tcx.dcx().emit_err(errors::ParamNotCaptured {
+                            param_span: tcx.def_span(param.def_id),
+                            opaque_span: tcx.def_span(opaque_def_id),
+                            kind: "type",
+                        });
+                    }
                 }
                 ty::GenericParamDefKind::Const { .. } => {
                     // FIXME(precise_capturing): Structured suggestion for this would be useful
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index bd64621..fb4a76b 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -128,6 +128,7 @@
         | sym::variant_count
         | sym::is_val_statically_known
         | sym::ptr_mask
+        | sym::aggregate_raw_ptr
         | sym::ub_checks
         | sym::fadd_algebraic
         | sym::fsub_algebraic
@@ -574,6 +575,10 @@
                 (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
             }
 
+            // This type check is not particularly useful, but the `where` bounds
+            // on the definition in `core` do the heavy lifting for checking it.
+            sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)),
+
             sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool),
 
             sym::simd_eq
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 3978934..d2ea51f 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -654,7 +654,7 @@
         // & expression, and its lifetime would be extended to the end of the block (due
         // to a different rule, not the below code).
         match pat.kind {
-            PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes(_), _), ..) => true,
+            PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(_), _), ..) => true,
 
             PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)),
 
@@ -671,7 +671,7 @@
             PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat),
 
             PatKind::Ref(_, _)
-            | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..)
+            | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..)
             | PatKind::Wild
             | PatKind::Never
             | PatKind::Path(_)
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 4d6a02f..f83ddc5 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -195,16 +195,19 @@
                 }
                 Some(fn_def_id.to_def_id())
             }
-            ItemKind::OpaqueTy(hir::OpaqueTy {
-                origin: hir::OpaqueTyOrigin::TyAlias { .. },
+            ItemKind::OpaqueTy(&hir::OpaqueTy {
+                origin: hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty },
                 ..
             }) => {
-                let parent_id = tcx.hir().get_parent_item(hir_id);
-                assert_ne!(parent_id, hir::CRATE_OWNER_ID);
-                debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id);
+                if in_assoc_ty {
+                    assert!(matches!(tcx.def_kind(parent), DefKind::AssocTy));
+                } else {
+                    assert!(matches!(tcx.def_kind(parent), DefKind::TyAlias));
+                }
+                debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent);
                 // Opaque types are always nested within another item, and
                 // inherit the generics of the item.
-                Some(parent_id.to_def_id())
+                Some(parent.to_def_id())
             }
             _ => None,
         },
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index c220581..02291cc 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -4,7 +4,7 @@
 use rustc_hir as hir;
 use rustc_infer::traits::util;
 use rustc_middle::ty::GenericArgs;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::Span;
 
@@ -214,7 +214,7 @@
         {
             self.tcx.type_of(projection_ty.def_id).instantiate(self.tcx, projection_ty.args)
         } else {
-            ty
+            ty.super_fold_with(self)
         }
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
index 520bf1d..8a9b5fe 100644
--- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
+++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
@@ -6,19 +6,29 @@
 #[note]
 pub struct ParamNotCaptured {
     #[primary_span]
-    pub param_span: Span,
-    #[label]
     pub opaque_span: Span,
+    #[label]
+    pub param_span: Span,
     pub kind: &'static str,
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_analysis_self_ty_not_captured)]
+#[note]
+pub struct SelfTyNotCaptured {
+    #[primary_span]
+    pub opaque_span: Span,
+    #[label]
+    pub trait_span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_analysis_lifetime_not_captured)]
 pub struct LifetimeNotCaptured {
     #[primary_span]
     pub use_span: Span,
     #[label(hir_analysis_param_label)]
-    pub param_span: Option<Span>,
+    pub param_span: Span,
     #[label]
     pub opaque_span: Span,
 }
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index d340a08..dd76862 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -228,8 +228,8 @@
         // Check whether this segment takes generic arguments and the user has provided any.
         let (generic_args, infer_args) = ctx.args_for_def_id(def_id);
 
-        let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter());
-        let mut args_iter = args_iter.clone().peekable();
+        let mut args_iter =
+            generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable();
 
         // If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
         // If we later encounter a lifetime, we know that the arguments were provided in the
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index c374f97..66b86c9 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -63,7 +63,6 @@
 #![feature(rustdoc_internals)]
 #![allow(internal_features)]
 #![feature(control_flow_enum)]
-#![feature(generic_nonzero)]
 #![feature(if_let_guard)]
 #![feature(is_sorted)]
 #![feature(iter_intersperse)]
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 0f4d8df..285b99c 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -10,7 +10,7 @@
 use rustc_ast_pretty::pprust::{Comments, PrintState};
 use rustc_hir as hir;
 use rustc_hir::{
-    BindingAnnotation, ByRef, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId,
+    BindingMode, ByRef, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId,
     LifetimeParamKind, Node, PatKind, RangeEnd, Term, TraitBoundModifier,
 };
 use rustc_span::source_map::SourceMap;
@@ -1723,7 +1723,7 @@
         match pat.kind {
             PatKind::Wild => self.word("_"),
             PatKind::Never => self.word("!"),
-            PatKind::Binding(BindingAnnotation(by_ref, mutbl), _, ident, sub) => {
+            PatKind::Binding(BindingMode(by_ref, mutbl), _, ident, sub) => {
                 if mutbl.is_mut() {
                     self.word_nbsp("mut");
                 }
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index a64d3b9..dfd0b7c 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -38,8 +38,11 @@
     receiver: Option<Span>,
     expr_span: Span,
     trait_id: DefId,
+    body_id: DefId,
 ) -> Result<(), ErrorGuaranteed> {
-    if tcx.lang_items().drop_trait() == Some(trait_id) {
+    if tcx.lang_items().drop_trait() == Some(trait_id)
+        && tcx.lang_items().fallback_surface_drop_fn() != Some(body_id)
+    {
         let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
             errors::ExplicitDestructorCallSugg::Snippet {
                 lo: expr_span.shrink_to_lo(),
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 8923137..48b9142 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -2697,7 +2697,7 @@
 
     fn point_at_param_definition(&self, err: &mut Diag<'_>, param: ty::ParamTy) {
         let generics = self.tcx.generics_of(self.body_id);
-        let generic_param = generics.type_param(&param, self.tcx);
+        let generic_param = generics.type_param(param, self.tcx);
         if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind {
             return;
         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 786754e..0b684fd 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -279,13 +279,23 @@
             }
             Entry::Occupied(mut entry) => {
                 debug!(" - composing on top of {:?}", entry.get());
-                match (&entry.get()[..], &adj[..]) {
-                    // Applying any adjustment on top of a NeverToAny
-                    // is a valid NeverToAny adjustment, because it can't
-                    // be reached.
-                    (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
+                match (&mut entry.get_mut()[..], &adj[..]) {
                     (
-                        &[
+                        [Adjustment { kind: Adjust::NeverToAny, target }],
+                        &[.., Adjustment { target: new_target, .. }],
+                    ) => {
+                        // NeverToAny coercion can target any type, so instead of adding a new
+                        // adjustment on top we can change the target.
+                        //
+                        // This is required for things like `a == a` (where `a: !`) to produce
+                        // valid MIR -- we need borrow adjustment from things like `==` to change
+                        // the type to `&!` (or `&()` depending on the fallback). This might be
+                        // relevant even in unreachable code.
+                        *target = new_target;
+                    }
+
+                    (
+                        &mut [
                             Adjustment { kind: Adjust::Deref(_), .. },
                             Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
                         ],
@@ -294,11 +304,13 @@
                             .., // Any following adjustments are allowed.
                         ],
                     ) => {
-                        // A reborrow has no effect before a dereference.
+                        // A reborrow has no effect before a dereference, so we can safely replace adjustments.
+                        *entry.get_mut() = adj;
                     }
-                    // FIXME: currently we never try to compose autoderefs
-                    // and ReifyFnPointer/UnsafeFnPointer, but we could.
+
                     _ => {
+                        // FIXME: currently we never try to compose autoderefs
+                        // and ReifyFnPointer/UnsafeFnPointer, but we could.
                         self.dcx().span_delayed_bug(
                             expr.span,
                             format!(
@@ -308,9 +320,10 @@
                                 adj
                             ),
                         );
+
+                        *entry.get_mut() = adj;
                     }
                 }
-                *entry.get_mut() = adj;
             }
         }
 
@@ -1118,6 +1131,7 @@
                             None,
                             span,
                             container_id,
+                            self.body_id.to_def_id(),
                         ) {
                             self.set_tainted_by_errors(e);
                         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index d9e5289..b729fbe 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -2144,7 +2144,7 @@
             let callee_ty = callee_ty.peel_refs();
             match *callee_ty.kind() {
                 ty::Param(param) => {
-                    let param = self.tcx.generics_of(self.body_id).type_param(&param, self.tcx);
+                    let param = self.tcx.generics_of(self.body_id).type_param(param, self.tcx);
                     if param.kind.is_synthetic() {
                         // if it's `impl Fn() -> ..` then just fall down to the def-id based logic
                         def_id = param.def_id;
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 0275906..d570152 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -628,6 +628,7 @@
                 Some(self.self_expr.span),
                 self.call_expr.span,
                 trait_def_id,
+                self.body_id.to_def_id(),
             ) {
                 self.set_tainted_by_errors(e);
             }
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 46227e4..e74c4d0 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -125,11 +125,11 @@
                 let Some(arg_ty) = args[0].as_type() else {
                     return false;
                 };
-                let ty::Param(param) = arg_ty.kind() else {
+                let ty::Param(param) = *arg_ty.kind() else {
                     return false;
                 };
                 // Is `generic_param` the same as the arg for this trait predicate?
-                generic_param.index == generics.type_param(&param, tcx).index
+                generic_param.index == generics.type_param(param, tcx).index
             } else {
                 false
             }
@@ -156,10 +156,10 @@
             return false;
         }
 
-        match ty.peel_refs().kind() {
+        match *ty.peel_refs().kind() {
             ty::Param(param) => {
                 let generics = self.tcx.generics_of(self.body_id);
-                let generic_param = generics.type_param(&param, self.tcx);
+                let generic_param = generics.type_param(param, self.tcx);
                 for unsatisfied in unsatisfied_predicates.iter() {
                     // The parameter implements `IntoIterator`
                     // but it has called a method that requires it to implement `Iterator`
@@ -3232,9 +3232,9 @@
                 .sort_by_key(|&info| (!info.def_id.is_local(), self.tcx.def_path_str(info.def_id)));
             candidates.dedup();
 
-            let param_type = match rcvr_ty.kind() {
+            let param_type = match *rcvr_ty.kind() {
                 ty::Param(param) => Some(param),
-                ty::Ref(_, ty, _) => match ty.kind() {
+                ty::Ref(_, ty, _) => match *ty.kind() {
                     ty::Param(param) => Some(param),
                     _ => None,
                 },
@@ -3429,7 +3429,7 @@
                                 } else {
                                     param_type.map_or_else(
                                         || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
-                                        ToString::to_string,
+                                        |p| p.to_string(),
                                     )
                                 },
                             },
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 3c1e01c..6040b68 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -7,7 +7,7 @@
 };
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
-use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId, Mutability, Pat, PatKind};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Pat, PatKind};
 use rustc_infer::infer;
 use rustc_infer::infer::type_variable::TypeVariableOrigin;
 use rustc_lint as lint;
@@ -79,7 +79,7 @@
 
 #[derive(Copy, Clone)]
 struct PatInfo<'tcx, 'a> {
-    binding_mode: BindingAnnotation,
+    binding_mode: ByRef,
     max_ref_mutbl: Mutability,
     top_info: TopInfo<'tcx>,
     decl_origin: Option<DeclOrigin<'a>>,
@@ -125,8 +125,6 @@
     }
 }
 
-const INITIAL_BM: BindingAnnotation = BindingAnnotation(ByRef::No, Mutability::Not);
-
 /// Mode for adjusting the expected type and binding mode.
 enum AdjustMode {
     /// Peel off all immediate reference types.
@@ -163,7 +161,7 @@
     ) {
         let info = TopInfo { expected, origin_expr, span };
         let pat_info = PatInfo {
-            binding_mode: INITIAL_BM,
+            binding_mode: ByRef::No,
             max_ref_mutbl: Mutability::Mut,
             top_info: info,
             decl_origin,
@@ -296,43 +294,43 @@
         &self,
         pat: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
-        def_bm: BindingAnnotation,
+        def_br: ByRef,
         adjust_mode: AdjustMode,
         max_ref_mutbl: Mutability,
-    ) -> (Ty<'tcx>, BindingAnnotation, Mutability, bool) {
-        if let ByRef::Yes(mutbl) = def_bm.0 {
+    ) -> (Ty<'tcx>, ByRef, Mutability, bool) {
+        if let ByRef::Yes(mutbl) = def_br {
             debug_assert!(mutbl <= max_ref_mutbl);
         }
         match adjust_mode {
-            AdjustMode::Pass => (expected, def_bm, max_ref_mutbl, false),
-            AdjustMode::Reset => (expected, INITIAL_BM, Mutability::Mut, false),
+            AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false),
+            AdjustMode::Reset => (expected, ByRef::No, Mutability::Mut, false),
             AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => {
-                let mutbls_match = def_bm.0 == ByRef::Yes(ref_pat_mutbl);
+                let mutbls_match = def_br == ByRef::Yes(ref_pat_mutbl);
                 if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
                     if mutbls_match {
                         debug!("consuming inherited reference");
-                        (expected, INITIAL_BM, cmp::min(max_ref_mutbl, ref_pat_mutbl), true)
+                        (expected, ByRef::No, cmp::min(max_ref_mutbl, ref_pat_mutbl), true)
                     } else {
                         let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut {
                             self.peel_off_references(
                                 pat,
                                 expected,
-                                def_bm,
+                                def_br,
                                 Mutability::Not,
                                 max_ref_mutbl,
                             )
                         } else {
-                            (expected, def_bm.cap_ref_mutability(Mutability::Not), Mutability::Not)
+                            (expected, def_br.cap_ref_mutability(Mutability::Not), Mutability::Not)
                         };
                         (new_ty, new_bm, max_ref_mutbl, false)
                     }
                 } else {
-                    (expected, INITIAL_BM, max_ref_mutbl, mutbls_match)
+                    (expected, ByRef::No, max_ref_mutbl, mutbls_match)
                 }
             }
             AdjustMode::Peel => {
                 let peeled =
-                    self.peel_off_references(pat, expected, def_bm, Mutability::Mut, max_ref_mutbl);
+                    self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl);
                 (peeled.0, peeled.1, peeled.2, false)
             }
         }
@@ -413,10 +411,10 @@
         &self,
         pat: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
-        mut def_bm: BindingAnnotation,
+        mut def_br: ByRef,
         max_peelable_mutability: Mutability,
         mut max_ref_mutability: Mutability,
-    ) -> (Ty<'tcx>, BindingAnnotation, Mutability) {
+    ) -> (Ty<'tcx>, ByRef, Mutability) {
         let mut expected = self.try_structurally_resolve_type(pat.span, expected);
         // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
         // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
@@ -437,7 +435,7 @@
             pat_adjustments.push(expected);
 
             expected = self.try_structurally_resolve_type(pat.span, inner_ty);
-            def_bm.0 = ByRef::Yes(match def_bm.0 {
+            def_br = ByRef::Yes(match def_br {
                 // If default binding mode is by value, make it `ref` or `ref mut`
                 // (depending on whether we observe `&` or `&mut`).
                 ByRef::No |
@@ -450,21 +448,21 @@
         }
 
         if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
-            def_bm = def_bm.cap_ref_mutability(max_ref_mutability);
-            if def_bm.0 == ByRef::Yes(Mutability::Not) {
+            def_br = def_br.cap_ref_mutability(max_ref_mutability);
+            if def_br == ByRef::Yes(Mutability::Not) {
                 max_ref_mutability = Mutability::Not;
             }
         }
 
         if !pat_adjustments.is_empty() {
-            debug!("default binding mode is now {:?}", def_bm);
+            debug!("default binding mode is now {:?}", def_br);
             self.typeck_results
                 .borrow_mut()
                 .pat_adjustments_mut()
                 .insert(pat.hir_id, pat_adjustments);
         }
 
-        (expected, def_bm, max_ref_mutability)
+        (expected, def_br, max_ref_mutability)
     }
 
     fn check_pat_lit(
@@ -669,17 +667,17 @@
     fn check_pat_ident(
         &self,
         pat: &'tcx Pat<'tcx>,
-        ba: BindingAnnotation,
+        ba: BindingMode,
         var_id: HirId,
         sub: Option<&'tcx Pat<'tcx>>,
         expected: Ty<'tcx>,
         pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
-        let PatInfo { binding_mode: BindingAnnotation(def_br, _), top_info: ti, .. } = pat_info;
+        let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info;
 
         // Determine the binding mode...
         let bm = match ba {
-            BindingAnnotation(ByRef::No, Mutability::Mut)
+            BindingMode(ByRef::No, Mutability::Mut)
                 if !(pat.span.at_least_rust_2024()
                     && self.tcx.features().mut_preserve_binding_mode_2024)
                     && matches!(def_br, ByRef::Yes(_)) =>
@@ -691,10 +689,10 @@
                     pat.span,
                     errors::DereferencingMutBinding { span: pat.span },
                 );
-                BindingAnnotation(ByRef::No, Mutability::Mut)
+                BindingMode(ByRef::No, Mutability::Mut)
             }
-            BindingAnnotation(ByRef::No, mutbl) => BindingAnnotation(def_br, mutbl),
-            BindingAnnotation(ByRef::Yes(_), _) => ba,
+            BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
+            BindingMode(ByRef::Yes(_), _) => ba,
         };
         // ...and store it in a side table:
         self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm);
@@ -736,7 +734,7 @@
     /// bindings have the same type by comparing them all against the type of that first pat.
     fn check_binding_alt_eq_ty(
         &self,
-        ba: BindingAnnotation,
+        ba: BindingMode,
         span: Span,
         var_id: HirId,
         ty: Ty<'tcx>,
@@ -776,10 +774,10 @@
         span: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-        ba: BindingAnnotation,
+        ba: BindingMode,
     ) {
         match (expected.kind(), actual.kind(), ba) {
-            (ty::Ref(_, inner_ty, _), _, BindingAnnotation::NONE)
+            (ty::Ref(_, inner_ty, _), _, BindingMode::NONE)
                 if self.can_eq(self.param_env, *inner_ty, actual) =>
             {
                 err.span_suggestion_verbose(
@@ -789,7 +787,7 @@
                     Applicability::MaybeIncorrect,
                 );
             }
-            (_, ty::Ref(_, inner_ty, _), BindingAnnotation::REF)
+            (_, ty::Ref(_, inner_ty, _), BindingMode::REF)
                 if self.can_eq(self.param_env, expected, *inner_ty) =>
             {
                 err.span_suggestion_verbose(
@@ -881,7 +879,7 @@
                         if let PatKind::Ref(the_ref, _) = i.kind
                             && let PatKind::Binding(mt, _, ident, _) = the_ref.kind
                         {
-                            let BindingAnnotation(_, mtblty) = mt;
+                            let BindingMode(_, mtblty) = mt;
                             err.span_suggestion_verbose(
                                 i.span,
                                 format!("consider removing `&{mutability}` from the pattern"),
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 60a5838..2bf4f51 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -229,8 +229,7 @@
                 else {
                     bug!();
                 };
-                let hir::PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), _, _, _) =
-                    pat.kind
+                let hir::PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), _, _, _) = pat.kind
                 else {
                     // Complex pattern, skip the non-upvar local.
                     continue;
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 1462037..bf6ee04 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -162,8 +162,11 @@
 fn lock_file_path(session_dir: &Path) -> PathBuf {
     let crate_dir = session_dir.parent().unwrap();
 
-    let directory_name = session_dir.file_name().unwrap().to_string_lossy();
-    assert_no_characters_lost(&directory_name);
+    let directory_name = session_dir
+        .file_name()
+        .unwrap()
+        .to_str()
+        .expect("malformed session dir name: contains non-Unicode characters");
 
     let dash_indices: Vec<_> = directory_name.match_indices('-').map(|(idx, _)| idx).collect();
     if dash_indices.len() != 3 {
@@ -329,8 +332,11 @@
 
     debug!("finalize_session_directory() - session directory: {}", incr_comp_session_dir.display());
 
-    let old_sub_dir_name = incr_comp_session_dir.file_name().unwrap().to_string_lossy();
-    assert_no_characters_lost(&old_sub_dir_name);
+    let old_sub_dir_name = incr_comp_session_dir
+        .file_name()
+        .unwrap()
+        .to_str()
+        .expect("malformed session dir name: contains non-Unicode characters");
 
     // Keep the 's-{timestamp}-{random-number}' prefix, but replace the
     // '-working' part with the SVH of the crate
@@ -527,8 +533,10 @@
     for session_dir in iter {
         debug!("find_source_directory_in_iter - inspecting `{}`", session_dir.display());
 
-        let directory_name = session_dir.file_name().unwrap().to_string_lossy();
-        assert_no_characters_lost(&directory_name);
+        let Some(directory_name) = session_dir.file_name().unwrap().to_str() else {
+            debug!("find_source_directory_in_iter - ignoring");
+            continue;
+        };
 
         if source_directories_already_tried.contains(&session_dir)
             || !is_session_directory(&directory_name)
@@ -619,12 +627,6 @@
     incr_dir.join(crate_name)
 }
 
-fn assert_no_characters_lost(s: &str) {
-    if s.contains('\u{FFFD}') {
-        bug!("Could not losslessly convert '{}'.", s)
-    }
-}
-
 fn is_old_enough_to_be_collected(timestamp: SystemTime) -> bool {
     timestamp < SystemTime::now() - Duration::from_secs(10)
 }
@@ -657,14 +659,14 @@
         };
 
         let entry_name = dir_entry.file_name();
-        let entry_name = entry_name.to_string_lossy();
+        let Some(entry_name) = entry_name.to_str() else {
+            continue;
+        };
 
         if is_session_directory_lock_file(&entry_name) {
-            assert_no_characters_lost(&entry_name);
-            lock_files.insert(entry_name.into_owned());
+            lock_files.insert(entry_name.to_string());
         } else if is_session_directory(&entry_name) {
-            assert_no_characters_lost(&entry_name);
-            session_directories.insert(entry_name.into_owned());
+            session_directories.insert(entry_name.to_string());
         } else {
             // This is something we don't know, leave it alone
         }
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index c7e5630..e37af66 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -400,7 +400,7 @@
 }
 
 // This type is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 crate::static_assert_size!(Chunk, 16);
 
 impl<T> ChunkedBitSet<T> {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 29c9f08..feef64b 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -61,7 +61,7 @@
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{
     codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString,
-    ErrorGuaranteed, IntoDiagArg,
+    ErrorGuaranteed, IntoDiagArg, StringPart,
 };
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -1917,6 +1917,23 @@
                     );
                     if !is_simple_error || terr.must_include_note() {
                         diag.note_expected_found(&expected_label, expected, &found_label, found);
+
+                        if let Some(ty::Closure(_, args)) =
+                            exp_found.map(|expected_type_found| expected_type_found.found.kind())
+                        {
+                            diag.highlighted_note(vec![
+                                StringPart::normal("closure has signature: `"),
+                                StringPart::highlighted(
+                                    self.tcx
+                                        .signature_unclosure(
+                                            args.as_closure().sig(),
+                                            rustc_hir::Unsafety::Normal,
+                                        )
+                                        .to_string(),
+                                ),
+                                StringPart::normal("`"),
+                            ]);
+                        }
                     }
                 }
             }
@@ -2354,7 +2371,7 @@
 
             // type_param_sugg_span is (span, has_bounds)
             let (type_scope, type_param_sugg_span) = match bound_kind {
-                GenericKind::Param(ref param) => {
+                GenericKind::Param(param) => {
                     let generics = self.tcx.generics_of(generic_param_scope);
                     let def_id = generics.type_param(param, self.tcx).def_id.expect_local();
                     let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index 008b75b..6a42f9b 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -28,7 +28,7 @@
 
         match err {
             ArgumentSorts(values, _) | Sorts(values) => {
-                match (values.expected.kind(), values.found.kind()) {
+                match (*values.expected.kind(), *values.found.kind()) {
                     (ty::Closure(..), ty::Closure(..)) => {
                         diag.note("no two closures, even if identical, have the same type");
                         diag.help("consider boxing your closure and/or using it as a trait object");
@@ -452,7 +452,7 @@
                     }
                     (ty::FnPtr(sig), ty::FnDef(def_id, _))
                     | (ty::FnDef(def_id, _), ty::FnPtr(sig)) => {
-                        if tcx.fn_sig(*def_id).skip_binder().unsafety() < sig.unsafety() {
+                        if tcx.fn_sig(def_id).skip_binder().unsafety() < sig.unsafety() {
                             diag.note(
                                 "unsafe functions cannot be coerced into safe function pointers",
                             );
@@ -527,7 +527,7 @@
         diag: &mut Diag<'_>,
         msg: impl Fn() -> String,
         body_owner_def_id: DefId,
-        proj_ty: &ty::AliasTy<'tcx>,
+        proj_ty: ty::AliasTy<'tcx>,
         ty: Ty<'tcx>,
     ) -> bool {
         let tcx = self.tcx;
@@ -541,7 +541,7 @@
         };
         // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
         // This will also work for `impl Trait`.
-        let ty::Param(param_ty) = proj_ty.self_ty().kind() else {
+        let ty::Param(param_ty) = *proj_ty.self_ty().kind() else {
             return false;
         };
         let generics = tcx.generics_of(body_owner_def_id);
@@ -598,7 +598,7 @@
     fn expected_projection(
         &self,
         diag: &mut Diag<'_>,
-        proj_ty: &ty::AliasTy<'tcx>,
+        proj_ty: ty::AliasTy<'tcx>,
         values: ExpectedFound<Ty<'tcx>>,
         body_owner_def_id: DefId,
         cause_code: &ObligationCauseCode<'_>,
@@ -709,7 +709,7 @@
         &self,
         diag: &mut Diag<'_>,
         msg: impl Fn() -> String,
-        proj_ty: &ty::AliasTy<'tcx>,
+        proj_ty: ty::AliasTy<'tcx>,
         ty: Ty<'tcx>,
     ) -> bool {
         let tcx = self.tcx;
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 255e688..81130d6 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -477,7 +477,7 @@
 }
 
 // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(SubregionOrigin<'_>, 32);
 
 impl<'tcx> SubregionOrigin<'tcx> {
@@ -1521,15 +1521,6 @@
         closure_kind_ty.to_opt_closure_kind()
     }
 
-    /// Clears the selection, evaluation, and projection caches. This is useful when
-    /// repeatedly attempting to select an `Obligation` while changing only
-    /// its `ParamEnv`, since `FulfillmentContext` doesn't use probing.
-    pub fn clear_caches(&self) {
-        self.selection_cache.clear();
-        self.evaluation_cache.clear();
-        self.inner.borrow_mut().projection_cache().clear();
-    }
-
     pub fn universe(&self) -> ty::UniverseIndex {
         self.universe.get()
     }
diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
index 06f8dd4..6e8efa3 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
@@ -382,7 +382,7 @@
                 edges.push((source_node, target_node));
             },
         );
-        let graph = VecGraph::new(nodes.len(), edges);
+        let graph = VecGraph::<_, false>::new(nodes.len(), edges);
         let sccs = Sccs::new(&graph);
         Self { nodes, sccs }
     }
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index 0444cbe..a5f5242 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -32,7 +32,7 @@
 
 #[macro_use]
 extern crate rustc_macros;
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 #[macro_use]
 extern crate rustc_data_structures;
 #[macro_use]
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs
index 94ad0f5..7b4e085 100644
--- a/compiler/rustc_infer/src/traits/mod.rs
+++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -112,7 +112,7 @@
 }
 
 // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(PredicateObligation<'_>, 48);
 
 pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>;
diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs
index 31ceb23..c6ffba5 100644
--- a/compiler/rustc_infer/src/traits/project.rs
+++ b/compiler/rustc_infer/src/traits/project.rs
@@ -78,11 +78,12 @@
 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 pub struct ProjectionCacheKey<'tcx> {
     ty: ty::AliasTy<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 impl<'tcx> ProjectionCacheKey<'tcx> {
-    pub fn new(ty: ty::AliasTy<'tcx>) -> Self {
-        Self { ty }
+    pub fn new(ty: ty::AliasTy<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
+        Self { ty, param_env }
     }
 }
 
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index d0ce23d..75df006 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -1,5 +1,4 @@
 #![feature(decl_macro)]
-#![feature(generic_nonzero)]
 #![feature(lazy_cell)]
 #![feature(let_chains)]
 #![feature(thread_spawn_unchecked)]
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index e563728..8d741ef 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -19,7 +19,9 @@
 use rustc_span::source_map::{RealFileLoader, SourceMapInputs};
 use rustc_span::symbol::sym;
 use rustc_span::{FileName, SourceFileHashAlgorithm};
-use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel};
+use rustc_target::spec::{
+    CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel, WasmCAbi,
+};
 use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
 use std::collections::{BTreeMap, BTreeSet};
 use std::num::NonZero;
@@ -758,7 +760,7 @@
     );
     tracked!(codegen_backend, Some("abc".to_string()));
     tracked!(collapse_macro_debuginfo, CollapseMacroDebuginfo::Yes);
-    tracked!(coverage_options, CoverageOptions { branch: true });
+    tracked!(coverage_options, CoverageOptions { branch: true, mcdc: true });
     tracked!(crate_attr, vec!["abc".to_string()]);
     tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
     tracked!(debug_info_for_profiling, true);
@@ -851,6 +853,7 @@
     tracked!(verify_llvm_ir, true);
     tracked!(virtual_function_elimination, true);
     tracked!(wasi_exec_model, Some(WasiExecModel::Reactor));
+    tracked!(wasm_c_abi, WasmCAbi::Spec);
     // tidy-alphabetical-end
 
     macro_rules! tracked_no_crate_hash {
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index ca84e93..83fff98 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -88,6 +88,10 @@
     /// tokens.
     UnknownPrefix,
 
+    /// Similar to the above, but *always* an error on every edition. This is used
+    /// for emoji identifier recovery, as those are not meant to be ever accepted.
+    InvalidPrefix,
+
     /// Examples: `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid
     /// suffix, but may be present here on string and float literals. Users of
     /// this type will need to check for and reject that case.
@@ -528,7 +532,7 @@
         // Known prefixes must have been handled earlier. So if
         // we see a prefix here, it is definitely an unknown prefix.
         match self.first() {
-            '#' | '"' | '\'' => UnknownPrefix,
+            '#' | '"' | '\'' => InvalidPrefix,
             _ => InvalidIdent,
         }
     }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 31c80c4..086c383 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -32,7 +32,6 @@
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(extract_if)]
-#![feature(generic_nonzero)]
 #![feature(if_let_guard)]
 #![feature(iter_order_by)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 4b06278..004c2c2 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -264,7 +264,19 @@
     binder: EarlyBinder<TraitRef<'tcx>>,
     mut did_has_local_parent: impl FnMut(DefId) -> bool,
 ) -> bool {
-    let infcx = tcx.infer_ctxt().build();
+    let infcx = tcx
+        .infer_ctxt()
+        // We use the new trait solver since the obligation we are trying to
+        // prove here may overflow and those are fatal in the old trait solver.
+        // Which is unacceptable for a lint.
+        //
+        // Thanksfully the part we use here are very similar to the
+        // new-trait-solver-as-coherence, which is in stabilization.
+        //
+        // https://github.com/rust-lang/rust/issues/123573
+        .with_next_trait_solver(true)
+        .build();
+
     let trait_ref = binder.instantiate(tcx, infcx.fresh_args_for_item(infer_span, trait_def_id));
 
     let trait_ref = trait_ref.fold_with(&mut ReplaceLocalTypesWithInfer {
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 3fe1f21..0b0949e 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1056,7 +1056,7 @@
         avoid_mut: bool,
         keep_space: (bool, bool),
     ) {
-        use ast::{BindingAnnotation, PatKind};
+        use ast::{BindingMode, PatKind};
 
         if let PatKind::Paren(inner) = &value.kind {
             match inner.kind {
@@ -1068,7 +1068,7 @@
                 // Avoid `p0 | .. | pn` if we should.
                 PatKind::Or(..) if avoid_or => return,
                 // Avoid `mut x` and `mut x @ p` if we should:
-                PatKind::Ident(BindingAnnotation::MUT, ..) if avoid_mut => {
+                PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
                     return;
                 }
                 // Otherwise proceed with linting.
diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp
index 64e6c18..8871f41 100644
--- a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp
@@ -175,7 +175,7 @@
 extern "C" LLVMRustResult
 LLVMRustWriteArchive(char *Dst, size_t NumMembers,
                      const LLVMRustArchiveMemberRef *NewMembers,
-                     bool WriteSymbtab, LLVMRustArchiveKind RustKind) {
+                     bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) {
 
   std::vector<NewArchiveMember> Members;
   auto Kind = fromRust(RustKind);
@@ -207,7 +207,7 @@
   auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false);
 #else
   auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab : SymtabWritingMode::NoSymtab;
-  auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false);
+  auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC);
 #endif
   if (!Result)
     return LLVMRustResult::Success;
diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
index 0998b46..e842f47 100644
--- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
@@ -4,8 +4,6 @@
 #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
 #include "llvm/ProfileData/InstrProf.h"
 
-#include <iostream>
-
 using namespace llvm;
 
 // FFI equivalent of enum `llvm::coverage::Counter::CounterKind`
@@ -43,6 +41,8 @@
   SkippedRegion = 2,
   GapRegion = 3,
   BranchRegion = 4,
+  MCDCDecisionRegion = 5,
+  MCDCBranchRegion = 6
 };
 
 static coverage::CounterMappingRegion::RegionKind
@@ -58,15 +58,102 @@
     return coverage::CounterMappingRegion::GapRegion;
   case LLVMRustCounterMappingRegionKind::BranchRegion:
     return coverage::CounterMappingRegion::BranchRegion;
+#if LLVM_VERSION_GE(18, 0)
+  case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion:
+    return coverage::CounterMappingRegion::MCDCDecisionRegion;
+  case LLVMRustCounterMappingRegionKind::MCDCBranchRegion:
+    return coverage::CounterMappingRegion::MCDCBranchRegion;
+#else
+  case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion:
+    break;
+  case LLVMRustCounterMappingRegionKind::MCDCBranchRegion:
+    break;
+#endif
   }
   report_fatal_error("Bad LLVMRustCounterMappingRegionKind!");
 }
 
+enum LLVMRustMCDCParametersTag {
+  None = 0,
+  Decision = 1,
+  Branch = 2,
+};
+
+struct LLVMRustMCDCDecisionParameters {
+  uint32_t BitmapIdx;
+  uint16_t NumConditions;
+};
+
+struct LLVMRustMCDCBranchParameters {
+  int16_t ConditionID;
+  int16_t ConditionIDs[2];
+};
+
+struct LLVMRustMCDCParameters {
+  LLVMRustMCDCParametersTag Tag;
+  LLVMRustMCDCDecisionParameters DecisionParameters;
+  LLVMRustMCDCBranchParameters BranchParameters;
+};
+
+// LLVM representations for `MCDCParameters` evolved from LLVM 18 to 19.
+// Look at representations in 18
+// https://github.com/rust-lang/llvm-project/blob/66a2881a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L253-L263
+// and representations in 19
+// https://github.com/llvm/llvm-project/blob/843cc474faefad1d639f4c44c1cf3ad7dbda76c8/llvm/include/llvm/ProfileData/Coverage/MCDCTypes.h
+#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
+static coverage::CounterMappingRegion::MCDCParameters
+fromRust(LLVMRustMCDCParameters Params) {
+  auto parameter = coverage::CounterMappingRegion::MCDCParameters{};
+  switch (Params.Tag) {
+  case LLVMRustMCDCParametersTag::None:
+    return parameter;
+  case LLVMRustMCDCParametersTag::Decision:
+    parameter.BitmapIdx =
+        static_cast<unsigned>(Params.DecisionParameters.BitmapIdx),
+    parameter.NumConditions =
+        static_cast<unsigned>(Params.DecisionParameters.NumConditions);
+    return parameter;
+  case LLVMRustMCDCParametersTag::Branch:
+    parameter.ID = static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
+        Params.BranchParameters.ConditionID),
+    parameter.FalseID =
+        static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
+            Params.BranchParameters.ConditionIDs[0]),
+    parameter.TrueID =
+        static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
+            Params.BranchParameters.ConditionIDs[1]);
+    return parameter;
+  }
+  report_fatal_error("Bad LLVMRustMCDCParametersTag!");
+}
+#elif LLVM_VERSION_GE(19, 0)
+static coverage::mcdc::Parameters fromRust(LLVMRustMCDCParameters Params) {
+  switch (Params.Tag) {
+  case LLVMRustMCDCParametersTag::None:
+    return std::monostate();
+  case LLVMRustMCDCParametersTag::Decision:
+    return coverage::mcdc::DecisionParameters(
+        Params.DecisionParameters.BitmapIdx,
+        Params.DecisionParameters.NumConditions);
+  case LLVMRustMCDCParametersTag::Branch:
+    return coverage::mcdc::BranchParameters(
+        static_cast<coverage::mcdc::ConditionID>(
+            Params.BranchParameters.ConditionID),
+        {static_cast<coverage::mcdc::ConditionID>(
+             Params.BranchParameters.ConditionIDs[0]),
+         static_cast<coverage::mcdc::ConditionID>(
+             Params.BranchParameters.ConditionIDs[1])});
+  }
+  report_fatal_error("Bad LLVMRustMCDCParametersTag!");
+}
+#endif
+
 // FFI equivalent of struct `llvm::coverage::CounterMappingRegion`
 // https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L211-L304
 struct LLVMRustCounterMappingRegion {
   LLVMRustCounter Count;
   LLVMRustCounter FalseCount;
+  LLVMRustMCDCParameters MCDCParameters;
   uint32_t FileID;
   uint32_t ExpandedFileID;
   uint32_t LineStart;
@@ -135,7 +222,8 @@
     MappingRegions.emplace_back(
         fromRust(Region.Count), fromRust(Region.FalseCount),
 #if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
-        coverage::CounterMappingRegion::MCDCParameters{},
+        // LLVM 19 may move this argument to last.
+        fromRust(Region.MCDCParameters),
 #endif
         Region.FileID, Region.ExpandedFileID, // File IDs, then region info.
         Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 6e11fd6..565bdc3 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -1522,6 +1522,7 @@
   delete Bundle;
 }
 
+// OpBundlesIndirect is an array of pointers (*not* a pointer to an array).
 extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
                                           LLVMValueRef *Args, unsigned NumArgs,
                                           OperandBundleDef **OpBundlesIndirect,
@@ -1546,6 +1547,33 @@
       unwrap(M), llvm::Intrinsic::instrprof_increment));
 }
 
+extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(18, 0)
+  return wrap(llvm::Intrinsic::getDeclaration(
+      unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters));
+#else
+  report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
+#endif
+}
+
+extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(18, 0)
+  return wrap(llvm::Intrinsic::getDeclaration(
+      unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update));
+#else
+  report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
+#endif
+}
+
+extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(18, 0)
+  return wrap(llvm::Intrinsic::getDeclaration(
+      unwrap(M), llvm::Intrinsic::instrprof_mcdc_condbitmap_update));
+#else
+  report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
+#endif
+}
+
 extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,
                                             LLVMValueRef Dst, unsigned DstAlign,
                                             LLVMValueRef Src, unsigned SrcAlign,
@@ -1574,6 +1602,7 @@
       unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile));
 }
 
+// OpBundlesIndirect is an array of pointers (*not* a pointer to an array).
 extern "C" LLVMValueRef
 LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
                     LLVMValueRef *Args, unsigned NumArgs,
@@ -1596,6 +1625,7 @@
                                       Name));
 }
 
+// OpBundlesIndirect is an array of pointers (*not* a pointer to an array).
 extern "C" LLVMValueRef
 LLVMRustBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
                     LLVMBasicBlockRef DefaultDest,
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index f133a2f..7dd0340 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -6,7 +6,6 @@
 #![feature(error_iter)]
 #![feature(extract_if)]
 #![feature(coroutines)]
-#![feature(generic_nonzero)]
 #![feature(iter_from_coroutine)]
 #![feature(let_chains)]
 #![feature(if_let_guard)]
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index e52a586..aadaca1 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -35,7 +35,6 @@
 #![feature(const_type_name)]
 #![feature(discriminant_kind)]
 #![feature(coroutines)]
-#![feature(generic_nonzero)]
 #![feature(if_let_guard)]
 #![feature(inline_const)]
 #![feature(iter_from_coroutine)]
diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs
index e30b6b2..123b32f 100644
--- a/compiler/rustc_middle/src/middle/exported_symbols.rs
+++ b/compiler/rustc_middle/src/middle/exported_symbols.rs
@@ -43,6 +43,7 @@
     NonGeneric(DefId),
     Generic(DefId, GenericArgsRef<'tcx>),
     DropGlue(Ty<'tcx>),
+    AsyncDropGlueCtorShim(Ty<'tcx>),
     ThreadLocalShim(DefId),
     NoDefId(ty::SymbolName<'tcx>),
 }
@@ -59,6 +60,9 @@
             ExportedSymbol::DropGlue(ty) => {
                 tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty))
             }
+            ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
+                tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty))
+            }
             ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance {
                 def: ty::InstanceDef::ThreadLocalShim(def_id),
                 args: ty::GenericArgs::empty(),
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index 155af06..596271c 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -70,7 +70,7 @@
     },
 }
 
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(ConstValue<'_>, 24);
 
 impl<'tcx> ConstValue<'tcx> {
@@ -87,7 +87,7 @@
     }
 
     pub fn try_to_bits(&self, size: Size) -> Option<u128> {
-        self.try_to_scalar_int()?.to_bits(size).ok()
+        self.try_to_scalar_int()?.try_to_bits(size).ok()
     }
 
     pub fn try_to_bool(&self) -> Option<bool> {
@@ -244,6 +244,8 @@
             Const::Ty(c) => match c.kind() {
                 ty::ConstKind::Value(valtree) if c.ty().is_primitive() => {
                     // A valtree of a type where leaves directly represent the scalar const value.
+                    // Just checking whether it is a leaf is insufficient as e.g. references are leafs
+                    // but the leaf value is the value they point to, not the reference itself!
                     Some(valtree.unwrap_leaf().into())
                 }
                 _ => None,
@@ -255,12 +257,22 @@
 
     #[inline]
     pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
-        self.try_to_scalar()?.try_to_int().ok()
+        // This is equivalent to `self.try_to_scalar()?.try_to_int().ok()`, but measurably faster.
+        match self {
+            Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
+            Const::Ty(c) => match c.kind() {
+                ty::ConstKind::Value(valtree) if c.ty().is_primitive() => {
+                    Some(valtree.unwrap_leaf())
+                }
+                _ => None,
+            },
+            _ => None,
+        }
     }
 
     #[inline]
     pub fn try_to_bits(self, size: Size) -> Option<u128> {
-        self.try_to_scalar_int()?.to_bits(size).ok()
+        self.try_to_scalar_int()?.try_to_bits(size).ok()
     }
 
     #[inline]
@@ -334,7 +346,7 @@
         let int = self.try_eval_scalar_int(tcx, param_env)?;
         let size =
             tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size;
-        int.to_bits(size).ok()
+        int.try_to_bits(size).ok()
     }
 
     /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index 582a180..04011fd 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -51,6 +51,25 @@
     pub struct ExpressionId {}
 }
 
+rustc_index::newtype_index! {
+    /// ID of a mcdc condition. Used by llvm to check mcdc coverage.
+    ///
+    /// Note for future: the max limit of 0xFFFF is probably too loose. Actually llvm does not
+    /// support decisions with too many conditions (7 and more at LLVM 18 while may be hundreds at 19)
+    /// and represents it with `int16_t`. This max value may be changed once we could
+    /// figure out an accurate limit.
+    #[derive(HashStable)]
+    #[encodable]
+    #[orderable]
+    #[max = 0xFFFF]
+    #[debug_format = "ConditionId({})"]
+    pub struct ConditionId {}
+}
+
+impl ConditionId {
+    pub const NONE: Self = Self::from_u32(0);
+}
+
 /// Enum that can hold a constant zero value, the ID of an physical coverage
 /// counter, or the ID of a coverage-counter expression.
 ///
@@ -106,6 +125,22 @@
     /// mappings. Intermediate expressions with no direct mappings are
     /// retained/zeroed based on whether they are transitively used.)
     ExpressionUsed { id: ExpressionId },
+
+    /// Marks the point in MIR control flow represented by a evaluated condition.
+    ///
+    /// This is eventually lowered to `llvm.instrprof.mcdc.condbitmap.update` in LLVM IR.
+    ///
+    /// If this statement does not survive MIR optimizations, the condition would never be
+    /// taken as evaluated.
+    CondBitmapUpdate { id: ConditionId, value: bool },
+
+    /// Marks the point in MIR control flow represented by a evaluated decision.
+    ///
+    /// This is eventually lowered to `llvm.instrprof.mcdc.tvbitmap.update` in LLVM IR.
+    ///
+    /// If this statement does not survive MIR optimizations, the decision would never be
+    /// taken as evaluated.
+    TestVectorBitmapUpdate { bitmap_idx: u32 },
 }
 
 impl Debug for CoverageKind {
@@ -116,6 +151,12 @@
             BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
             CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
             ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
+            CondBitmapUpdate { id, value } => {
+                write!(fmt, "CondBitmapUpdate({:?}, {:?})", id.index(), value)
+            }
+            TestVectorBitmapUpdate { bitmap_idx } => {
+                write!(fmt, "TestVectorUpdate({:?})", bitmap_idx)
+            }
         }
     }
 }
@@ -172,16 +213,23 @@
     Code(CovTerm),
     /// Associates a branch region with separate counters for true and false.
     Branch { true_term: CovTerm, false_term: CovTerm },
+    /// Associates a branch region with separate counters for true and false.
+    MCDCBranch { true_term: CovTerm, false_term: CovTerm, mcdc_params: ConditionInfo },
+    /// Associates a decision region with a bitmap and number of conditions.
+    MCDCDecision(DecisionInfo),
 }
 
 impl MappingKind {
     /// Iterator over all coverage terms in this mapping kind.
     pub fn terms(&self) -> impl Iterator<Item = CovTerm> {
-        let one = |a| std::iter::once(a).chain(None);
-        let two = |a, b| std::iter::once(a).chain(Some(b));
+        let zero = || None.into_iter().chain(None);
+        let one = |a| Some(a).into_iter().chain(None);
+        let two = |a, b| Some(a).into_iter().chain(Some(b));
         match *self {
             Self::Code(term) => one(term),
             Self::Branch { true_term, false_term } => two(true_term, false_term),
+            Self::MCDCBranch { true_term, false_term, .. } => two(true_term, false_term),
+            Self::MCDCDecision(_) => zero(),
         }
     }
 
@@ -193,6 +241,12 @@
             Self::Branch { true_term, false_term } => {
                 Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) }
             }
+            Self::MCDCBranch { true_term, false_term, mcdc_params } => Self::MCDCBranch {
+                true_term: map_fn(true_term),
+                false_term: map_fn(false_term),
+                mcdc_params,
+            },
+            Self::MCDCDecision(param) => Self::MCDCDecision(param),
         }
     }
 }
@@ -212,7 +266,7 @@
 pub struct FunctionCoverageInfo {
     pub function_source_hash: u64,
     pub num_counters: usize,
-
+    pub mcdc_bitmap_bytes: u32,
     pub expressions: IndexVec<ExpressionId, Expression>,
     pub mappings: Vec<Mapping>,
 }
@@ -226,6 +280,8 @@
     /// data structures without having to scan the entire body first.
     pub num_block_markers: usize,
     pub branch_spans: Vec<BranchSpan>,
+    pub mcdc_branch_spans: Vec<MCDCBranchSpan>,
+    pub mcdc_decision_spans: Vec<MCDCDecisionSpan>,
 }
 
 #[derive(Clone, Debug)]
@@ -235,3 +291,47 @@
     pub true_marker: BlockMarkerId,
     pub false_marker: BlockMarkerId,
 }
+
+#[derive(Copy, Clone, Debug)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+pub struct ConditionInfo {
+    pub condition_id: ConditionId,
+    pub true_next_id: ConditionId,
+    pub false_next_id: ConditionId,
+}
+
+impl Default for ConditionInfo {
+    fn default() -> Self {
+        Self {
+            condition_id: ConditionId::NONE,
+            true_next_id: ConditionId::NONE,
+            false_next_id: ConditionId::NONE,
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+pub struct MCDCBranchSpan {
+    pub span: Span,
+    /// If `None`, this actually represents a normal branch span inserted for
+    /// code that was too complex for MC/DC.
+    pub condition_info: Option<ConditionInfo>,
+    pub true_marker: BlockMarkerId,
+    pub false_marker: BlockMarkerId,
+}
+
+#[derive(Copy, Clone, Debug)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+pub struct DecisionInfo {
+    pub bitmap_idx: u32,
+    pub conditions_num: u16,
+}
+
+#[derive(Clone, Debug)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+pub struct MCDCDecisionSpan {
+    pub span: Span,
+    pub conditions_num: usize,
+    pub end_markers: Vec<BlockMarkerId>,
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index e9be26d..65ce1cd 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -88,7 +88,7 @@
 /// This is needed in `thir::pattern::lower_inline_const`.
 pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
 
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(InterpErrorInfo<'_>, 8);
 
 /// Packages the kind of error we got from the const code interpreter
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 9f9433e..9728967 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -37,7 +37,7 @@
     Ptr(Pointer<Prov>, u8),
 }
 
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(Scalar, 24);
 
 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
@@ -236,7 +236,7 @@
     ) -> Result<Either<u128, Pointer<Prov>>, ScalarSizeMismatch> {
         assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
         Ok(match self {
-            Scalar::Int(int) => Left(int.to_bits(target_size).map_err(|size| {
+            Scalar::Int(int) => Left(int.try_to_bits(target_size).map_err(|size| {
                 ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes() }
             })?),
             Scalar::Ptr(ptr, sz) => {
@@ -301,6 +301,11 @@
     }
 
     #[inline(always)]
+    pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> {
+        self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into())
+    }
+
+    #[inline(always)]
     #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
     pub fn assert_int(self) -> ScalarInt {
         self.try_to_int().unwrap()
@@ -311,16 +316,13 @@
     #[inline]
     pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
         assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
-        self.try_to_int()
-            .map_err(|_| err_unsup!(ReadPointerAsInt(None)))?
-            .to_bits(target_size)
-            .map_err(|size| {
-                err_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
-                    target_size: target_size.bytes(),
-                    data_size: size.bytes(),
-                }))
-                .into()
-            })
+        self.to_scalar_int()?.try_to_bits(target_size).map_err(|size| {
+            err_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
+                target_size: target_size.bytes(),
+                data_size: size.bytes(),
+            }))
+            .into()
+        })
     }
 
     #[inline(always)]
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index df013ef..43e4e82 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -18,8 +18,7 @@
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
 use rustc_hir::{
-    self as hir, BindingAnnotation, ByRef, CoroutineDesugaring, CoroutineKind, HirId,
-    ImplicitSelfKind,
+    self as hir, BindingMode, ByRef, CoroutineDesugaring, CoroutineKind, HirId, ImplicitSelfKind,
 };
 use rustc_session::Session;
 use rustc_span::source_map::Spanned;
@@ -930,7 +929,7 @@
 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
 pub struct VarBindingForm<'tcx> {
     /// Is variable bound via `x`, `mut x`, `ref x`, `ref mut x`, `mut ref x`, or `mut ref mut x`?
-    pub binding_mode: BindingAnnotation,
+    pub binding_mode: BindingMode,
     /// If an explicit type was provided for this variable binding,
     /// this holds the source Span of that type.
     ///
@@ -1155,7 +1154,7 @@
             self.local_info(),
             LocalInfo::User(
                 BindingForm::Var(VarBindingForm {
-                    binding_mode: BindingAnnotation(ByRef::No, _),
+                    binding_mode: BindingMode(ByRef::No, _),
                     opt_ty_info: _,
                     opt_match_place: _,
                     pat_span: _,
@@ -1172,7 +1171,7 @@
             self.local_info(),
             LocalInfo::User(
                 BindingForm::Var(VarBindingForm {
-                    binding_mode: BindingAnnotation(ByRef::No, _),
+                    binding_mode: BindingMode(ByRef::No, _),
                     opt_ty_info: _,
                     opt_match_place: _,
                     pat_span: _,
@@ -1814,7 +1813,7 @@
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 43e1318..9eed701 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -65,7 +65,9 @@
                 match instance.def {
                     // "Normal" functions size estimate: the number of
                     // statements, plus one for the terminator.
-                    InstanceDef::Item(..) | InstanceDef::DropGlue(..) => {
+                    InstanceDef::Item(..)
+                    | InstanceDef::DropGlue(..)
+                    | InstanceDef::AsyncDropGlueCtorShim(..) => {
                         let mir = tcx.instance_mir(instance.def);
                         mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum()
                     }
@@ -406,7 +408,8 @@
                             | InstanceDef::DropGlue(..)
                             | InstanceDef::CloneShim(..)
                             | InstanceDef::ThreadLocalShim(..)
-                            | InstanceDef::FnPtrAddrShim(..) => None,
+                            | InstanceDef::FnPtrAddrShim(..)
+                            | InstanceDef::AsyncDropGlueCtorShim(..) => None,
                         }
                     }
                     MonoItem::Static(def_id) => def_id.as_local().map(Idx::index),
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 15bd5c0..7a91d73 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -187,6 +187,17 @@
             }));
             s
         }
+        ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => {
+            // Unfortunately, pretty-printed typed are not very filename-friendly.
+            // We dome some filtering.
+            let mut s = ".".to_owned();
+            s.extend(ty.to_string().chars().filter_map(|c| match c {
+                ' ' => None,
+                ':' | '<' | '>' => Some('_'),
+                c => Some(c),
+            }));
+            s
+        }
         _ => String::new(),
     };
 
@@ -475,7 +486,8 @@
     branch_info: &coverage::BranchInfo,
     w: &mut dyn io::Write,
 ) -> io::Result<()> {
-    let coverage::BranchInfo { branch_spans, .. } = branch_info;
+    let coverage::BranchInfo { branch_spans, mcdc_branch_spans, mcdc_decision_spans, .. } =
+        branch_info;
 
     for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {
         writeln!(
@@ -483,7 +495,26 @@
             "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
         )?;
     }
-    if !branch_spans.is_empty() {
+
+    for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
+        mcdc_branch_spans
+    {
+        writeln!(
+            w,
+            "{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
+            condition_info.map(|info| info.condition_id)
+        )?;
+    }
+
+    for coverage::MCDCDecisionSpan { span, conditions_num, end_markers } in mcdc_decision_spans {
+        writeln!(
+            w,
+            "{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?} }} => {span:?}"
+        )?;
+    }
+
+    if !branch_spans.is_empty() || !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty()
+    {
         writeln!(w)?;
     }
 
@@ -1074,6 +1105,15 @@
 
                         struct_fmt.finish()
                     }),
+
+                    AggregateKind::RawPtr(pointee_ty, mutability) => {
+                        let kind_str = match mutability {
+                            Mutability::Mut => "mut",
+                            Mutability::Not => "const",
+                        };
+                        with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?;
+                        fmt_tuple(fmt, "")
+                    }
                 }
             }
 
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 0f62212..e3f5872 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -213,7 +213,7 @@
 }
 
 // Make sure this enum doesn't unintentionally grow
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16);
 
 /// Outlives-constraints can be categorized to determine whether and why they
@@ -361,4 +361,8 @@
     /// InstrumentCoverage MIR pass, if the highest-numbered counter increments
     /// were removed by MIR optimizations.
     pub max_counter_id: mir::coverage::CounterId,
+
+    /// Coverage codegen for mcdc needs to know the size of the global bitmap so that it can
+    /// set the `bytemap-bytes` argument of the `llvm.instrprof.mcdc.tvbitmap.update` intrinsic.
+    pub mcdc_bitmap_bytes: u32,
 }
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 9b40957..db13bb9 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1351,6 +1351,21 @@
     Closure(DefId, GenericArgsRef<'tcx>),
     Coroutine(DefId, GenericArgsRef<'tcx>),
     CoroutineClosure(DefId, GenericArgsRef<'tcx>),
+
+    /// Construct a raw pointer from the data pointer and metadata.
+    ///
+    /// The `Ty` here is the type of the *pointee*, not the pointer itself.
+    /// The `Mutability` indicates whether this produces a `*const` or `*mut`.
+    ///
+    /// The [`Rvalue::Aggregate`] operands for thus must be
+    ///
+    /// 0. A raw pointer of matching mutability with any [`core::ptr::Thin`] pointee
+    /// 1. A value of the appropriate [`core::ptr::Pointee::Metadata`] type
+    ///
+    /// *Both* operands must always be included, even the unit value if this is
+    /// creating a thin pointer. If you're just converting between thin pointers,
+    /// you may want an [`Rvalue::Cast`] with [`CastKind::PtrToPtr`] instead.
+    RawPtr(Ty<'tcx>, Mutability),
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
@@ -1453,7 +1468,7 @@
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     // tidy-alphabetical-start
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index b86aa60..abe99f3 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -14,7 +14,7 @@
 }
 
 // At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(PlaceTy<'_>, 16);
 
 impl<'tcx> PlaceTy<'tcx> {
@@ -206,6 +206,7 @@
                 AggregateKind::CoroutineClosure(did, args) => {
                     Ty::new_coroutine_closure(tcx, did, args)
                 }
+                AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability),
             },
             Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
             Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 4f7b2f7..a6d5252 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -350,12 +350,14 @@
                             receiver_by_ref: _,
                         } |
                         ty::InstanceDef::CoroutineKindShim { coroutine_def_id: _def_id } |
+                        ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, None) |
                         ty::InstanceDef::DropGlue(_def_id, None) => {}
 
                         ty::InstanceDef::FnPtrShim(_def_id, ty) |
                         ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
                         ty::InstanceDef::CloneShim(_def_id, ty) |
-                        ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => {
+                        ty::InstanceDef::FnPtrAddrShim(_def_id, ty) |
+                        ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, Some(ty)) => {
                             // FIXME(eddyb) use a better `TyContext` here.
                             self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
                         }
@@ -751,6 +753,9 @@
                             ) => {
                                 self.visit_args(coroutine_closure_args, location);
                             }
+                            AggregateKind::RawPtr(ty, _) => {
+                                self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+                            }
                         }
 
                         for operand in operands {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index c58eb46..a5d21c4 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1340,6 +1340,14 @@
     query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` is `Unpin`", env.value }
     }
+    /// Query backing `Ty::has_surface_async_drop`.
+    query has_surface_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+        desc { "computing whether `{}` has `AsyncDrop` implementation", env.value }
+    }
+    /// Query backing `Ty::has_surface_drop`.
+    query has_surface_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+        desc { "computing whether `{}` has `Drop` implementation", env.value }
+    }
     /// Query backing `Ty::needs_drop`.
     query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` needs drop", env.value }
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index 6438619..038d3fe 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -322,7 +322,7 @@
 
                 // Ensure that keys grow no larger than 72 bytes by accident.
                 // Increase this limit if necessary, but do try to keep the size low if possible
-                #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))]
+                #[cfg(target_pointer_width = "64")]
                 const _: () = {
                     if mem::size_of::<Key<'static>>() > 72 {
                         panic!("{}", concat!(
@@ -337,7 +337,7 @@
 
                 // Ensure that values grow no larger than 64 bytes by accident.
                 // Increase this limit if necessary, but do try to keep the size low if possible
-                #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))]
+                #[cfg(target_pointer_width = "64")]
                 const _: () = {
                     if mem::size_of::<Value<'static>>() > 64 {
                         panic!("{}", concat!(
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 8763e94..d52b1ef 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -12,11 +12,11 @@
 use rustc_errors::{DiagArgValue, IntoDiagArg};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{BindingAnnotation, ByRef, HirId, MatchSource, RangeEnd};
+use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd};
 use rustc_index::newtype_index;
 use rustc_index::IndexVec;
 use rustc_middle::middle::region;
-use rustc_middle::mir::interpret::{AllocId, Scalar};
+use rustc_middle::mir::interpret::AllocId;
 use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp};
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::layout::IntegerExt;
@@ -603,10 +603,7 @@
     pub fn simple_ident(&self) -> Option<Symbol> {
         match self.kind {
             PatKind::Binding {
-                name,
-                mode: BindingAnnotation(ByRef::No, _),
-                subpattern: None,
-                ..
+                name, mode: BindingMode(ByRef::No, _), subpattern: None, ..
             } => Some(name),
             _ => None,
         }
@@ -730,7 +727,7 @@
     Binding {
         name: Symbol,
         #[type_visitable(ignore)]
-        mode: BindingAnnotation,
+        mode: BindingMode,
         #[type_visitable(ignore)]
         var: LocalVarId,
         ty: Ty<'tcx>,
@@ -1009,18 +1006,20 @@
 
             // This code is hot when compiling matches with many ranges. So we
             // special-case extraction of evaluated scalars for speed, for types where
-            // raw data comparisons are appropriate. E.g. `unicode-normalization` has
+            // we can do scalar comparisons. E.g. `unicode-normalization` has
             // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
             // in this way.
-            (Finite(mir::Const::Ty(a)), Finite(mir::Const::Ty(b)))
-                if matches!(ty.kind(), ty::Uint(_) | ty::Char) =>
-            {
-                return Some(a.to_valtree().cmp(&b.to_valtree()));
+            (Finite(a), Finite(b)) if matches!(ty.kind(), ty::Int(_) | ty::Uint(_) | ty::Char) => {
+                if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) {
+                    let sz = ty.primitive_size(tcx);
+                    let cmp = match ty.kind() {
+                        ty::Uint(_) | ty::Char => a.assert_uint(sz).cmp(&b.assert_uint(sz)),
+                        ty::Int(_) => a.assert_int(sz).cmp(&b.assert_int(sz)),
+                        _ => unreachable!(),
+                    };
+                    return Some(cmp);
+                }
             }
-            (
-                Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(a)), _)),
-                Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(b)), _)),
-            ) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => return Some(a.cmp(&b)),
             _ => {}
         }
 
@@ -1206,7 +1205,7 @@
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     // tidy-alphabetical-start
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 28f6184..ceee3ea 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -551,7 +551,7 @@
 }
 
 // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(ObligationCauseCode<'_>, 48);
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 49b806b8..fd4573c 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -22,6 +22,9 @@
 
 pub type ConstKind<'tcx> = IrConstKind<TyCtxt<'tcx>>;
 
+#[cfg(target_pointer_width = "64")]
+static_assert_size!(ConstKind<'_>, 32);
+
 /// Use this rather than `ConstData`, whenever possible.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)]
 #[rustc_pass_by_value]
@@ -59,7 +62,7 @@
     pub kind: ConstKind<'tcx>,
 }
 
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(ConstData<'_>, 40);
 
 impl<'tcx> Const<'tcx> {
@@ -406,7 +409,7 @@
         let size =
             tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size;
         // if `ty` does not depend on generic parameters, use an empty param_env
-        int.to_bits(size).ok()
+        int.try_to_bits(size).ok()
     }
 
     #[inline]
diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs
index 71d7dfd..40ac878 100644
--- a/compiler/rustc_middle/src/ty/consts/int.rs
+++ b/compiler/rustc_middle/src/ty/consts/int.rs
@@ -126,7 +126,7 @@
 ///
 /// This is a packed struct in order to allow this type to be optimally embedded in enums
 /// (like Scalar).
-#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Clone, Copy, Eq, PartialEq, Hash)]
 #[repr(packed)]
 pub struct ScalarInt {
     /// The first `size` bytes of `data` are the value.
@@ -167,9 +167,12 @@
 
 impl ScalarInt {
     pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() };
-
     pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() };
 
+    fn raw(data: u128, size: Size) -> Self {
+        Self { data, size: NonZero::new(size.bytes() as u8).unwrap() }
+    }
+
     #[inline]
     pub fn size(self) -> Size {
         Size::from_bytes(self.size.get())
@@ -196,7 +199,7 @@
 
     #[inline]
     pub fn null(size: Size) -> Self {
-        Self { data: 0, size: NonZero::new(size.bytes() as u8).unwrap() }
+        Self::raw(0, size)
     }
 
     #[inline]
@@ -207,11 +210,15 @@
     #[inline]
     pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
         let data = i.into();
-        if size.truncate(data) == data {
-            Some(Self { data, size: NonZero::new(size.bytes() as u8).unwrap() })
-        } else {
-            None
-        }
+        if size.truncate(data) == data { Some(Self::raw(data, size)) } else { None }
+    }
+
+    /// Returns the truncated result, and whether truncation changed the value.
+    #[inline]
+    pub fn truncate_from_uint(i: impl Into<u128>, size: Size) -> (Self, bool) {
+        let data = i.into();
+        let r = Self::raw(size.truncate(data), size);
+        (r, r.data != data)
     }
 
     #[inline]
@@ -220,26 +227,27 @@
         // `into` performed sign extension, we have to truncate
         let truncated = size.truncate(i as u128);
         if size.sign_extend(truncated) as i128 == i {
-            Some(Self { data: truncated, size: NonZero::new(size.bytes() as u8).unwrap() })
+            Some(Self::raw(truncated, size))
         } else {
             None
         }
     }
 
+    /// Returns the truncated result, and whether truncation changed the value.
+    #[inline]
+    pub fn truncate_from_int(i: impl Into<i128>, size: Size) -> (Self, bool) {
+        let data = i.into();
+        let r = Self::raw(size.truncate(data as u128), size);
+        (r, size.sign_extend(r.data) as i128 != data)
+    }
+
     #[inline]
     pub fn try_from_target_usize(i: impl Into<u128>, tcx: TyCtxt<'_>) -> Option<Self> {
         Self::try_from_uint(i, tcx.data_layout.pointer_size)
     }
 
     #[inline]
-    pub fn assert_bits(self, target_size: Size) -> u128 {
-        self.to_bits(target_size).unwrap_or_else(|size| {
-            bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
-        })
-    }
-
-    #[inline]
-    pub fn to_bits(self, target_size: Size) -> Result<u128, Size> {
+    pub fn try_to_bits(self, target_size: Size) -> Result<u128, Size> {
         assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
         if target_size.bytes() == u64::from(self.size.get()) {
             self.check_data();
@@ -249,16 +257,28 @@
         }
     }
 
+    #[inline]
+    pub fn assert_bits(self, target_size: Size) -> u128 {
+        self.try_to_bits(target_size).unwrap_or_else(|size| {
+            bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
+        })
+    }
+
     /// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
     /// Fails if the size of the `ScalarInt` is not equal to `size` and returns the
     /// `ScalarInt`s size in that case.
     #[inline]
     pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
-        self.to_bits(size)
+        self.try_to_bits(size)
+    }
+
+    #[inline]
+    pub fn assert_uint(self, size: Size) -> u128 {
+        self.assert_bits(size)
     }
 
     // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
-    // in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
+    // in not equal to 1 byte and returns the `size` value of the `ScalarInt` in
     // that case.
     #[inline]
     pub fn try_to_u8(self) -> Result<u8, Size> {
@@ -266,7 +286,7 @@
     }
 
     /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
-    /// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in
+    /// in not equal to 2 bytes and returns the `size` value of the `ScalarInt` in
     /// that case.
     #[inline]
     pub fn try_to_u16(self) -> Result<u16, Size> {
@@ -274,7 +294,7 @@
     }
 
     /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
-    /// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in
+    /// in not equal to 4 bytes and returns the `size` value of the `ScalarInt` in
     /// that case.
     #[inline]
     pub fn try_to_u32(self) -> Result<u32, Size> {
@@ -282,7 +302,7 @@
     }
 
     /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
-    /// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in
+    /// in not equal to 8 bytes and returns the `size` value of the `ScalarInt` in
     /// that case.
     #[inline]
     pub fn try_to_u64(self) -> Result<u64, Size> {
@@ -290,7 +310,7 @@
     }
 
     /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
-    /// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in
+    /// in not equal to 16 bytes and returns the `size` value of the `ScalarInt` in
     /// that case.
     #[inline]
     pub fn try_to_u128(self) -> Result<u128, Size> {
@@ -303,7 +323,7 @@
     }
 
     // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
-    // in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
+    // in not equal to 1 byte or if the value is not 0 or 1 and returns the `size`
     // value of the `ScalarInt` in that case.
     #[inline]
     pub fn try_to_bool(self) -> Result<bool, Size> {
@@ -319,40 +339,46 @@
     /// `ScalarInt`s size in that case.
     #[inline]
     pub fn try_to_int(self, size: Size) -> Result<i128, Size> {
-        let b = self.to_bits(size)?;
+        let b = self.try_to_bits(size)?;
         Ok(size.sign_extend(b) as i128)
     }
 
+    #[inline]
+    pub fn assert_int(self, size: Size) -> i128 {
+        let b = self.assert_bits(size);
+        size.sign_extend(b) as i128
+    }
+
     /// Tries to convert the `ScalarInt` to i8.
-    /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 1 }`
+    /// Fails if the size of the `ScalarInt` is not equal to 1 byte
     /// and returns the `ScalarInt`s size in that case.
     pub fn try_to_i8(self) -> Result<i8, Size> {
         self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
     }
 
     /// Tries to convert the `ScalarInt` to i16.
-    /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 2 }`
+    /// Fails if the size of the `ScalarInt` is not equal to 2 bytes
     /// and returns the `ScalarInt`s size in that case.
     pub fn try_to_i16(self) -> Result<i16, Size> {
         self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
     }
 
     /// Tries to convert the `ScalarInt` to i32.
-    /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 4 }`
+    /// Fails if the size of the `ScalarInt` is not equal to 4 bytes
     /// and returns the `ScalarInt`s size in that case.
     pub fn try_to_i32(self) -> Result<i32, Size> {
         self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
     }
 
     /// Tries to convert the `ScalarInt` to i64.
-    /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 8 }`
+    /// Fails if the size of the `ScalarInt` is not equal to 8 bytes
     /// and returns the `ScalarInt`s size in that case.
     pub fn try_to_i64(self) -> Result<i64, Size> {
         self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
     }
 
     /// Tries to convert the `ScalarInt` to i128.
-    /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 16 }`
+    /// Fails if the size of the `ScalarInt` is not equal to 16 bytes
     /// and returns the `ScalarInt`s size in that case.
     pub fn try_to_i128(self) -> Result<i128, Size> {
         self.try_to_int(Size::from_bits(128))
@@ -366,7 +392,7 @@
     #[inline]
     pub fn try_to_float<F: Float>(self) -> Result<F, Size> {
         // Going through `to_uint` to check size and truncation.
-        Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?))
+        Ok(F::from_bits(self.try_to_bits(Size::from_bits(F::BITS))?))
     }
 
     #[inline]
@@ -415,7 +441,7 @@
                 fn try_from(int: ScalarInt) -> Result<Self, Size> {
                     // The `unwrap` cannot fail because to_bits (if it succeeds)
                     // is guaranteed to return a value that fits into the size.
-                    int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
+                    int.try_to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
                        .map(|u| u.try_into().unwrap())
                 }
             }
@@ -450,7 +476,7 @@
 
     #[inline]
     fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
-        let Ok(bits) = int.to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
+        let Ok(bits) = int.try_to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
             return Err(CharTryFromScalarInt);
         };
         match char::from_u32(bits.try_into().unwrap()) {
@@ -472,7 +498,7 @@
     type Error = Size;
     #[inline]
     fn try_from(int: ScalarInt) -> Result<Self, Size> {
-        int.to_bits(Size::from_bytes(2)).map(Self::from_bits)
+        int.try_to_bits(Size::from_bytes(2)).map(Self::from_bits)
     }
 }
 
@@ -488,7 +514,7 @@
     type Error = Size;
     #[inline]
     fn try_from(int: ScalarInt) -> Result<Self, Size> {
-        int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
+        int.try_to_bits(Size::from_bytes(4)).map(Self::from_bits)
     }
 }
 
@@ -504,7 +530,7 @@
     type Error = Size;
     #[inline]
     fn try_from(int: ScalarInt) -> Result<Self, Size> {
-        int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
+        int.try_to_bits(Size::from_bytes(8)).map(Self::from_bits)
     }
 }
 
@@ -520,7 +546,7 @@
     type Error = Size;
     #[inline]
     fn try_from(int: ScalarInt) -> Result<Self, Size> {
-        int.to_bits(Size::from_bytes(16)).map(Self::from_bits)
+        int.try_to_bits(Size::from_bytes(16)).map(Self::from_bits)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index 94e4170..a7e0a04 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -71,8 +71,5 @@
     Cast(CastKind, Const<'tcx>, Ty<'tcx>),
 }
 
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(Expr<'_>, 24);
-
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
-static_assert_size!(super::ConstKind<'_>, 32);
diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs
index ffa0e89..96bc551 100644
--- a/compiler/rustc_middle/src/ty/consts/valtree.rs
+++ b/compiler/rustc_middle/src/ty/consts/valtree.rs
@@ -3,7 +3,7 @@
 use crate::ty::{self, Ty, TyCtxt};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 
-#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq)]
 #[derive(HashStable)]
 /// This datastructure is used to represent the value of constants used in the type system.
 ///
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index e984f54..a99196c 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -263,7 +263,7 @@
     /// Returns the `GenericParamDef` associated with this `EarlyParamRegion`.
     pub fn region_param(
         &'tcx self,
-        param: &ty::EarlyParamRegion,
+        param: ty::EarlyParamRegion,
         tcx: TyCtxt<'tcx>,
     ) -> &'tcx GenericParamDef {
         let param = self.param_at(param.index as usize, tcx);
@@ -274,7 +274,7 @@
     }
 
     /// Returns the `GenericParamDef` associated with this `ParamTy`.
-    pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef {
+    pub fn type_param(&'tcx self, param: ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef {
         let param = self.param_at(param.index as usize, tcx);
         match param.kind {
             GenericParamDefKind::Type { .. } => param,
@@ -286,7 +286,7 @@
     /// `Generics`.
     pub fn opt_type_param(
         &'tcx self,
-        param: &ParamTy,
+        param: ParamTy,
         tcx: TyCtxt<'tcx>,
     ) -> Option<&'tcx GenericParamDef> {
         let param = self.opt_param_at(param.index as usize, tcx)?;
@@ -297,7 +297,7 @@
     }
 
     /// Returns the `GenericParamDef` associated with this `ParamConst`.
-    pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef {
+    pub fn const_param(&'tcx self, param: ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef {
         let param = self.param_at(param.index as usize, tcx);
         match param.kind {
             GenericParamDefKind::Const { .. } => param,
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index f8f59fb..4002d0d 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -168,6 +168,12 @@
     ///
     /// The `DefId` is for `FnPtr::addr`, the `Ty` is the type `T`.
     FnPtrAddrShim(DefId, Ty<'tcx>),
+
+    /// `core::future::async_drop::async_drop_in_place::<'_, T>`.
+    ///
+    /// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty`
+    /// is the type `T`.
+    AsyncDropGlueCtorShim(DefId, Option<Ty<'tcx>>),
 }
 
 impl<'tcx> Instance<'tcx> {
@@ -210,7 +216,9 @@
             InstanceDef::Item(def) => tcx
                 .upstream_monomorphizations_for(def)
                 .and_then(|monos| monos.get(&self.args).cloned()),
-            InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args),
+            InstanceDef::DropGlue(_, Some(_)) | InstanceDef::AsyncDropGlueCtorShim(_, _) => {
+                tcx.upstream_drop_glue_for(self.args)
+            }
             _ => None,
         }
     }
@@ -235,7 +243,8 @@
             | ty::InstanceDef::CoroutineKindShim { coroutine_def_id: def_id }
             | InstanceDef::DropGlue(def_id, _)
             | InstanceDef::CloneShim(def_id, _)
-            | InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
+            | InstanceDef::FnPtrAddrShim(def_id, _)
+            | InstanceDef::AsyncDropGlueCtorShim(def_id, _) => def_id,
         }
     }
 
@@ -243,9 +252,9 @@
     pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
         match self {
             ty::InstanceDef::Item(def) => Some(def),
-            ty::InstanceDef::DropGlue(def_id, Some(_)) | InstanceDef::ThreadLocalShim(def_id) => {
-                Some(def_id)
-            }
+            ty::InstanceDef::DropGlue(def_id, Some(_))
+            | InstanceDef::AsyncDropGlueCtorShim(def_id, _)
+            | InstanceDef::ThreadLocalShim(def_id) => Some(def_id),
             InstanceDef::VTableShim(..)
             | InstanceDef::ReifyShim(..)
             | InstanceDef::FnPtrShim(..)
@@ -279,6 +288,7 @@
         let def_id = match *self {
             ty::InstanceDef::Item(def) => def,
             ty::InstanceDef::DropGlue(_, Some(_)) => return false,
+            ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => return false,
             ty::InstanceDef::ThreadLocalShim(_) => return false,
             _ => return true,
         };
@@ -347,11 +357,13 @@
             | InstanceDef::ThreadLocalShim(..)
             | InstanceDef::FnPtrAddrShim(..)
             | InstanceDef::FnPtrShim(..)
-            | InstanceDef::DropGlue(_, Some(_)) => false,
+            | InstanceDef::DropGlue(_, Some(_))
+            | InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => false,
             InstanceDef::ClosureOnceShim { .. }
             | InstanceDef::ConstructCoroutineInClosureShim { .. }
             | InstanceDef::CoroutineKindShim { .. }
             | InstanceDef::DropGlue(..)
+            | InstanceDef::AsyncDropGlueCtorShim(..)
             | InstanceDef::Item(_)
             | InstanceDef::Intrinsic(..)
             | InstanceDef::ReifyShim(..)
@@ -396,6 +408,8 @@
         InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
         InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"),
         InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"),
+        InstanceDef::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"),
+        InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
     }
 }
 
@@ -638,6 +652,12 @@
         Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
     }
 
+    pub fn resolve_async_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> {
+        let def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, None);
+        let args = tcx.mk_args(&[ty.into()]);
+        Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
+    }
+
     #[instrument(level = "debug", skip(tcx), ret)]
     pub fn fn_once_adapter_instance(
         tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 50e68bf..6381bd1 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -15,7 +15,9 @@
 use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
 use rustc_target::abi::call::FnAbi;
 use rustc_target::abi::*;
-use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
+use rustc_target::spec::{
+    abi::Abi as SpecAbi, HasTargetSpec, HasWasmCAbiOpt, PanicStrategy, Target, WasmCAbi,
+};
 
 use std::borrow::Cow;
 use std::cmp;
@@ -483,6 +485,12 @@
     }
 }
 
+impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> {
+    fn wasm_c_abi_opt(&self) -> WasmCAbi {
+        self.sess.opts.unstable_opts.wasm_c_abi
+    }
+}
+
 impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> {
     #[inline]
     fn tcx(&self) -> TyCtxt<'tcx> {
@@ -528,6 +536,12 @@
     }
 }
 
+impl<'tcx, T: HasWasmCAbiOpt> HasWasmCAbiOpt for LayoutCx<'tcx, T> {
+    fn wasm_c_abi_opt(&self) -> WasmCAbi {
+        self.tcx.wasm_c_abi_opt()
+    }
+}
+
 impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx.tcx()
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index e6b773a..cc026e3 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1797,7 +1797,8 @@
             | ty::InstanceDef::DropGlue(..)
             | ty::InstanceDef::CloneShim(..)
             | ty::InstanceDef::ThreadLocalShim(..)
-            | ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
+            | ty::InstanceDef::FnPtrAddrShim(..)
+            | ty::InstanceDef::AsyncDropGlueCtorShim(..) => self.mir_shims(instance),
         }
     }
 
@@ -2183,7 +2184,7 @@
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 0bd009c..e545018 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1276,7 +1276,7 @@
 
     fn pretty_print_inherent_projection(
         &mut self,
-        alias_ty: &ty::AliasTy<'tcx>,
+        alias_ty: ty::AliasTy<'tcx>,
     ) -> Result<(), PrintError> {
         let def_key = self.tcx().def_key(alias_ty.def_id);
         self.path_generic_args(
@@ -3204,7 +3204,7 @@
 
     ty::AliasTy<'tcx> {
         if let DefKind::Impl { of_trait: false } = cx.tcx().def_kind(cx.tcx().parent(self.def_id)) {
-            p!(pretty_print_inherent_projection(self))
+            p!(pretty_print_inherent_projection(*self))
         } else {
             // If we're printing verbosely, or don't want to invoke queries
             // (`is_impl_trait_in_trait`), then fall back to printing the def path.
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 90c68e7..14a77d4 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -427,6 +427,7 @@
     crate::mir::coverage::BlockMarkerId,
     crate::mir::coverage::CounterId,
     crate::mir::coverage::ExpressionId,
+    crate::mir::coverage::ConditionId,
     crate::mir::Local,
     crate::mir::Promoted,
     crate::traits::Reveal,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index ad64745..0184ff5 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -24,6 +24,7 @@
 use rustc_target::spec::abi::{self, Abi};
 use std::assert_matches::debug_assert_matches;
 use std::borrow::Cow;
+use std::iter;
 use std::ops::{ControlFlow, Deref, Range};
 use ty::util::IntTypeExt;
 
@@ -1379,7 +1380,7 @@
         Ty::new_param(tcx, self.index, self.name)
     }
 
-    pub fn span_from_generics(&self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span {
+    pub fn span_from_generics(self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span {
         let generics = tcx.generics_of(item_with_generics);
         let type_param = generics.type_param(self, tcx);
         tcx.def_span(type_param.def_id)
@@ -2316,6 +2317,133 @@
         }
     }
 
+    /// Returns the type of the async destructor of this type.
+    pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> {
+        if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) {
+            return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
+                .instantiate_identity();
+        }
+        match *self.kind() {
+            ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
+                let assoc_items = tcx
+                    .associated_item_def_ids(tcx.require_lang_item(LangItem::AsyncDestruct, None));
+                Ty::new_projection(tcx, assoc_items[0], [self])
+            }
+
+            ty::Array(elem_ty, _) | ty::Slice(elem_ty) => {
+                let dtor = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropSlice)
+                    .instantiate(tcx, &[elem_ty.into()]);
+                Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
+                    .instantiate(tcx, &[dtor.into()])
+            }
+
+            ty::Adt(adt_def, args) if adt_def.is_enum() || adt_def.is_struct() => self
+                .adt_async_destructor_ty(
+                    tcx,
+                    adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))),
+                    param_env,
+                ),
+            ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env),
+            ty::Closure(_, args) => self.adt_async_destructor_ty(
+                tcx,
+                iter::once(args.as_closure().upvar_tys()),
+                param_env,
+            ),
+            ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty(
+                tcx,
+                iter::once(args.as_coroutine_closure().upvar_tys()),
+                param_env,
+            ),
+
+            ty::Adt(adt_def, _) => {
+                assert!(adt_def.is_union());
+
+                let surface_drop = self.surface_async_dropper_ty(tcx, param_env).unwrap();
+
+                Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
+                    .instantiate(tcx, &[surface_drop.into()])
+            }
+
+            ty::Bound(..)
+            | ty::Foreign(_)
+            | ty::Placeholder(_)
+            | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                bug!("`async_destructor_ty` applied to unexpected type: {self:?}")
+            }
+
+            _ => bug!("`async_destructor_ty` is not yet implemented for type: {self:?}"),
+        }
+    }
+
+    fn adt_async_destructor_ty<I>(
+        self,
+        tcx: TyCtxt<'tcx>,
+        variants: I,
+        param_env: ParamEnv<'tcx>,
+    ) -> Ty<'tcx>
+    where
+        I: Iterator + ExactSizeIterator,
+        I::Item: IntoIterator<Item = Ty<'tcx>>,
+    {
+        debug_assert!(!self.is_async_destructor_noop(tcx, param_env));
+
+        let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer);
+        let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain);
+
+        let noop =
+            Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity();
+        let either = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropEither);
+
+        let variants_dtor = variants
+            .into_iter()
+            .map(|variant| {
+                variant
+                    .into_iter()
+                    .map(|ty| defer.instantiate(tcx, &[ty.into()]))
+                    .reduce(|acc, next| chain.instantiate(tcx, &[acc.into(), next.into()]))
+                    .unwrap_or(noop)
+            })
+            .reduce(|other, matched| {
+                either.instantiate(tcx, &[other.into(), matched.into(), self.into()])
+            })
+            .unwrap();
+
+        let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx, param_env) {
+            Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain)
+                .instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()])
+        } else {
+            variants_dtor
+        };
+
+        Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
+            .instantiate(tcx, &[dtor.into()])
+    }
+
+    fn surface_async_dropper_ty(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+    ) -> Option<Ty<'tcx>> {
+        if self.has_surface_async_drop(tcx, param_env) {
+            Some(LangItem::SurfaceAsyncDropInPlace)
+        } else if self.has_surface_drop(tcx, param_env) {
+            Some(LangItem::AsyncDropSurfaceDropInPlace)
+        } else {
+            None
+        }
+        .map(|dropper| {
+            Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()])
+        })
+    }
+
+    fn async_destructor_combinator(
+        tcx: TyCtxt<'tcx>,
+        lang_item: LangItem,
+    ) -> ty::EarlyBinder<Ty<'tcx>> {
+        tcx.fn_sig(tcx.require_lang_item(lang_item, None))
+            .map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap())
+    }
+
     /// Returns the type of metadata for (potentially fat) pointers to this type,
     /// or the struct tail if the metadata type cannot be determined.
     pub fn ptr_metadata_ty_or_tail(
@@ -2697,7 +2825,7 @@
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 818fb78..a28afcc 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -17,7 +17,7 @@
     def::{DefKind, Res},
     def_id::{DefId, LocalDefId, LocalDefIdMap},
     hir_id::OwnerId,
-    BindingAnnotation, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability,
+    BindingMode, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability,
 };
 use rustc_index::IndexVec;
 use rustc_macros::HashStable;
@@ -78,8 +78,8 @@
 
     adjustments: ItemLocalMap<Vec<ty::adjustment::Adjustment<'tcx>>>,
 
-    /// Stores the actual binding mode for all instances of [`BindingAnnotation`].
-    pat_binding_modes: ItemLocalMap<BindingAnnotation>,
+    /// Stores the actual binding mode for all instances of [`BindingMode`].
+    pat_binding_modes: ItemLocalMap<BindingMode>,
 
     /// Stores the types which were implicitly dereferenced in pattern binding modes
     /// for later usage in THIR lowering. For example,
@@ -413,22 +413,17 @@
         matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _))))
     }
 
-    pub fn extract_binding_mode(
-        &self,
-        s: &Session,
-        id: HirId,
-        sp: Span,
-    ) -> Option<BindingAnnotation> {
+    pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option<BindingMode> {
         self.pat_binding_modes().get(id).copied().or_else(|| {
             s.dcx().span_bug(sp, "missing binding mode");
         })
     }
 
-    pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingAnnotation> {
+    pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingMode> {
         LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_binding_modes }
     }
 
-    pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingAnnotation> {
+    pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingMode> {
         LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes }
     }
 
@@ -460,7 +455,7 @@
         let mut has_ref_mut = false;
         pat.walk(|pat| {
             if let hir::PatKind::Binding(_, id, _, _) = pat.kind
-                && let Some(BindingAnnotation(ByRef::Yes(Mutability::Mut), _)) =
+                && let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) =
                     self.pat_binding_modes().get(id)
             {
                 has_ref_mut = true;
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 9af665c..42e0565 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -432,19 +432,19 @@
             .filter(|&(_, k)| {
                 match k.unpack() {
                     GenericArgKind::Lifetime(region) => match region.kind() {
-                        ty::ReEarlyParam(ref ebr) => {
+                        ty::ReEarlyParam(ebr) => {
                             !impl_generics.region_param(ebr, self).pure_wrt_drop
                         }
                         // Error: not a region param
                         _ => false,
                     },
-                    GenericArgKind::Type(ty) => match ty.kind() {
-                        ty::Param(ref pt) => !impl_generics.type_param(pt, self).pure_wrt_drop,
+                    GenericArgKind::Type(ty) => match *ty.kind() {
+                        ty::Param(pt) => !impl_generics.type_param(pt, self).pure_wrt_drop,
                         // Error: not a type param
                         _ => false,
                     },
                     GenericArgKind::Const(ct) => match ct.kind() {
-                        ty::ConstKind::Param(ref pc) => {
+                        ty::ConstKind::Param(pc) => {
                             !impl_generics.const_param(pc, self).pure_wrt_drop
                         }
                         // Error: not a const param
@@ -1303,6 +1303,98 @@
         }
     }
 
+    /// Checks whether values of this type `T` implements the `AsyncDrop`
+    /// trait.
+    pub fn has_surface_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
+        self.could_have_surface_async_drop() && tcx.has_surface_async_drop_raw(param_env.and(self))
+    }
+
+    /// Fast path helper for testing if a type has `AsyncDrop`
+    /// implementation.
+    ///
+    /// Returning `false` means the type is known to not have `AsyncDrop`
+    /// implementation. Returning `true` means nothing -- could be
+    /// `AsyncDrop`, might not be.
+    fn could_have_surface_async_drop(self) -> bool {
+        !self.is_async_destructor_trivially_noop()
+            && !matches!(
+                self.kind(),
+                ty::Tuple(_)
+                    | ty::Slice(_)
+                    | ty::Array(_, _)
+                    | ty::Closure(..)
+                    | ty::CoroutineClosure(..)
+                    | ty::Coroutine(..)
+            )
+    }
+
+    /// Checks whether values of this type `T` implements the `Drop`
+    /// trait.
+    pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
+        self.could_have_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self))
+    }
+
+    /// Fast path helper for testing if a type has `Drop` implementation.
+    ///
+    /// Returning `false` means the type is known to not have `Drop`
+    /// implementation. Returning `true` means nothing -- could be
+    /// `Drop`, might not be.
+    fn could_have_surface_drop(self) -> bool {
+        !self.is_async_destructor_trivially_noop()
+            && !matches!(
+                self.kind(),
+                ty::Tuple(_)
+                    | ty::Slice(_)
+                    | ty::Array(_, _)
+                    | ty::Closure(..)
+                    | ty::CoroutineClosure(..)
+                    | ty::Coroutine(..)
+            )
+    }
+
+    /// Checks whether values of this type `T` implement has noop async destructor.
+    //
+    // FIXME: implement optimization to make ADTs, which do not need drop,
+    // to skip fields or to have noop async destructor.
+    pub fn is_async_destructor_noop(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> bool {
+        self.is_async_destructor_trivially_noop()
+            || if let ty::Adt(adt_def, _) = self.kind() {
+                (adt_def.is_union() || adt_def.is_payloadfree())
+                    && !self.has_surface_async_drop(tcx, param_env)
+                    && !self.has_surface_drop(tcx, param_env)
+            } else {
+                false
+            }
+    }
+
+    /// Fast path helper for testing if a type has noop async destructor.
+    ///
+    /// Returning `true` means the type is known to have noop async destructor
+    /// implementation. Returning `true` means nothing -- could be
+    /// `Drop`, might not be.
+    fn is_async_destructor_trivially_noop(self) -> bool {
+        match self.kind() {
+            ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Bool
+            | ty::Char
+            | ty::Str
+            | ty::Never
+            | ty::Ref(..)
+            | ty::RawPtr(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(_) => true,
+            ty::Tuple(tys) => tys.is_empty(),
+            ty::Adt(adt_def, _) => adt_def.is_manually_drop(),
+            _ => false,
+        }
+    }
+
     /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely
     /// non-copy and *might* have a destructor attached; if it returns
     /// `false`, then `ty` definitely has no destructor (i.e., no drop glue).
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 1de691f..34440c6 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -97,6 +97,8 @@
     .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
     .label = dereference of raw pointer
 
+mir_build_exceeds_mcdc_condition_num_limit =  Conditions number of the decision ({$conditions_num}) exceeds limit ({$max_conditions_num}). MCDC analysis will not count this expression.
+
 mir_build_extern_static_requires_unsafe =
     use of extern static is unsafe and requires unsafe block
     .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs
index ab00439..9e9ccd3 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo.rs
+++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs
@@ -1,14 +1,20 @@
 use std::assert_matches::assert_matches;
 use std::collections::hash_map::Entry;
+use std::collections::VecDeque;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
-use rustc_middle::mir::{self, BasicBlock, UnOp};
-use rustc_middle::thir::{ExprId, ExprKind, Thir};
+use rustc_middle::mir::coverage::{
+    BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageKind, MCDCBranchSpan,
+    MCDCDecisionSpan,
+};
+use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
+use rustc_middle::thir::{ExprId, ExprKind, LogicalOp, Thir};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::LocalDefId;
+use rustc_span::Span;
 
-use crate::build::Builder;
+use crate::build::{Builder, CFG};
+use crate::errors::MCDCExceedsConditionNumLimit;
 
 pub(crate) struct BranchInfoBuilder {
     /// Maps condition expressions to their enclosing `!`, for better instrumentation.
@@ -16,6 +22,10 @@
 
     num_block_markers: usize,
     branch_spans: Vec<BranchSpan>,
+
+    mcdc_branch_spans: Vec<MCDCBranchSpan>,
+    mcdc_decision_spans: Vec<MCDCDecisionSpan>,
+    mcdc_state: Option<MCDCState>,
 }
 
 #[derive(Clone, Copy)]
@@ -33,7 +43,14 @@
     /// is enabled and `def_id` represents a function that is eligible for coverage.
     pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Self> {
         if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) {
-            Some(Self { nots: FxHashMap::default(), num_block_markers: 0, branch_spans: vec![] })
+            Some(Self {
+                nots: FxHashMap::default(),
+                num_block_markers: 0,
+                branch_spans: vec![],
+                mcdc_branch_spans: vec![],
+                mcdc_decision_spans: vec![],
+                mcdc_state: MCDCState::new_if_enabled(tcx),
+            })
         } else {
             None
         }
@@ -79,21 +96,242 @@
         }
     }
 
+    fn fetch_mcdc_condition_info(
+        &mut self,
+        tcx: TyCtxt<'_>,
+        true_marker: BlockMarkerId,
+        false_marker: BlockMarkerId,
+    ) -> Option<ConditionInfo> {
+        let mcdc_state = self.mcdc_state.as_mut()?;
+        let (mut condition_info, decision_result) =
+            mcdc_state.take_condition(true_marker, false_marker);
+        if let Some(decision) = decision_result {
+            match decision.conditions_num {
+                0 => {
+                    unreachable!("Decision with no condition is not expected");
+                }
+                1..=MAX_CONDITIONS_NUM_IN_DECISION => {
+                    self.mcdc_decision_spans.push(decision);
+                }
+                _ => {
+                    // Do not generate mcdc mappings and statements for decisions with too many conditions.
+                    let rebase_idx = self.mcdc_branch_spans.len() - decision.conditions_num + 1;
+                    for branch in &mut self.mcdc_branch_spans[rebase_idx..] {
+                        branch.condition_info = None;
+                    }
+
+                    // ConditionInfo of this branch shall also be reset.
+                    condition_info = None;
+
+                    tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit {
+                        span: decision.span,
+                        conditions_num: decision.conditions_num,
+                        max_conditions_num: MAX_CONDITIONS_NUM_IN_DECISION,
+                    });
+                }
+            }
+        }
+        condition_info
+    }
+
+    fn add_two_way_branch<'tcx>(
+        &mut self,
+        cfg: &mut CFG<'tcx>,
+        source_info: SourceInfo,
+        true_block: BasicBlock,
+        false_block: BasicBlock,
+    ) {
+        let true_marker = self.inject_block_marker(cfg, source_info, true_block);
+        let false_marker = self.inject_block_marker(cfg, source_info, false_block);
+
+        self.branch_spans.push(BranchSpan { span: source_info.span, true_marker, false_marker });
+    }
+
     fn next_block_marker_id(&mut self) -> BlockMarkerId {
         let id = BlockMarkerId::from_usize(self.num_block_markers);
         self.num_block_markers += 1;
         id
     }
 
+    fn inject_block_marker(
+        &mut self,
+        cfg: &mut CFG<'_>,
+        source_info: SourceInfo,
+        block: BasicBlock,
+    ) -> BlockMarkerId {
+        let id = self.next_block_marker_id();
+
+        let marker_statement = mir::Statement {
+            source_info,
+            kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }),
+        };
+        cfg.push(block, marker_statement);
+
+        id
+    }
+
     pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
-        let Self { nots: _, num_block_markers, branch_spans } = self;
+        let Self {
+            nots: _,
+            num_block_markers,
+            branch_spans,
+            mcdc_branch_spans,
+            mcdc_decision_spans,
+            mcdc_state: _,
+        } = self;
 
         if num_block_markers == 0 {
             assert!(branch_spans.is_empty());
             return None;
         }
 
-        Some(Box::new(mir::coverage::BranchInfo { num_block_markers, branch_spans }))
+        Some(Box::new(mir::coverage::BranchInfo {
+            num_block_markers,
+            branch_spans,
+            mcdc_branch_spans,
+            mcdc_decision_spans,
+        }))
+    }
+}
+
+/// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen,
+/// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge.
+/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
+const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
+
+struct MCDCState {
+    /// To construct condition evaluation tree.
+    decision_stack: VecDeque<ConditionInfo>,
+    processing_decision: Option<MCDCDecisionSpan>,
+}
+
+impl MCDCState {
+    fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
+        tcx.sess
+            .instrument_coverage_mcdc()
+            .then(|| Self { decision_stack: VecDeque::new(), processing_decision: None })
+    }
+
+    // At first we assign ConditionIds for each sub expression.
+    // If the sub expression is composite, re-assign its ConditionId to its LHS and generate a new ConditionId for its RHS.
+    //
+    // Example: "x = (A && B) || (C && D) || (D && F)"
+    //
+    //      Visit Depth1:
+    //              (A && B) || (C && D) || (D && F)
+    //              ^-------LHS--------^    ^-RHS--^
+    //                      ID=1              ID=2
+    //
+    //      Visit LHS-Depth2:
+    //              (A && B) || (C && D)
+    //              ^-LHS--^    ^-RHS--^
+    //                ID=1        ID=3
+    //
+    //      Visit LHS-Depth3:
+    //               (A && B)
+    //               LHS   RHS
+    //               ID=1  ID=4
+    //
+    //      Visit RHS-Depth3:
+    //                         (C && D)
+    //                         LHS   RHS
+    //                         ID=3  ID=5
+    //
+    //      Visit RHS-Depth2:              (D && F)
+    //                                     LHS   RHS
+    //                                     ID=2  ID=6
+    //
+    //      Visit Depth1:
+    //              (A && B)  || (C && D)  || (D && F)
+    //              ID=1  ID=4   ID=3  ID=5   ID=2  ID=6
+    //
+    // A node ID of '0' always means MC/DC isn't being tracked.
+    //
+    // If a "next" node ID is '0', it means it's the end of the test vector.
+    //
+    // As the compiler tracks expression in pre-order, we can ensure that condition info of parents are always properly assigned when their children are visited.
+    // - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
+    // - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
+    fn record_conditions(&mut self, op: LogicalOp, span: Span) {
+        let decision = match self.processing_decision.as_mut() {
+            Some(decision) => {
+                decision.span = decision.span.to(span);
+                decision
+            }
+            None => self.processing_decision.insert(MCDCDecisionSpan {
+                span,
+                conditions_num: 0,
+                end_markers: vec![],
+            }),
+        };
+
+        let parent_condition = self.decision_stack.pop_back().unwrap_or_default();
+        let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
+            decision.conditions_num += 1;
+            ConditionId::from(decision.conditions_num)
+        } else {
+            parent_condition.condition_id
+        };
+
+        decision.conditions_num += 1;
+        let rhs_condition_id = ConditionId::from(decision.conditions_num);
+
+        let (lhs, rhs) = match op {
+            LogicalOp::And => {
+                let lhs = ConditionInfo {
+                    condition_id: lhs_id,
+                    true_next_id: rhs_condition_id,
+                    false_next_id: parent_condition.false_next_id,
+                };
+                let rhs = ConditionInfo {
+                    condition_id: rhs_condition_id,
+                    true_next_id: parent_condition.true_next_id,
+                    false_next_id: parent_condition.false_next_id,
+                };
+                (lhs, rhs)
+            }
+            LogicalOp::Or => {
+                let lhs = ConditionInfo {
+                    condition_id: lhs_id,
+                    true_next_id: parent_condition.true_next_id,
+                    false_next_id: rhs_condition_id,
+                };
+                let rhs = ConditionInfo {
+                    condition_id: rhs_condition_id,
+                    true_next_id: parent_condition.true_next_id,
+                    false_next_id: parent_condition.false_next_id,
+                };
+                (lhs, rhs)
+            }
+        };
+        // We visit expressions tree in pre-order, so place the left-hand side on the top.
+        self.decision_stack.push_back(rhs);
+        self.decision_stack.push_back(lhs);
+    }
+
+    fn take_condition(
+        &mut self,
+        true_marker: BlockMarkerId,
+        false_marker: BlockMarkerId,
+    ) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
+        let Some(condition_info) = self.decision_stack.pop_back() else {
+            return (None, None);
+        };
+        let Some(decision) = self.processing_decision.as_mut() else {
+            bug!("Processing decision should have been created before any conditions are taken");
+        };
+        if condition_info.true_next_id == ConditionId::NONE {
+            decision.end_markers.push(true_marker);
+        }
+        if condition_info.false_next_id == ConditionId::NONE {
+            decision.end_markers.push(false_marker);
+        }
+
+        if self.decision_stack.is_empty() {
+            (Some(condition_info), self.processing_decision.take())
+        } else {
+            (Some(condition_info), None)
+        }
     }
 }
 
@@ -107,7 +345,7 @@
         mut else_block: BasicBlock,
     ) {
         // Bail out if branch coverage is not enabled for this function.
-        let Some(branch_info) = self.coverage_branch_info.as_ref() else { return };
+        let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
 
         // If this condition expression is nested within one or more `!` expressions,
         // replace it with the enclosing `!` collected by `visit_unary_not`.
@@ -117,30 +355,34 @@
                 std::mem::swap(&mut then_block, &mut else_block);
             }
         }
-        let source_info = self.source_info(self.thir[expr_id].span);
 
-        // Now that we have `source_info`, we can upgrade to a &mut reference.
-        let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut");
+        let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };
 
-        let mut inject_branch_marker = |block: BasicBlock| {
-            let id = branch_info.next_block_marker_id();
+        // Separate path for handling branches when MC/DC is enabled.
+        if branch_info.mcdc_state.is_some() {
+            let mut inject_block_marker =
+                |block| branch_info.inject_block_marker(&mut self.cfg, source_info, block);
+            let true_marker = inject_block_marker(then_block);
+            let false_marker = inject_block_marker(else_block);
+            let condition_info =
+                branch_info.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker);
+            branch_info.mcdc_branch_spans.push(MCDCBranchSpan {
+                span: source_info.span,
+                condition_info,
+                true_marker,
+                false_marker,
+            });
+            return;
+        }
 
-            let marker_statement = mir::Statement {
-                source_info,
-                kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }),
-            };
-            self.cfg.push(block, marker_statement);
+        branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block);
+    }
 
-            id
-        };
-
-        let true_marker = inject_branch_marker(then_block);
-        let false_marker = inject_branch_marker(else_block);
-
-        branch_info.branch_spans.push(BranchSpan {
-            span: source_info.span,
-            true_marker,
-            false_marker,
-        });
+    pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) {
+        if let Some(branch_info) = self.coverage_branch_info.as_mut()
+            && let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
+        {
+            mcdc_state.record_conditions(logical_op, span);
+        }
     }
 }
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 367c391..f46dcee 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -14,7 +14,7 @@
     fx::{FxHashSet, FxIndexMap, FxIndexSet},
     stack::ensure_sufficient_stack,
 };
-use rustc_hir::{BindingAnnotation, ByRef};
+use rustc_hir::{BindingMode, ByRef};
 use rustc_middle::middle::region;
 use rustc_middle::mir::{self, *};
 use rustc_middle::thir::{self, *};
@@ -77,11 +77,13 @@
 
         match expr.kind {
             ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
+                this.visit_coverage_branch_operation(LogicalOp::And, expr_span);
                 let lhs_then_block = unpack!(this.then_else_break_inner(block, lhs, args));
                 let rhs_then_block = unpack!(this.then_else_break_inner(lhs_then_block, rhs, args));
                 rhs_then_block.unit()
             }
             ExprKind::LogicalOp { op: LogicalOp::Or, lhs, rhs } => {
+                this.visit_coverage_branch_operation(LogicalOp::Or, expr_span);
                 let local_scope = this.local_scope();
                 let (lhs_success_block, failure_block) =
                     this.in_if_then_scope(local_scope, expr_span, |this| {
@@ -385,15 +387,15 @@
         let fake_borrows = match_has_guard
             .then(|| util::FakeBorrowCollector::collect_fake_borrows(self, candidates));
 
+        // See the doc comment on `match_candidates` for why we have an
+        // otherwise block. Match checking will ensure this is actually
+        // unreachable.
         let otherwise_block = self.cfg.start_new_block();
 
         // This will generate code to test scrutinee_place and
         // branch to the appropriate arm block
         self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates);
 
-        // See the doc comment on `match_candidates` for why we may have an
-        // otherwise block. Match checking will ensure this is actually
-        // unreachable.
         let source_info = self.source_info(scrutinee_span);
 
         // Matching on a `scrutinee_place` with an uninhabited type doesn't
@@ -621,12 +623,7 @@
     ) -> BlockAnd<()> {
         match irrefutable_pat.kind {
             // Optimize the case of `let x = ...` to write directly into `x`
-            PatKind::Binding {
-                mode: BindingAnnotation(ByRef::No, _),
-                var,
-                subpattern: None,
-                ..
-            } => {
+            PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => {
                 let place =
                     self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
                 unpack!(block = self.expr_into_dest(place, block, initializer_id));
@@ -652,7 +649,7 @@
                     box Pat {
                         kind:
                             PatKind::Binding {
-                                mode: BindingAnnotation(ByRef::No, _),
+                                mode: BindingMode(ByRef::No, _),
                                 var,
                                 subpattern: None,
                                 ..
@@ -893,7 +890,7 @@
         f: &mut impl FnMut(
             &mut Self,
             Symbol,
-            BindingAnnotation,
+            BindingMode,
             LocalVarId,
             Span,
             Ty<'tcx>,
@@ -1148,7 +1145,7 @@
     span: Span,
     source: Place<'tcx>,
     var_id: LocalVarId,
-    binding_mode: BindingAnnotation,
+    binding_mode: BindingMode,
 }
 
 /// Indicates that the type of `source` must be a subtype of the
@@ -2412,7 +2409,7 @@
         source_info: SourceInfo,
         visibility_scope: SourceScope,
         name: Symbol,
-        mode: BindingAnnotation,
+        mode: BindingMode,
         var_id: LocalVarId,
         var_ty: Ty<'tcx>,
         user_ty: UserTypeProjections,
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index b5d7261..3c18afe 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -9,7 +9,7 @@
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId, Node};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Node};
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@@ -931,7 +931,7 @@
                 // Don't introduce extra copies for simple bindings
                 PatKind::Binding {
                     var,
-                    mode: BindingAnnotation(ByRef::No, mutability),
+                    mode: BindingMode(ByRef::No, mutability),
                     subpattern: None,
                     ..
                 } => {
@@ -941,7 +941,7 @@
                         if let Some(kind) = param.self_kind {
                             LocalInfo::User(BindingForm::ImplicitSelf(kind))
                         } else {
-                            let binding_mode = BindingAnnotation(ByRef::No, mutability);
+                            let binding_mode = BindingMode(ByRef::No, mutability);
                             LocalInfo::User(BindingForm::Var(VarBindingForm {
                                 binding_mode,
                                 opt_ty_info: param.ty_span,
@@ -1023,7 +1023,13 @@
     let num = num.as_str();
     match float_ty {
         // FIXME(f16_f128): When available, compare to the library parser as with `f32` and `f64`
-        ty::FloatTy::F16 => num.parse::<Half>().ok().map(Scalar::from_f16),
+        ty::FloatTy::F16 => {
+            let mut f = num.parse::<Half>().ok()?;
+            if neg {
+                f = -f;
+            }
+            Some(Scalar::from_f16(f))
+        }
         ty::FloatTy::F32 => {
             let Ok(rust_f) = num.parse::<f32>() else { return None };
             let mut f = num
@@ -1071,7 +1077,13 @@
             Some(Scalar::from_f64(f))
         }
         // FIXME(f16_f128): When available, compare to the library parser as with `f32` and `f64`
-        ty::FloatTy::F128 => num.parse::<Quad>().ok().map(Scalar::from_f128),
+        ty::FloatTy::F128 => {
+            let mut f = num.parse::<Quad>().ok()?;
+            if neg {
+                f = -f;
+            }
+            Some(Scalar::from_f128(f))
+        }
     }
 }
 
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 9ee0fb7..0c1e1d5 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -2,7 +2,7 @@
 use crate::errors::*;
 
 use rustc_errors::DiagArgValue;
-use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId, Mutability};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability};
 use rustc_middle::mir::BorrowKind;
 use rustc_middle::thir::visit::Visitor;
 use rustc_middle::thir::*;
@@ -288,7 +288,7 @@
                     visit::walk_pat(self, pat);
                 }
             }
-            PatKind::Binding { mode: BindingAnnotation(ByRef::Yes(rm), _), ty, .. } => {
+            PatKind::Binding { mode: BindingMode(ByRef::Yes(rm), _), ty, .. } => {
                 if self.inside_adt {
                     let ty::Ref(_, ty, _) = ty.kind() else {
                         span_bug!(
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 26f10fd..9ddfb12 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -819,6 +819,15 @@
 }
 
 #[derive(Diagnostic)]
+#[diag(mir_build_exceeds_mcdc_condition_num_limit)]
+pub(crate) struct MCDCExceedsConditionNumLimit {
+    #[primary_span]
+    pub span: Span,
+    pub conditions_num: usize,
+    pub max_conditions_num: usize,
+}
+
+#[derive(Diagnostic)]
 #[diag(mir_build_pattern_not_covered, code = E0005)]
 pub(crate) struct PatternNotCovered<'s, 'tcx> {
     #[primary_span]
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 03195a1..241d38f 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -15,7 +15,7 @@
 };
 use rustc_hir::def::*;
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId};
 use rustc_middle::middle::limits::get_limit_size;
 use rustc_middle::thir::visit::Visitor;
 use rustc_middle::thir::*;
@@ -839,7 +839,7 @@
 ) {
     if let PatKind::Binding {
         name,
-        mode: BindingAnnotation(ByRef::No, Mutability::Not),
+        mode: BindingMode(ByRef::No, Mutability::Not),
         subpattern: None,
         ty,
         ..
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index d382d2c..0b15c52 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -9,7 +9,7 @@
 
 use self::counters::{CounterIncrementSite, CoverageCounters};
 use self::graph::{BasicCoverageBlock, CoverageGraph};
-use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
+use self::spans::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans};
 
 use crate::MirPass;
 
@@ -100,9 +100,12 @@
         &coverage_counters,
     );
 
+    inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
+
     mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
         function_source_hash: hir_info.function_source_hash,
         num_counters: coverage_counters.num_counters(),
+        mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
         expressions: coverage_counters.into_expressions(),
         mappings,
     }));
@@ -136,20 +139,47 @@
             .as_term()
     };
 
-    coverage_spans
-        .all_bcb_mappings()
-        .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| {
-            let kind = match bcb_mapping_kind {
+    let mut mappings = Vec::new();
+
+    mappings.extend(coverage_spans.mappings.iter().filter_map(
+        |BcbMapping { kind: bcb_mapping_kind, span }| {
+            let kind = match *bcb_mapping_kind {
                 BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
-                BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch {
+                BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => {
+                    MappingKind::Branch {
+                        true_term: term_for_bcb(true_bcb),
+                        false_term: term_for_bcb(false_bcb),
+                    }
+                }
+                BcbMappingKind::MCDCBranch {
+                    true_bcb,
+                    false_bcb,
+                    condition_info: Some(mcdc_params),
+                } => MappingKind::MCDCBranch {
                     true_term: term_for_bcb(true_bcb),
                     false_term: term_for_bcb(false_bcb),
+                    mcdc_params,
                 },
+                BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => {
+                    MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, conditions_num })
+                }
             };
+            let code_region = make_code_region(source_map, file_name, *span, body_span)?;
+            Some(Mapping { kind, code_region })
+        },
+    ));
+
+    mappings.extend(coverage_spans.branch_pairs.iter().filter_map(
+        |&BcbBranchPair { span, true_bcb, false_bcb }| {
+            let true_term = term_for_bcb(true_bcb);
+            let false_term = term_for_bcb(false_bcb);
+            let kind = MappingKind::Branch { true_term, false_term };
             let code_region = make_code_region(source_map, file_name, span, body_span)?;
             Some(Mapping { kind, code_region })
-        })
-        .collect::<Vec<_>>()
+        },
+    ));
+
+    mappings
 }
 
 /// For each BCB node or BCB edge that has an associated coverage counter,
@@ -204,6 +234,55 @@
     }
 }
 
+/// For each conditions inject statements to update condition bitmap after it has been evaluated.
+/// For each decision inject statements to update test vector bitmap after it has been evaluated.
+fn inject_mcdc_statements<'tcx>(
+    mir_body: &mut mir::Body<'tcx>,
+    basic_coverage_blocks: &CoverageGraph,
+    coverage_spans: &CoverageSpans,
+) {
+    if coverage_spans.test_vector_bitmap_bytes() == 0 {
+        return;
+    }
+
+    // Inject test vector update first because `inject_statement` always insert new statement at head.
+    for (end_bcbs, bitmap_idx) in
+        coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind {
+            BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, .. } => {
+                Some((end_bcbs, *bitmap_idx))
+            }
+            _ => None,
+        })
+    {
+        for end in end_bcbs {
+            let end_bb = basic_coverage_blocks[*end].leader_bb();
+            inject_statement(mir_body, CoverageKind::TestVectorBitmapUpdate { bitmap_idx }, end_bb);
+        }
+    }
+
+    for (true_bcb, false_bcb, condition_id) in
+        coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind {
+            BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => {
+                Some((true_bcb, false_bcb, condition_info?.condition_id))
+            }
+            _ => None,
+        })
+    {
+        let true_bb = basic_coverage_blocks[true_bcb].leader_bb();
+        inject_statement(
+            mir_body,
+            CoverageKind::CondBitmapUpdate { id: condition_id, value: true },
+            true_bb,
+        );
+        let false_bb = basic_coverage_blocks[false_bcb].leader_bb();
+        inject_statement(
+            mir_body,
+            CoverageKind::CondBitmapUpdate { id: condition_id, value: false },
+            false_bb,
+        );
+    }
+}
+
 /// Given two basic blocks that have a control-flow edge between them, creates
 /// and returns a new block that sits between those blocks.
 fn inject_edge_counter_basic_block(
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index 6571525..f77ee63 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -61,7 +61,17 @@
         .max()
         .unwrap_or(CounterId::ZERO);
 
-    CoverageIdsInfo { max_counter_id }
+    let mcdc_bitmap_bytes = mir_body
+        .coverage_branch_info
+        .as_deref()
+        .map(|info| {
+            info.mcdc_decision_spans
+                .iter()
+                .fold(0, |acc, decision| acc + (1_u32 << decision.conditions_num).div_ceil(8))
+        })
+        .unwrap_or_default();
+
+    CoverageIdsInfo { max_counter_id, mcdc_bitmap_bytes }
 }
 
 fn all_coverage_in_mir_body<'a, 'tcx>(
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 03ede88..88f18b7 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,7 +1,9 @@
 use rustc_data_structures::graph::DirectedGraph;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir;
+use rustc_middle::mir::coverage::ConditionInfo;
 use rustc_span::{BytePos, Span};
+use std::collections::BTreeSet;
 
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
 use crate::coverage::spans::from_mir::SpanFromMir;
@@ -9,12 +11,24 @@
 
 mod from_mir;
 
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Debug)]
 pub(super) enum BcbMappingKind {
     /// Associates an ordinary executable code span with its corresponding BCB.
     Code(BasicCoverageBlock),
-    /// Associates a branch span with BCBs for its true and false arms.
-    Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
+
+    // Ordinary branch mappings are stored separately, so they don't have a
+    // variant in this enum.
+    //
+    /// Associates a mcdc branch span with condition info besides fields for normal branch.
+    MCDCBranch {
+        true_bcb: BasicCoverageBlock,
+        false_bcb: BasicCoverageBlock,
+        /// If `None`, this actually represents a normal branch mapping inserted
+        /// for code that was too complex for MC/DC.
+        condition_info: Option<ConditionInfo>,
+    },
+    /// Associates a mcdc decision with its join BCB.
+    MCDCDecision { end_bcbs: BTreeSet<BasicCoverageBlock>, bitmap_idx: u32, conditions_num: u16 },
 }
 
 #[derive(Debug)]
@@ -23,9 +37,21 @@
     pub(super) span: Span,
 }
 
+/// This is separate from [`BcbMappingKind`] to help prepare for larger changes
+/// that will be needed for improved branch coverage in the future.
+/// (See <https://github.com/rust-lang/rust/pull/124217>.)
+#[derive(Debug)]
+pub(super) struct BcbBranchPair {
+    pub(super) span: Span,
+    pub(super) true_bcb: BasicCoverageBlock,
+    pub(super) false_bcb: BasicCoverageBlock,
+}
+
 pub(super) struct CoverageSpans {
     bcb_has_mappings: BitSet<BasicCoverageBlock>,
-    mappings: Vec<BcbMapping>,
+    pub(super) mappings: Vec<BcbMapping>,
+    pub(super) branch_pairs: Vec<BcbBranchPair>,
+    test_vector_bitmap_bytes: u32,
 }
 
 impl CoverageSpans {
@@ -33,8 +59,8 @@
         self.bcb_has_mappings.contains(bcb)
     }
 
-    pub(super) fn all_bcb_mappings(&self) -> impl Iterator<Item = &BcbMapping> {
-        self.mappings.iter()
+    pub(super) fn test_vector_bitmap_bytes(&self) -> u32 {
+        self.test_vector_bitmap_bytes
     }
 }
 
@@ -48,6 +74,7 @@
     basic_coverage_blocks: &CoverageGraph,
 ) -> Option<CoverageSpans> {
     let mut mappings = vec![];
+    let mut branch_pairs = vec![];
 
     if hir_info.is_async_fn {
         // An async function desugars into a function that returns a future,
@@ -69,14 +96,20 @@
             BcbMapping { kind: BcbMappingKind::Code(bcb), span }
         }));
 
-        mappings.extend(from_mir::extract_branch_mappings(
+        branch_pairs.extend(from_mir::extract_branch_pairs(
+            mir_body,
+            hir_info,
+            basic_coverage_blocks,
+        ));
+
+        mappings.extend(from_mir::extract_mcdc_mappings(
             mir_body,
             hir_info.body_span,
             basic_coverage_blocks,
         ));
     }
 
-    if mappings.is_empty() {
+    if mappings.is_empty() && branch_pairs.is_empty() {
         return None;
     }
 
@@ -85,17 +118,29 @@
     let mut insert = |bcb| {
         bcb_has_mappings.insert(bcb);
     };
-    for &BcbMapping { kind, span: _ } in &mappings {
-        match kind {
+    let mut test_vector_bitmap_bytes = 0;
+    for BcbMapping { kind, span: _ } in &mappings {
+        match *kind {
             BcbMappingKind::Code(bcb) => insert(bcb),
-            BcbMappingKind::Branch { true_bcb, false_bcb } => {
+            BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => {
                 insert(true_bcb);
                 insert(false_bcb);
             }
+            BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => {
+                // `bcb_has_mappings` is used for inject coverage counters
+                // but they are not needed for decision BCBs.
+                // While the length of test vector bitmap should be calculated here.
+                test_vector_bitmap_bytes = test_vector_bitmap_bytes
+                    .max(bitmap_idx + (1_u32 << conditions_num as u32).div_ceil(8));
+            }
         }
     }
+    for &BcbBranchPair { true_bcb, false_bcb, .. } in &branch_pairs {
+        insert(true_bcb);
+        insert(false_bcb);
+    }
 
-    Some(CoverageSpans { bcb_has_mappings, mappings })
+    Some(CoverageSpans { bcb_has_mappings, mappings, branch_pairs, test_vector_bitmap_bytes })
 }
 
 #[derive(Debug)]
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index adb0c9f..64f21d7 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -1,7 +1,9 @@
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_index::IndexVec;
-use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
+use rustc_middle::mir::coverage::{
+    BlockMarkerId, BranchSpan, CoverageKind, MCDCBranchSpan, MCDCDecisionSpan,
+};
 use rustc_middle::mir::{
     self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
     TerminatorKind,
@@ -11,7 +13,7 @@
 use crate::coverage::graph::{
     BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
 };
-use crate::coverage::spans::{BcbMapping, BcbMappingKind};
+use crate::coverage::spans::{BcbBranchPair, BcbMapping, BcbMappingKind};
 use crate::coverage::ExtractedHirInfo;
 
 /// Traverses the MIR body to produce an initial collection of coverage-relevant
@@ -227,7 +229,10 @@
 
         // These coverage statements should not exist prior to coverage instrumentation.
         StatementKind::Coverage(
-            CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. },
+            CoverageKind::CounterIncrement { .. }
+            | CoverageKind::ExpressionUsed { .. }
+            | CoverageKind::CondBitmapUpdate { .. }
+            | CoverageKind::TestVectorBitmapUpdate { .. },
         ) => bug!(
             "Unexpected coverage statement found during coverage instrumentation: {statement:?}"
         ),
@@ -361,15 +366,10 @@
     }
 }
 
-pub(super) fn extract_branch_mappings(
+fn resolve_block_markers(
+    branch_info: &mir::coverage::BranchInfo,
     mir_body: &mir::Body<'_>,
-    body_span: Span,
-    basic_coverage_blocks: &CoverageGraph,
-) -> Vec<BcbMapping> {
-    let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
-        return vec![];
-    };
-
+) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
     let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
         None,
         branch_info.num_block_markers,
@@ -384,6 +384,24 @@
         }
     }
 
+    block_markers
+}
+
+// FIXME: There is currently a lot of redundancy between
+// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so
+// that they can each be modified without interfering with the other, but in
+// the long term we should try to bring them together again when branch coverage
+// and MC/DC coverage support are more mature.
+
+pub(super) fn extract_branch_pairs(
+    mir_body: &mir::Body<'_>,
+    hir_info: &ExtractedHirInfo,
+    basic_coverage_blocks: &CoverageGraph,
+) -> Vec<BcbBranchPair> {
+    let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] };
+
+    let block_markers = resolve_block_markers(branch_info, mir_body);
+
     branch_info
         .branch_spans
         .iter()
@@ -393,7 +411,8 @@
             if !raw_span.ctxt().outer_expn_data().is_root() {
                 return None;
             }
-            let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
+            let (span, _) =
+                unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?;
 
             let bcb_from_marker =
                 |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
@@ -401,7 +420,75 @@
             let true_bcb = bcb_from_marker(true_marker)?;
             let false_bcb = bcb_from_marker(false_marker)?;
 
-            Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span })
+            Some(BcbBranchPair { span, true_bcb, false_bcb })
         })
         .collect::<Vec<_>>()
 }
+
+pub(super) fn extract_mcdc_mappings(
+    mir_body: &mir::Body<'_>,
+    body_span: Span,
+    basic_coverage_blocks: &CoverageGraph,
+) -> Vec<BcbMapping> {
+    let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
+        return vec![];
+    };
+
+    let block_markers = resolve_block_markers(branch_info, mir_body);
+
+    let bcb_from_marker =
+        |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
+
+    let check_branch_bcb =
+        |raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| {
+            // For now, ignore any branch span that was introduced by
+            // expansion. This makes things like assert macros less noisy.
+            if !raw_span.ctxt().outer_expn_data().is_root() {
+                return None;
+            }
+            let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
+
+            let true_bcb = bcb_from_marker(true_marker)?;
+            let false_bcb = bcb_from_marker(false_marker)?;
+            Some((span, true_bcb, false_bcb))
+        };
+
+    let mcdc_branch_filter_map =
+        |&MCDCBranchSpan { span: raw_span, true_marker, false_marker, condition_info }| {
+            check_branch_bcb(raw_span, true_marker, false_marker).map(
+                |(span, true_bcb, false_bcb)| BcbMapping {
+                    kind: BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info },
+                    span,
+                },
+            )
+        };
+
+    let mut next_bitmap_idx = 0;
+
+    let decision_filter_map = |decision: &MCDCDecisionSpan| {
+        let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?;
+
+        let end_bcbs = decision
+            .end_markers
+            .iter()
+            .map(|&marker| bcb_from_marker(marker))
+            .collect::<Option<_>>()?;
+
+        let bitmap_idx = next_bitmap_idx;
+        next_bitmap_idx += (1_u32 << decision.conditions_num).div_ceil(8);
+
+        Some(BcbMapping {
+            kind: BcbMappingKind::MCDCDecision {
+                end_bcbs,
+                bitmap_idx,
+                conditions_num: decision.conditions_num as u16,
+            },
+            span,
+        })
+    };
+
+    std::iter::empty()
+        .chain(branch_info.mcdc_branch_spans.iter().filter_map(mcdc_branch_filter_map))
+        .chain(branch_info.mcdc_decision_spans.iter().filter_map(decision_filter_map))
+        .collect::<Vec<_>>()
+}
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 8e8d782..2483208 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -885,6 +885,7 @@
                 AggregateKind::Adt(did, ..) => tcx.def_kind(did) != DefKind::Enum,
                 // Coroutines are never ZST, as they at least contain the implicit states.
                 AggregateKind::Coroutine(..) => false,
+                AggregateKind::RawPtr(..) => bug!("MIR for RawPtr aggregate must have 2 fields"),
             };
 
             if is_zst {
@@ -910,6 +911,8 @@
             }
             // Do not track unions.
             AggregateKind::Adt(_, _, _, _, Some(_)) => return None,
+            // FIXME: Do the extra work to GVN `from_raw_parts`
+            AggregateKind::RawPtr(..) => return None,
         };
 
         let fields: Option<Vec<_>> = fields
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 60513a6..7dcff45 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -11,7 +11,7 @@
 use rustc_middle::mir::*;
 use rustc_middle::ty::TypeVisitableExt;
 use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
-use rustc_session::config::OptLevel;
+use rustc_session::config::{DebugInfo, OptLevel};
 use rustc_span::source_map::Spanned;
 use rustc_span::sym;
 use rustc_target::abi::FieldIdx;
@@ -332,7 +332,8 @@
             | InstanceDef::DropGlue(..)
             | InstanceDef::CloneShim(..)
             | InstanceDef::ThreadLocalShim(..)
-            | InstanceDef::FnPtrAddrShim(..) => return Ok(()),
+            | InstanceDef::FnPtrAddrShim(..)
+            | InstanceDef::AsyncDropGlueCtorShim(..) => return Ok(()),
         }
 
         if self.tcx.is_constructor(callee_def_id) {
@@ -699,7 +700,19 @@
         // Insert all of the (mapped) parts of the callee body into the caller.
         caller_body.local_decls.extend(callee_body.drain_vars_and_temps());
         caller_body.source_scopes.extend(&mut callee_body.source_scopes.drain(..));
-        caller_body.var_debug_info.append(&mut callee_body.var_debug_info);
+        if self
+            .tcx
+            .sess
+            .opts
+            .unstable_opts
+            .inline_mir_preserve_debug
+            .unwrap_or(self.tcx.sess.opts.debuginfo != DebugInfo::None)
+        {
+            // Note that we need to preserve these in the standard library so that
+            // people working on rust can build with or without debuginfo while
+            // still getting consistent results from the mir-opt tests.
+            caller_body.var_debug_info.append(&mut callee_body.var_debug_info);
+        }
         caller_body.basic_blocks_mut().extend(callee_body.basic_blocks_mut().drain(..));
 
         caller_body[callsite.block].terminator = Some(Terminator {
@@ -1071,7 +1084,8 @@
     tcx: TyCtxt<'tcx>,
     instance: InstanceDef<'tcx>,
 ) -> Result<&'tcx Body<'tcx>, &'static str> {
-    if let ty::InstanceDef::DropGlue(_, Some(ty)) = instance
+    if let ty::InstanceDef::DropGlue(_, Some(ty))
+    | ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) = instance
         && let ty::Adt(def, args) = ty.kind()
     {
         let fields = def.all_fields();
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index 99c7b61..8c5f965 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -94,8 +94,10 @@
                 | InstanceDef::CloneShim(..) => {}
 
                 // This shim does not call any other functions, thus there can be no recursion.
-                InstanceDef::FnPtrAddrShim(..) => continue,
-                InstanceDef::DropGlue(..) => {
+                InstanceDef::FnPtrAddrShim(..) => {
+                    continue;
+                }
+                InstanceDef::DropGlue(..) | InstanceDef::AsyncDropGlueCtorShim(..) => {
                     // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to
                     // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
                     // needs some more analysis.
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index ff786d4..fd768cc 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -36,6 +36,7 @@
                         ctx.simplify_bool_cmp(&statement.source_info, rvalue);
                         ctx.simplify_ref_deref(&statement.source_info, rvalue);
                         ctx.simplify_len(&statement.source_info, rvalue);
+                        ctx.simplify_ptr_aggregate(&statement.source_info, rvalue);
                         ctx.simplify_cast(rvalue);
                     }
                     _ => {}
@@ -58,8 +59,17 @@
 
 impl<'tcx> InstSimplifyContext<'tcx, '_> {
     fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
+        self.should_simplify_custom(source_info, "Rvalue", rvalue)
+    }
+
+    fn should_simplify_custom(
+        &self,
+        source_info: &SourceInfo,
+        label: &str,
+        value: impl std::fmt::Debug,
+    ) -> bool {
         self.tcx.consider_optimizing(|| {
-            format!("InstSimplify - Rvalue: {rvalue:?} SourceInfo: {source_info:?}")
+            format!("InstSimplify - {label}: {value:?} SourceInfo: {source_info:?}")
         })
     }
 
@@ -111,7 +121,7 @@
         if a.const_.ty().is_bool() { a.const_.try_to_bool() } else { None }
     }
 
-    /// Transform "&(*a)" ==> "a".
+    /// Transform `&(*a)` ==> `a`.
     fn simplify_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
         if let Rvalue::Ref(_, _, place) = rvalue {
             if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() {
@@ -131,7 +141,7 @@
         }
     }
 
-    /// Transform "Len([_; N])" ==> "N".
+    /// Transform `Len([_; N])` ==> `N`.
     fn simplify_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
         if let Rvalue::Len(ref place) = *rvalue {
             let place_ty = place.ty(self.local_decls, self.tcx).ty;
@@ -147,6 +157,30 @@
         }
     }
 
+    /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`.
+    fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
+        if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue
+        {
+            let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx);
+            if meta_ty.is_unit() {
+                // The mutable borrows we're holding prevent printing `rvalue` here
+                if !self.should_simplify_custom(
+                    source_info,
+                    "Aggregate::RawPtr",
+                    (&pointee_ty, *mutability, &fields),
+                ) {
+                    return;
+                }
+
+                let mut fields = std::mem::take(fields);
+                let _meta = fields.pop().unwrap();
+                let data = fields.pop().unwrap();
+                let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability);
+                *rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty);
+            }
+        }
+    }
+
     fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
         if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue {
             let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 2218154..b8dbf8a 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -603,6 +603,7 @@
                         AggregateKind::Adt(_, variant, _, _, _) => variant,
                         AggregateKind::Array(_)
                         | AggregateKind::Tuple
+                        | AggregateKind::RawPtr(_, _)
                         | AggregateKind::Closure(_, _)
                         | AggregateKind::Coroutine(_, _)
                         | AggregateKind::CoroutineClosure(_, _) => VariantIdx::ZERO,
@@ -796,7 +797,7 @@
                 if let Some(ref value) = self.eval_operand(discr)
                     && let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value))
                     && let Ok(constant) = value_const.try_to_int()
-                    && let Ok(constant) = constant.to_bits(constant.size())
+                    && let Ok(constant) = constant.try_to_bits(constant.size())
                 {
                     // We managed to evaluate the discriminant, so we know we only need to visit
                     // one target.
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 7e89206..da63fcf 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -287,6 +287,34 @@
                             terminator.kind = TerminatorKind::Unreachable;
                         }
                     }
+                    sym::aggregate_raw_ptr => {
+                        let Ok([data, meta]) = <[_; 2]>::try_from(std::mem::take(args)) else {
+                            span_bug!(
+                                terminator.source_info.span,
+                                "Wrong number of arguments for aggregate_raw_ptr intrinsic",
+                            );
+                        };
+                        let target = target.unwrap();
+                        let pointer_ty = generic_args.type_at(0);
+                        let kind = if let ty::RawPtr(pointee_ty, mutability) = pointer_ty.kind() {
+                            AggregateKind::RawPtr(*pointee_ty, *mutability)
+                        } else {
+                            span_bug!(
+                                terminator.source_info.span,
+                                "Return type of aggregate_raw_ptr intrinsic must be a raw pointer",
+                            );
+                        };
+                        let fields = [data.node, meta.node];
+                        block.statements.push(Statement {
+                            source_info: terminator.source_info,
+                            kind: StatementKind::Assign(Box::new((
+                                *destination,
+                                Rvalue::Aggregate(Box::new(kind), fields.into()),
+                            ))),
+                        });
+
+                        terminator.kind = TerminatorKind::Goto { target };
+                    }
                     _ => {}
                 }
             }
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 4d9a198..1411d9b 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -41,7 +41,10 @@
                 should_cleanup = true;
                 continue;
             }
-            if SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() {
+            // unsound: https://github.com/rust-lang/rust/issues/124150
+            if tcx.sess.opts.unstable_opts.unsound_mir_opts
+                && SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some()
+            {
                 should_cleanup = true;
                 continue;
             }
@@ -369,8 +372,7 @@
         }
 
         fn int_equal(l: ScalarInt, r: impl Into<u128>, size: Size) -> bool {
-            l.try_to_int(l.size()).unwrap()
-                == ScalarInt::try_from_uint(r, size).unwrap().try_to_int(size).unwrap()
+            l.assert_int(l.size()) == ScalarInt::try_from_uint(r, size).unwrap().assert_int(size)
         }
 
         // We first compare the two branches, and then the other branches need to fulfill the same conditions.
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index a9d4b86..1f4af0e 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -490,14 +490,14 @@
                                 }
                                 _ => None,
                             };
-                            match rhs_val.map(|x| x.try_to_uint(sz).unwrap()) {
+                            match rhs_val.map(|x| x.assert_uint(sz)) {
                                 // for the zero test, int vs uint does not matter
                                 Some(x) if x != 0 => {}        // okay
                                 _ => return Err(Unpromotable), // value not known or 0 -- not okay
                             }
                             // Furthermore, for signed divison, we also have to exclude `int::MIN / -1`.
                             if lhs_ty.is_signed() {
-                                match rhs_val.map(|x| x.try_to_int(sz).unwrap()) {
+                                match rhs_val.map(|x| x.assert_int(sz)) {
                                     Some(-1) | None => {
                                         // The RHS is -1 or unknown, so we have to be careful.
                                         // But is the LHS int::MIN?
@@ -508,7 +508,7 @@
                                             _ => None,
                                         };
                                         let lhs_min = sz.signed_int_min();
-                                        match lhs_val.map(|x| x.try_to_int(sz).unwrap()) {
+                                        match lhs_val.map(|x| x.assert_int(sz)) {
                                             Some(x) if x != lhs_min => {}  // okay
                                             _ => return Err(Unpromotable), // value not known or int::MIN -- not okay
                                         }
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index fa6906b..1c85a60 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -22,6 +22,8 @@
 use rustc_middle::mir::patch::MirPatch;
 use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
 
+mod async_destructor_ctor;
+
 pub fn provide(providers: &mut Providers) {
     providers.mir_shims = make_shim;
 }
@@ -127,6 +129,9 @@
         ty::InstanceDef::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance),
         ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
         ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
+        ty::InstanceDef::AsyncDropGlueCtorShim(def_id, ty) => {
+            async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty)
+        }
         ty::InstanceDef::Virtual(..) => {
             bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
         }
diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
new file mode 100644
index 0000000..80eadb9
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
@@ -0,0 +1,618 @@
+use std::iter;
+
+use itertools::Itertools;
+use rustc_ast::Mutability;
+use rustc_const_eval::interpret;
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_index::{Idx, IndexVec};
+use rustc_middle::mir::{
+    BasicBlock, BasicBlockData, Body, CallSource, CastKind, Const, ConstOperand, ConstValue, Local,
+    LocalDecl, MirSource, Operand, Place, PlaceElem, Rvalue, SourceInfo, Statement, StatementKind,
+    Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, RETURN_PLACE,
+};
+use rustc_middle::ty::adjustment::PointerCoercion;
+use rustc_middle::ty::util::Discr;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::source_map::respan;
+use rustc_span::{Span, Symbol};
+use rustc_target::abi::{FieldIdx, VariantIdx};
+use rustc_target::spec::PanicStrategy;
+
+use super::{local_decls_for_sig, new_body};
+
+pub fn build_async_destructor_ctor_shim<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    ty: Option<Ty<'tcx>>,
+) -> Body<'tcx> {
+    debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
+
+    AsyncDestructorCtorShimBuilder::new(tcx, def_id, ty).build()
+}
+
+/// Builder for async_drop_in_place shim. Functions as a stack machine
+/// to build up an expression using combinators. Stack contains pairs
+/// of locals and types. Combinator is a not yet instantiated pair of a
+/// function and a type, is considered to be an operator which consumes
+/// operands from the stack by instantiating its function and its type
+/// with operand types and moving locals into the function call. Top
+/// pair is considered to be the last operand.
+// FIXME: add mir-opt tests
+struct AsyncDestructorCtorShimBuilder<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    self_ty: Option<Ty<'tcx>>,
+    span: Span,
+    source_info: SourceInfo,
+    param_env: ty::ParamEnv<'tcx>,
+
+    stack: Vec<Operand<'tcx>>,
+    last_bb: BasicBlock,
+    top_cleanup_bb: Option<BasicBlock>,
+
+    locals: IndexVec<Local, LocalDecl<'tcx>>,
+    bbs: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+}
+
+#[derive(Clone, Copy)]
+enum SurfaceDropKind {
+    Async,
+    Sync,
+}
+
+impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
+    const SELF_PTR: Local = Local::from_u32(1);
+    const INPUT_COUNT: usize = 1;
+    const MAX_STACK_LEN: usize = 2;
+
+    fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Option<Ty<'tcx>>) -> Self {
+        let args = if let Some(ty) = self_ty {
+            tcx.mk_args(&[ty.into()])
+        } else {
+            ty::GenericArgs::identity_for_item(tcx, def_id)
+        };
+        let sig = tcx.fn_sig(def_id).instantiate(tcx, args);
+        let sig = tcx.instantiate_bound_regions_with_erased(sig);
+        let span = tcx.def_span(def_id);
+
+        let source_info = SourceInfo::outermost(span);
+
+        debug_assert_eq!(sig.inputs().len(), Self::INPUT_COUNT);
+        let locals = local_decls_for_sig(&sig, span);
+
+        // Usual case: noop() + unwind resume + return
+        let mut bbs = IndexVec::with_capacity(3);
+        let param_env = tcx.param_env_reveal_all_normalized(def_id);
+        AsyncDestructorCtorShimBuilder {
+            tcx,
+            def_id,
+            self_ty,
+            span,
+            source_info,
+            param_env,
+
+            stack: Vec::with_capacity(Self::MAX_STACK_LEN),
+            last_bb: bbs.push(BasicBlockData::new(None)),
+            top_cleanup_bb: match tcx.sess.panic_strategy() {
+                PanicStrategy::Unwind => {
+                    // Don't drop input arg because it's just a pointer
+                    Some(bbs.push(BasicBlockData {
+                        statements: Vec::new(),
+                        terminator: Some(Terminator {
+                            source_info,
+                            kind: TerminatorKind::UnwindResume,
+                        }),
+                        is_cleanup: true,
+                    }))
+                }
+                PanicStrategy::Abort => None,
+            },
+
+            locals,
+            bbs,
+        }
+    }
+
+    fn build(self) -> Body<'tcx> {
+        let (tcx, def_id, Some(self_ty)) = (self.tcx, self.def_id, self.self_ty) else {
+            return self.build_zst_output();
+        };
+
+        let surface_drop_kind = || {
+            let param_env = tcx.param_env_reveal_all_normalized(def_id);
+            if self_ty.has_surface_async_drop(tcx, param_env) {
+                Some(SurfaceDropKind::Async)
+            } else if self_ty.has_surface_drop(tcx, param_env) {
+                Some(SurfaceDropKind::Sync)
+            } else {
+                None
+            }
+        };
+
+        match self_ty.kind() {
+            ty::Array(elem_ty, _) => self.build_slice(true, *elem_ty),
+            ty::Slice(elem_ty) => self.build_slice(false, *elem_ty),
+
+            ty::Tuple(elem_tys) => self.build_chain(None, elem_tys.iter()),
+            ty::Adt(adt_def, args) if adt_def.is_struct() => {
+                let field_tys = adt_def.non_enum_variant().fields.iter().map(|f| f.ty(tcx, args));
+                self.build_chain(surface_drop_kind(), field_tys)
+            }
+            ty::Closure(_, args) => self.build_chain(None, args.as_closure().upvar_tys().iter()),
+            ty::CoroutineClosure(_, args) => {
+                self.build_chain(None, args.as_coroutine_closure().upvar_tys().iter())
+            }
+
+            ty::Adt(adt_def, args) if adt_def.is_enum() => {
+                self.build_enum(*adt_def, *args, surface_drop_kind())
+            }
+
+            ty::Adt(adt_def, _) => {
+                assert!(adt_def.is_union());
+                match surface_drop_kind().unwrap() {
+                    SurfaceDropKind::Async => self.build_fused_async_surface(),
+                    SurfaceDropKind::Sync => self.build_fused_sync_surface(),
+                }
+            }
+
+            ty::Bound(..)
+            | ty::Foreign(_)
+            | ty::Placeholder(_)
+            | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) | ty::TyVar(_))
+            | ty::Param(_)
+            | ty::Alias(..) => {
+                bug!("Building async destructor for unexpected type: {self_ty:?}")
+            }
+
+            _ => {
+                bug!(
+                    "Building async destructor constructor shim is not yet implemented for type: {self_ty:?}"
+                )
+            }
+        }
+    }
+
+    fn build_enum(
+        mut self,
+        adt_def: ty::AdtDef<'tcx>,
+        args: ty::GenericArgsRef<'tcx>,
+        surface_drop: Option<SurfaceDropKind>,
+    ) -> Body<'tcx> {
+        let tcx = self.tcx;
+
+        let surface = match surface_drop {
+            None => None,
+            Some(kind) => {
+                self.put_self();
+                Some(match kind {
+                    SurfaceDropKind::Async => self.combine_async_surface(),
+                    SurfaceDropKind::Sync => self.combine_sync_surface(),
+                })
+            }
+        };
+
+        let mut other = None;
+        for (variant_idx, discr) in adt_def.discriminants(tcx) {
+            let variant = adt_def.variant(variant_idx);
+
+            let mut chain = None;
+            for (field_idx, field) in variant.fields.iter_enumerated() {
+                let field_ty = field.ty(tcx, args);
+                self.put_variant_field(variant.name, variant_idx, field_idx, field_ty);
+                let defer = self.combine_defer(field_ty);
+                chain = Some(match chain {
+                    None => defer,
+                    Some(chain) => self.combine_chain(chain, defer),
+                })
+            }
+            let variant_dtor = chain.unwrap_or_else(|| self.put_noop());
+
+            other = Some(match other {
+                None => variant_dtor,
+                Some(other) => {
+                    self.put_self();
+                    self.put_discr(discr);
+                    self.combine_either(other, variant_dtor)
+                }
+            });
+        }
+        let variants_dtor = other.unwrap_or_else(|| self.put_noop());
+
+        let dtor = match surface {
+            None => variants_dtor,
+            Some(surface) => self.combine_chain(surface, variants_dtor),
+        };
+        self.combine_fuse(dtor);
+        self.return_()
+    }
+
+    fn build_chain<I>(mut self, surface_drop: Option<SurfaceDropKind>, elem_tys: I) -> Body<'tcx>
+    where
+        I: Iterator<Item = Ty<'tcx>> + ExactSizeIterator,
+    {
+        let surface = match surface_drop {
+            None => None,
+            Some(kind) => {
+                self.put_self();
+                Some(match kind {
+                    SurfaceDropKind::Async => self.combine_async_surface(),
+                    SurfaceDropKind::Sync => self.combine_sync_surface(),
+                })
+            }
+        };
+
+        let mut chain = None;
+        for (field_idx, field_ty) in elem_tys.enumerate().map(|(i, ty)| (FieldIdx::new(i), ty)) {
+            self.put_field(field_idx, field_ty);
+            let defer = self.combine_defer(field_ty);
+            chain = Some(match chain {
+                None => defer,
+                Some(chain) => self.combine_chain(chain, defer),
+            })
+        }
+        let chain = chain.unwrap_or_else(|| self.put_noop());
+
+        let dtor = match surface {
+            None => chain,
+            Some(surface) => self.combine_chain(surface, chain),
+        };
+        self.combine_fuse(dtor);
+        self.return_()
+    }
+
+    fn build_zst_output(mut self) -> Body<'tcx> {
+        self.put_zst_output();
+        self.return_()
+    }
+
+    fn build_fused_async_surface(mut self) -> Body<'tcx> {
+        self.put_self();
+        let surface = self.combine_async_surface();
+        self.combine_fuse(surface);
+        self.return_()
+    }
+
+    fn build_fused_sync_surface(mut self) -> Body<'tcx> {
+        self.put_self();
+        let surface = self.combine_sync_surface();
+        self.combine_fuse(surface);
+        self.return_()
+    }
+
+    fn build_slice(mut self, is_array: bool, elem_ty: Ty<'tcx>) -> Body<'tcx> {
+        if is_array {
+            self.put_array_as_slice(elem_ty)
+        } else {
+            self.put_self()
+        }
+        let dtor = self.combine_slice(elem_ty);
+        self.combine_fuse(dtor);
+        self.return_()
+    }
+
+    fn put_zst_output(&mut self) {
+        let return_ty = self.locals[RETURN_PLACE].ty;
+        self.put_operand(Operand::Constant(Box::new(ConstOperand {
+            span: self.span,
+            user_ty: None,
+            const_: Const::zero_sized(return_ty),
+        })));
+    }
+
+    /// Puts `to_drop: *mut Self` on top of the stack.
+    fn put_self(&mut self) {
+        self.put_operand(Operand::Copy(Self::SELF_PTR.into()))
+    }
+
+    /// Given that `Self is [ElemTy; N]` puts `to_drop: *mut [ElemTy]`
+    /// on top of the stack.
+    fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) {
+        let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty));
+        self.put_temp_rvalue(Rvalue::Cast(
+            CastKind::PointerCoercion(PointerCoercion::Unsize),
+            Operand::Copy(Self::SELF_PTR.into()),
+            slice_ptr_ty,
+        ))
+    }
+
+    /// If given Self is a struct puts `to_drop: *mut FieldTy` on top
+    /// of the stack.
+    fn put_field(&mut self, field: FieldIdx, field_ty: Ty<'tcx>) {
+        let place = Place {
+            local: Self::SELF_PTR,
+            projection: self
+                .tcx
+                .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]),
+        };
+        self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place))
+    }
+
+    /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
+    /// the stack.
+    fn put_variant_field(
+        &mut self,
+        variant_sym: Symbol,
+        variant: VariantIdx,
+        field: FieldIdx,
+        field_ty: Ty<'tcx>,
+    ) {
+        let place = Place {
+            local: Self::SELF_PTR,
+            projection: self.tcx.mk_place_elems(&[
+                PlaceElem::Deref,
+                PlaceElem::Downcast(Some(variant_sym), variant),
+                PlaceElem::Field(field, field_ty),
+            ]),
+        };
+        self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place))
+    }
+
+    /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
+    /// the stack.
+    fn put_discr(&mut self, discr: Discr<'tcx>) {
+        let (size, _) = discr.ty.int_size_and_signed(self.tcx);
+        self.put_operand(Operand::const_from_scalar(
+            self.tcx,
+            discr.ty,
+            interpret::Scalar::from_uint(discr.val, size),
+            self.span,
+        ));
+    }
+
+    /// Puts `x: RvalueType` on top of the stack.
+    fn put_temp_rvalue(&mut self, rvalue: Rvalue<'tcx>) {
+        let last_bb = &mut self.bbs[self.last_bb];
+        debug_assert!(last_bb.terminator.is_none());
+        let source_info = self.source_info;
+
+        let local_ty = rvalue.ty(&self.locals, self.tcx);
+        // We need to create a new local to be able to "consume" it with
+        // a combinator
+        let local = self.locals.push(LocalDecl::with_source_info(local_ty, source_info));
+        last_bb.statements.extend_from_slice(&[
+            Statement { source_info, kind: StatementKind::StorageLive(local) },
+            Statement {
+                source_info,
+                kind: StatementKind::Assign(Box::new((local.into(), rvalue))),
+            },
+        ]);
+
+        self.put_operand(Operand::Move(local.into()));
+    }
+
+    /// Puts operand on top of the stack.
+    fn put_operand(&mut self, operand: Operand<'tcx>) {
+        if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb {
+            let source_info = self.source_info;
+            match &operand {
+                Operand::Copy(_) | Operand::Constant(_) => {
+                    *top_cleanup_bb = self.bbs.push(BasicBlockData {
+                        statements: Vec::new(),
+                        terminator: Some(Terminator {
+                            source_info,
+                            kind: TerminatorKind::Goto { target: *top_cleanup_bb },
+                        }),
+                        is_cleanup: true,
+                    });
+                }
+                Operand::Move(place) => {
+                    let local = place.as_local().unwrap();
+                    *top_cleanup_bb = self.bbs.push(BasicBlockData {
+                        statements: Vec::new(),
+                        terminator: Some(Terminator {
+                            source_info,
+                            kind: if self.locals[local].ty.needs_drop(self.tcx, self.param_env) {
+                                TerminatorKind::Drop {
+                                    place: local.into(),
+                                    target: *top_cleanup_bb,
+                                    unwind: UnwindAction::Terminate(
+                                        UnwindTerminateReason::InCleanup,
+                                    ),
+                                    replace: false,
+                                }
+                            } else {
+                                TerminatorKind::Goto { target: *top_cleanup_bb }
+                            },
+                        }),
+                        is_cleanup: true,
+                    });
+                }
+            };
+        }
+        self.stack.push(operand);
+    }
+
+    /// Puts `noop: async_drop::Noop` on top of the stack
+    fn put_noop(&mut self) -> Ty<'tcx> {
+        self.apply_combinator(0, LangItem::AsyncDropNoop, &[])
+    }
+
+    fn combine_async_surface(&mut self) -> Ty<'tcx> {
+        self.apply_combinator(1, LangItem::SurfaceAsyncDropInPlace, &[self.self_ty.unwrap().into()])
+    }
+
+    fn combine_sync_surface(&mut self) -> Ty<'tcx> {
+        self.apply_combinator(
+            1,
+            LangItem::AsyncDropSurfaceDropInPlace,
+            &[self.self_ty.unwrap().into()],
+        )
+    }
+
+    fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> {
+        self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()])
+    }
+
+    fn combine_slice(&mut self, elem_ty: Ty<'tcx>) -> Ty<'tcx> {
+        self.apply_combinator(1, LangItem::AsyncDropSlice, &[elem_ty.into()])
+    }
+
+    fn combine_defer(&mut self, to_drop_ty: Ty<'tcx>) -> Ty<'tcx> {
+        self.apply_combinator(1, LangItem::AsyncDropDefer, &[to_drop_ty.into()])
+    }
+
+    fn combine_chain(&mut self, first: Ty<'tcx>, second: Ty<'tcx>) -> Ty<'tcx> {
+        self.apply_combinator(2, LangItem::AsyncDropChain, &[first.into(), second.into()])
+    }
+
+    fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> {
+        self.apply_combinator(
+            4,
+            LangItem::AsyncDropEither,
+            &[other.into(), matched.into(), self.self_ty.unwrap().into()],
+        )
+    }
+
+    fn return_(mut self) -> Body<'tcx> {
+        let last_bb = &mut self.bbs[self.last_bb];
+        debug_assert!(last_bb.terminator.is_none());
+        let source_info = self.source_info;
+
+        let (1, Some(output)) = (self.stack.len(), self.stack.pop()) else {
+            span_bug!(
+                self.span,
+                "async destructor ctor shim builder finished with invalid number of stack items: expected 1 found {}",
+                self.stack.len(),
+            )
+        };
+        #[cfg(debug_assertions)]
+        if let Some(ty) = self.self_ty {
+            debug_assert_eq!(
+                output.ty(&self.locals, self.tcx),
+                ty.async_destructor_ty(self.tcx, self.param_env),
+                "output async destructor types did not match for type: {ty:?}",
+            );
+        }
+
+        let dead_storage = match &output {
+            Operand::Move(place) => Some(Statement {
+                source_info,
+                kind: StatementKind::StorageDead(place.as_local().unwrap()),
+            }),
+            _ => None,
+        };
+
+        last_bb.statements.extend(
+            iter::once(Statement {
+                source_info,
+                kind: StatementKind::Assign(Box::new((RETURN_PLACE.into(), Rvalue::Use(output)))),
+            })
+            .chain(dead_storage),
+        );
+
+        last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return });
+
+        let source = MirSource::from_instance(ty::InstanceDef::AsyncDropGlueCtorShim(
+            self.def_id,
+            self.self_ty,
+        ));
+        new_body(source, self.bbs, self.locals, Self::INPUT_COUNT, self.span)
+    }
+
+    fn apply_combinator(
+        &mut self,
+        arity: usize,
+        function: LangItem,
+        args: &[ty::GenericArg<'tcx>],
+    ) -> Ty<'tcx> {
+        let function = self.tcx.require_lang_item(function, Some(self.span));
+        let operands_split = self
+            .stack
+            .len()
+            .checked_sub(arity)
+            .expect("async destructor ctor shim combinator tried to consume too many items");
+        let operands = &self.stack[operands_split..];
+
+        let func_ty = Ty::new_fn_def(self.tcx, function, args.iter().copied());
+        let func_sig = func_ty.fn_sig(self.tcx).no_bound_vars().unwrap();
+        #[cfg(debug_assertions)]
+        operands.iter().zip(func_sig.inputs()).for_each(|(operand, expected_ty)| {
+            let operand_ty = operand.ty(&self.locals, self.tcx);
+            if operand_ty == *expected_ty {
+                return;
+            }
+
+            // If projection of Discriminant then compare with `Ty::discriminant_ty`
+            if let ty::Alias(ty::AliasKind::Projection, ty::AliasTy { args, def_id, .. }) =
+                expected_ty.kind()
+                && Some(*def_id) == self.tcx.lang_items().discriminant_type()
+                && args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty
+            {
+                return;
+            }
+
+            span_bug!(
+                self.span,
+                "Operand type and combinator argument type are not equal.
+    operand_ty: {:?}
+    argument_ty: {:?}
+",
+                operand_ty,
+                expected_ty
+            );
+        });
+
+        let target = self.bbs.push(BasicBlockData {
+            statements: operands
+                .iter()
+                .rev()
+                .filter_map(|o| {
+                    if let Operand::Move(Place { local, projection }) = o {
+                        assert!(projection.is_empty());
+                        Some(Statement {
+                            source_info: self.source_info,
+                            kind: StatementKind::StorageDead(*local),
+                        })
+                    } else {
+                        None
+                    }
+                })
+                .collect(),
+            terminator: None,
+            is_cleanup: false,
+        });
+
+        let dest_ty = func_sig.output();
+        let dest =
+            self.locals.push(LocalDecl::with_source_info(dest_ty, self.source_info).immutable());
+
+        let unwind = if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb {
+            for _ in 0..arity {
+                *top_cleanup_bb =
+                    self.bbs[*top_cleanup_bb].terminator().successors().exactly_one().ok().unwrap();
+            }
+            UnwindAction::Cleanup(*top_cleanup_bb)
+        } else {
+            UnwindAction::Unreachable
+        };
+
+        let last_bb = &mut self.bbs[self.last_bb];
+        debug_assert!(last_bb.terminator.is_none());
+        last_bb.statements.push(Statement {
+            source_info: self.source_info,
+            kind: StatementKind::StorageLive(dest),
+        });
+        last_bb.terminator = Some(Terminator {
+            source_info: self.source_info,
+            kind: TerminatorKind::Call {
+                func: Operand::Constant(Box::new(ConstOperand {
+                    span: self.span,
+                    user_ty: None,
+                    const_: Const::Val(ConstValue::ZeroSized, func_ty),
+                })),
+                destination: dest.into(),
+                target: Some(target),
+                unwind,
+                call_source: CallSource::Misc,
+                fn_span: self.span,
+                args: self.stack.drain(operands_split..).map(|o| respan(self.span, o)).collect(),
+            },
+        });
+
+        self.put_operand(Operand::Move(dest.into()));
+        self.last_bb = target;
+
+        dest_ty
+    }
+}
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 36d623f..a8fa6fe 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -966,13 +966,14 @@
         ty::InstanceDef::ThreadLocalShim(..) => {
             bug!("{:?} being reified", instance);
         }
-        ty::InstanceDef::DropGlue(_, None) => {
+        ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None) => {
             // Don't need to emit noop drop glue if we are calling directly.
             if !is_direct_call {
                 output.push(create_fn_mono_item(tcx, instance, source));
             }
         }
         ty::InstanceDef::DropGlue(_, Some(_))
+        | ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_))
         | ty::InstanceDef::VTableShim(..)
         | ty::InstanceDef::ReifyShim(..)
         | ty::InstanceDef::ClosureOnceShim { .. }
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index 5a92657..23e0789 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -625,7 +625,8 @@
                 | ty::InstanceDef::Virtual(..)
                 | ty::InstanceDef::CloneShim(..)
                 | ty::InstanceDef::ThreadLocalShim(..)
-                | ty::InstanceDef::FnPtrAddrShim(..) => return None,
+                | ty::InstanceDef::FnPtrAddrShim(..)
+                | ty::InstanceDef::AsyncDropGlueCtorShim(..) => return None,
             };
 
             // If this is a method, we want to put it into the same module as
@@ -769,7 +770,9 @@
     };
 
     let def_id = match instance.def {
-        InstanceDef::Item(def_id) | InstanceDef::DropGlue(def_id, Some(_)) => def_id,
+        InstanceDef::Item(def_id)
+        | InstanceDef::DropGlue(def_id, Some(_))
+        | InstanceDef::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id,
 
         // We match the visibility of statics here
         InstanceDef::ThreadLocalShim(def_id) => {
@@ -786,6 +789,7 @@
         | InstanceDef::ConstructCoroutineInClosureShim { .. }
         | InstanceDef::CoroutineKindShim { .. }
         | InstanceDef::DropGlue(..)
+        | InstanceDef::AsyncDropGlueCtorShim(..)
         | InstanceDef::CloneShim(..)
         | InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,
     };
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 1c1ca0b..1abb1d2 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -30,7 +30,7 @@
 //
 // This assertion is in this crate, rather than in `rustc_lexer`, because that
 // crate cannot depend on `rustc_data_structures`.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12);
 
 #[derive(Clone, Debug)]
@@ -204,6 +204,7 @@
                     self.ident(start)
                 }
                 rustc_lexer::TokenKind::InvalidIdent
+                | rustc_lexer::TokenKind::InvalidPrefix
                     // Do not recover an identifier with emoji if the codepoint is a confusable
                     // with a recoverable substitution token, like `➖`.
                     if !UNICODE_ARRAY
@@ -301,7 +302,9 @@
                 rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret),
                 rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent),
 
-                rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => {
+                rustc_lexer::TokenKind::Unknown
+                | rustc_lexer::TokenKind::InvalidIdent
+                | rustc_lexer::TokenKind::InvalidPrefix => {
                     // Don't emit diagnostics for sequences of the same invalid token
                     if swallow_next_invalid > 0 {
                         swallow_next_invalid -= 1;
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index baaed5e..62c8f9f 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -454,7 +454,7 @@
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 64f7665..f256dbf 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -29,7 +29,7 @@
 use rustc_ast::tokenstream::AttrTokenTree;
 use rustc_ast::util::parser::AssocOp;
 use rustc_ast::{
-    AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingAnnotation, Block,
+    AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
     BlockCheckMode, Expr, ExprKind, GenericArg, Generics, HasTokens, Item, ItemKind, Param, Pat,
     PatKind, Path, PathSegment, QSelf, Ty, TyKind,
 };
@@ -51,7 +51,7 @@
 pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param {
     let pat = P(Pat {
         id: ast::DUMMY_NODE_ID,
-        kind: PatKind::Ident(BindingAnnotation::NONE, ident, None),
+        kind: PatKind::Ident(BindingMode::NONE, ident, None),
         span: ident.span,
         tokens: None,
     });
@@ -2787,7 +2787,7 @@
                                 }
                                 _ => {}
                             },
-                            PatKind::Ident(BindingAnnotation::NONE, ident, None) => {
+                            PatKind::Ident(BindingMode::NONE, ident, None) => {
                                 match &first_pat.kind {
                                     PatKind::Ident(_, old_ident, _) => {
                                         let path = PatKind::Path(
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 8ae809f..84ecd0a 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2712,7 +2712,7 @@
                 match ty {
                     Ok(ty) => {
                         let ident = Ident::new(kw::Empty, this.prev_token.span);
-                        let bm = BindingAnnotation::NONE;
+                        let bm = BindingMode::NONE;
                         let pat = this.mk_pat_ident(ty.span, bm, ident);
                         (pat, ty)
                     }
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index a4a9ba9..4a996f8 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -179,7 +179,7 @@
 
 // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
 // it doesn't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 rustc_data_structures::static_assert_size!(Parser<'_>, 264);
 
 /// Stores span information about a closure.
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index dd1ecf9b..78d3d01 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -16,8 +16,8 @@
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, BinOpToken, Delimiter, Token};
 use rustc_ast::{
-    self as ast, AttrVec, BindingAnnotation, ByRef, Expr, ExprKind, MacCall, Mutability, Pat,
-    PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
+    self as ast, AttrVec, BindingMode, ByRef, Expr, ExprKind, MacCall, Mutability, Pat, PatField,
+    PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
 };
 use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, Diag, PResult};
@@ -486,7 +486,7 @@
             }
             // Parse ref ident @ pat / ref mut ident @ pat
             let mutbl = self.parse_mutability();
-            self.parse_pat_ident(BindingAnnotation(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)?
+            self.parse_pat_ident(BindingMode(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)?
         } else if self.eat_keyword(kw::Box) {
             self.parse_pat_box()?
         } else if self.check_inline_const(0) {
@@ -511,7 +511,7 @@
             // Parse `ident @ pat`
             // This can give false positives and parse nullary enums,
             // they are dealt with later in resolve.
-            self.parse_pat_ident(BindingAnnotation::NONE, syntax_loc)?
+            self.parse_pat_ident(BindingMode::NONE, syntax_loc)?
         } else if self.is_start_of_pat_with_path() {
             // Parse pattern starting with a path
             let (qself, path) = if self.eat_lt() {
@@ -766,8 +766,7 @@
         let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?;
 
         // If we don't have `mut $ident (@ pat)?`, error.
-        if let PatKind::Ident(BindingAnnotation(br @ ByRef::No, m @ Mutability::Not), ..) =
-            &mut pat.kind
+        if let PatKind::Ident(BindingMode(br @ ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind
         {
             // Don't recurse into the subpattern.
             // `mut` on the outer binding doesn't affect the inner bindings.
@@ -779,8 +778,7 @@
             self.ban_mut_general_pat(mut_span, &pat, changed_any_binding);
         }
 
-        if matches!(pat.kind, PatKind::Ident(BindingAnnotation(ByRef::Yes(_), Mutability::Mut), ..))
-        {
+        if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(_), Mutability::Mut), ..)) {
             self.psess.gated_spans.gate(sym::mut_ref, pat.span);
         }
         Ok(pat.into_inner().kind)
@@ -792,7 +790,7 @@
         struct AddMut(bool);
         impl MutVisitor for AddMut {
             fn visit_pat(&mut self, pat: &mut P<Pat>) {
-                if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) =
+                if let PatKind::Ident(BindingMode(ByRef::No, m @ Mutability::Not), ..) =
                     &mut pat.kind
                 {
                     self.0 = true;
@@ -1025,7 +1023,7 @@
     /// error message when parsing mistakes like `ref foo(a, b)`.
     fn parse_pat_ident(
         &mut self,
-        binding_annotation: BindingAnnotation,
+        binding_annotation: BindingMode,
         syntax_loc: Option<PatternLocation>,
     ) -> PResult<'a, PatKind> {
         let ident = self.parse_ident_common(false)?;
@@ -1163,7 +1161,7 @@
                 None
             };
 
-            Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub))
+            Ok(PatKind::Ident(BindingMode::NONE, Ident::new(kw::Box, box_span), sub))
         } else {
             let pat = self.parse_pat_with_range_pat(false, None, None)?;
             self.psess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span));
@@ -1344,7 +1342,7 @@
         if let Some(last) = fields.iter().last()
             && last.is_shorthand
             && let PatKind::Ident(binding, ident, None) = last.pat.kind
-            && binding != BindingAnnotation::NONE
+            && binding != BindingMode::NONE
             && self.token == token::Colon
             // We found `ref mut? ident:`, try to parse a `name,` or `name }`.
             && let Some(name_span) = self.look_ahead(1, |t| t.is_ident().then(|| t.span))
@@ -1400,7 +1398,7 @@
 
             let fieldname = self.parse_field_name()?;
             hi = self.prev_token.span;
-            let ann = BindingAnnotation(by_ref, mutability);
+            let ann = BindingMode(by_ref, mutability);
             let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
             let subpat =
                 if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat };
@@ -1418,7 +1416,7 @@
         })
     }
 
-    pub(super) fn mk_pat_ident(&self, span: Span, ann: BindingAnnotation, ident: Ident) -> P<Pat> {
+    pub(super) fn mk_pat_ident(&self, span: Span, ann: BindingMode, ident: Ident) -> P<Pat> {
         self.mk_pat(span, PatKind::Ident(ann, ident, None))
     }
 
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index ccda43c..faf6ca7 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -1087,7 +1087,7 @@
 }
 
 // Assert a reasonable size for `Piece`
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 rustc_index::static_assert_size!(Piece<'_>, 16);
 
 #[cfg(test)]
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index e03052b..bce29e2 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -8,7 +8,6 @@
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
 #![allow(internal_features)]
-#![feature(generic_nonzero)]
 #![feature(let_chains)]
 #![feature(map_try_insert)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index a1f37ee..d45ee32 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -83,8 +83,7 @@
 fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
     for param in params {
         match param.pat.kind {
-            hir::PatKind::Wild
-            | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {}
+            hir::PatKind::Wild | hir::PatKind::Binding(hir::BindingMode::NONE, _, _, None) => {}
             _ => {
                 tcx.dcx().emit_err(NoPatterns { span: param.pat.span });
             }
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index 3373835..914481d 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -3,7 +3,6 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
-#![feature(generic_nonzero)]
 #![feature(min_specialization)]
 #![feature(rustc_attrs)]
 #![allow(rustc::potential_query_instability, unused_parens)]
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 5349370..2b3fa7f 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -459,7 +459,8 @@
                     }
                     TaskDepsRef::Ignore => return,
                     TaskDepsRef::Forbid => {
-                        panic!("Illegal read of: {dep_node_index:?}")
+                        // Reading is forbidden in this context. ICE with a useful error message.
+                        panic_on_forbidden_read(data, dep_node_index)
                     }
                 };
                 let task_deps = &mut *task_deps;
@@ -1366,3 +1367,45 @@
 
     eprintln!("end of try_mark_green dep node stack");
 }
+
+#[cold]
+#[inline(never)]
+fn panic_on_forbidden_read<D: Deps>(data: &DepGraphData<D>, dep_node_index: DepNodeIndex) -> ! {
+    // We have to do an expensive reverse-lookup of the DepNode that
+    // corresponds to `dep_node_index`, but that's OK since we are about
+    // to ICE anyway.
+    let mut dep_node = None;
+
+    // First try to find the dep node among those that already existed in the
+    // previous session
+    for (prev_index, index) in data.current.prev_index_to_index.lock().iter_enumerated() {
+        if index == &Some(dep_node_index) {
+            dep_node = Some(data.previous.index_to_node(prev_index));
+            break;
+        }
+    }
+
+    if dep_node.is_none() {
+        // Try to find it among the new nodes
+        for shard in data.current.new_node_to_index.lock_shards() {
+            if let Some((node, _)) = shard.iter().find(|(_, index)| **index == dep_node_index) {
+                dep_node = Some(*node);
+                break;
+            }
+        }
+    }
+
+    let dep_node = dep_node.map_or_else(
+        || format!("with index {:?}", dep_node_index),
+        |dep_node| format!("`{:?}`", dep_node),
+    );
+
+    panic!(
+        "Error: trying to record dependency on DepNode {dep_node} in a \
+         context that does not allow it (e.g. during query deserialization). \
+         The most common case of recording a dependency on a DepNode `foo` is \
+         when the correspondng query `foo` is invoked. Invoking queries is not \
+         allowed as part of loading something from the incremental on-disk cache. \
+         See <https://github.com/rust-lang/rust/pull/91919>."
+    )
+}
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs
index 6a959a9..416f556 100644
--- a/compiler/rustc_query_system/src/lib.rs
+++ b/compiler/rustc_query_system/src/lib.rs
@@ -1,6 +1,5 @@
 #![feature(assert_matches)]
 #![feature(core_intrinsics)]
-#![feature(generic_nonzero)]
 #![feature(hash_raw_entry)]
 #![feature(min_specialization)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index ba1391b..753ba09 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -46,7 +46,7 @@
 #[derive(Copy, Clone, Debug)]
 struct BindingInfo {
     span: Span,
-    annotation: BindingAnnotation,
+    annotation: BindingMode,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -3655,14 +3655,14 @@
     fn try_resolve_as_non_binding(
         &mut self,
         pat_src: PatternSource,
-        ann: BindingAnnotation,
+        ann: BindingMode,
         ident: Ident,
         has_sub: bool,
     ) -> Option<Res> {
         // An immutable (no `mut`) by-value (no `ref`) binding pattern without
         // a sub pattern (no `@ $pat`) is syntactically ambiguous as it could
         // also be interpreted as a path to e.g. a constant, variant, etc.
-        let is_syntactic_ambiguity = !has_sub && ann == BindingAnnotation::NONE;
+        let is_syntactic_ambiguity = !has_sub && ann == BindingMode::NONE;
 
         let ls_binding = self.maybe_resolve_ident_in_lexical_scope(ident, ValueNS)?;
         let (res, binding) = match ls_binding {
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index 5a9403e..3e1d8f3 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -11,7 +11,6 @@
 #![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(const_option)]
 #![feature(core_intrinsics)]
-#![feature(generic_nonzero)]
 #![feature(inline_const)]
 #![feature(min_specialization)]
 #![feature(never_type)]
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 2a7b565..d5b22f8 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -148,6 +148,8 @@
 pub struct CoverageOptions {
     /// Add branch coverage instrumentation.
     pub branch: bool,
+    /// Add mcdc coverage instrumentation.
+    pub mcdc: bool,
 }
 
 /// Settings for `-Z instrument-xray` flag.
@@ -2880,7 +2882,7 @@
     use rustc_feature::UnstableFeatures;
     use rustc_span::edition::Edition;
     use rustc_span::RealFileName;
-    use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
+    use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel, WasmCAbi};
     use rustc_target::spec::{
         RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
     };
@@ -2978,6 +2980,7 @@
         Polonius,
         InliningThreshold,
         FunctionReturn,
+        WasmCAbi,
     );
 
     impl<T1, T2> DepTrackingHash for (T1, T2)
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index c63af90..58e1394 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -1,4 +1,3 @@
-#![feature(generic_nonzero)]
 #![feature(let_chains)]
 #![feature(lazy_cell)]
 #![feature(option_get_or_insert_default)]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index c4d802a..d510805 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -8,7 +8,9 @@
 use rustc_data_structures::stable_hasher::Hash64;
 use rustc_errors::ColorConfig;
 use rustc_errors::{LanguageIdentifier, TerminalUrl};
-use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
+use rustc_target::spec::{
+    CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet, WasmCAbi,
+};
 use rustc_target::spec::{
     RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
 };
@@ -396,7 +398,7 @@
     pub const parse_optimization_fuel: &str = "crate=integer";
     pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
     pub const parse_instrument_coverage: &str = parse_bool;
-    pub const parse_coverage_options: &str = "`branch` or `no-branch`";
+    pub const parse_coverage_options: &str = "either  `no-branch`, `branch` or `mcdc`";
     pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`";
     pub const parse_unpretty: &str = "`string` or `string=string`";
     pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number";
@@ -441,6 +443,7 @@
         "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number";
     pub const parse_llvm_module_flag: &str = "<key>:<type>:<value>:<behavior>. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)";
     pub const parse_function_return: &str = "`keep` or `thunk-extern`";
+    pub const parse_wasm_c_abi: &str = "`legacy` or `spec`";
 }
 
 mod parse {
@@ -946,17 +949,19 @@
         let Some(v) = v else { return true };
 
         for option in v.split(',') {
-            let (option, enabled) = match option.strip_prefix("no-") {
-                Some(without_no) => (without_no, false),
-                None => (option, true),
-            };
-            let slot = match option {
-                "branch" => &mut slot.branch,
+            match option {
+                "no-branch" => {
+                    slot.branch = false;
+                    slot.mcdc = false;
+                }
+                "branch" => slot.branch = true,
+                "mcdc" => {
+                    slot.branch = true;
+                    slot.mcdc = true;
+                }
                 _ => return false,
-            };
-            *slot = enabled;
+            }
         }
-
         true
     }
 
@@ -1433,6 +1438,15 @@
         }
         true
     }
+
+    pub(crate) fn parse_wasm_c_abi(slot: &mut WasmCAbi, v: Option<&str>) -> bool {
+        match v {
+            Some("spec") => *slot = WasmCAbi::Spec,
+            Some("legacy") => *slot = WasmCAbi::Legacy,
+            _ => return false,
+        }
+        true
+    }
 }
 
 options! {
@@ -1717,6 +1731,9 @@
         "enable MIR inlining (default: no)"),
     inline_mir_hint_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
         "inlining threshold for functions with inline hint (default: 100)"),
+    inline_mir_preserve_debug: Option<bool> = (None, parse_opt_bool, [TRACKED],
+        "when MIR inlining, whether to preserve debug info for callee variables \
+        (default: preserve for debuginfo != None, otherwise remove)"),
     inline_mir_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
         "a default MIR inlining threshold (default: 50)"),
     input_stats: bool = (false, parse_bool, [UNTRACKED],
@@ -2058,6 +2075,8 @@
         Requires `-Clto[=[fat,yes]]`"),
     wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED],
         "whether to build a wasi command or reactor"),
+    wasm_c_abi: WasmCAbi = (WasmCAbi::Legacy, parse_wasm_c_abi, [TRACKED],
+        "use spec-compliant C ABI for `wasm32-unknown-unknown` (default: legacy)"),
     write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED],
         "whether long type names should be written to files instead of being printed in errors"),
     // tidy-alphabetical-end
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index b63c119..2bc14b4 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -352,6 +352,10 @@
         self.instrument_coverage() && self.opts.unstable_opts.coverage_options.branch
     }
 
+    pub fn instrument_coverage_mcdc(&self) -> bool {
+        self.instrument_coverage() && self.opts.unstable_opts.coverage_options.mcdc
+    }
+
     pub fn is_sanitizer_cfi_enabled(&self) -> bool {
         self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
     }
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 61bbedf..fd31c02 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -500,6 +500,12 @@
         matches!(instance.def, ty::InstanceDef::DropGlue(_, None))
     }
 
+    fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool {
+        let tables = self.0.borrow_mut();
+        let instance = tables.instances[def];
+        matches!(instance.def, ty::InstanceDef::AsyncDropGlueCtorShim(_, None))
+    }
+
     fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance {
         let mut tables = self.0.borrow_mut();
         let def_id = tables[def_id];
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index c9f6661..7c02162 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -543,6 +543,9 @@
             mir::AggregateKind::CoroutineClosure(..) => {
                 todo!("FIXME(async_closures): Lower these to SMIR")
             }
+            mir::AggregateKind::RawPtr(ty, mutability) => {
+                stable_mir::mir::AggregateKind::RawPtr(ty.stable(tables), mutability.stable(tables))
+            }
         }
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index 112e44f..4abf991 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -807,7 +807,10 @@
             | ty::InstanceDef::ThreadLocalShim(..)
             | ty::InstanceDef::DropGlue(..)
             | ty::InstanceDef::CloneShim(..)
-            | ty::InstanceDef::FnPtrShim(..) => stable_mir::mir::mono::InstanceKind::Shim,
+            | ty::InstanceDef::FnPtrShim(..)
+            | ty::InstanceDef::AsyncDropGlueCtorShim(..) => {
+                stable_mir::mir::mono::InstanceKind::Shim
+            }
         };
         stable_mir::mir::mono::Instance { def, kind }
     }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 46bae1c..2d3053cc 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -361,6 +361,7 @@
         adt_const_params,
         advanced_slice_patterns,
         adx_target_feature,
+        aggregate_raw_ptr,
         alias,
         align,
         align_offset,
@@ -424,6 +425,16 @@
         async_call_mut,
         async_call_once,
         async_closure,
+        async_destruct,
+        async_drop,
+        async_drop_chain,
+        async_drop_defer,
+        async_drop_either,
+        async_drop_fuse,
+        async_drop_in_place,
+        async_drop_noop,
+        async_drop_slice,
+        async_drop_surface_drop_in_place,
         async_fn,
         async_fn_in_trait,
         async_fn_kind_helper,
@@ -825,6 +836,7 @@
         fadd_fast,
         fake_variadic,
         fallback,
+        fallback_surface_drop,
         fdiv_algebraic,
         fdiv_fast,
         feature,
@@ -1788,6 +1800,7 @@
         sub_assign,
         sub_with_overflow,
         suggestion,
+        surface_async_drop_in_place,
         sym,
         sync,
         synthetic,
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index f68668a..f1c35123 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -55,7 +55,9 @@
     printer
         .print_def_path(
             def_id,
-            if let ty::InstanceDef::DropGlue(_, _) = instance.def {
+            if let ty::InstanceDef::DropGlue(_, _) | ty::InstanceDef::AsyncDropGlueCtorShim(_, _) =
+                instance.def
+            {
                 // Add the name of the dropped type to the symbol name
                 &*instance.args
             } else {
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index 4502df3..cdd82aa 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -1,6 +1,6 @@
 use crate::abi::{self, Abi, Align, FieldsShape, Size};
 use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout};
-use crate::spec::{self, HasTargetSpec};
+use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt};
 use rustc_span::Symbol;
 use std::fmt;
 use std::str::FromStr;
@@ -829,7 +829,7 @@
     ) -> Result<(), AdjustForForeignAbiError>
     where
         Ty: TyAbiInterface<'a, C> + Copy,
-        C: HasDataLayout + HasTargetSpec,
+        C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt,
     {
         if abi == spec::abi::Abi::X86Interrupt {
             if let Some(arg) = self.args.first_mut() {
@@ -886,7 +886,9 @@
             "sparc" => sparc::compute_abi_info(cx, self),
             "sparc64" => sparc64::compute_abi_info(cx, self),
             "nvptx64" => {
-                if cx.target_spec().adjust_abi(abi, self.c_variadic) == spec::abi::Abi::PtxKernel {
+                if cx.target_spec().adjust_abi(cx, abi, self.c_variadic)
+                    == spec::abi::Abi::PtxKernel
+                {
                     nvptx64::compute_ptx_kernel_abi_info(cx, self)
                 } else {
                     nvptx64::compute_abi_info(self)
@@ -895,7 +897,7 @@
             "hexagon" => hexagon::compute_abi_info(self),
             "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self),
             "wasm32" | "wasm64" => {
-                if cx.target_spec().adjust_abi(abi, self.c_variadic) == spec::abi::Abi::Wasm {
+                if cx.target_spec().adjust_abi(cx, abi, self.c_variadic) == spec::abi::Abi::Wasm {
                     wasm::compute_wasm_abi_info(self)
                 } else {
                     wasm::compute_c_abi_info(cx, self)
@@ -947,7 +949,7 @@
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 3a69b19..291a761 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -37,7 +37,7 @@
 use crate::abi::call::Conv;
 use crate::abi::{Endian, Integer, Size, TargetDataLayout, TargetDataLayoutErrors};
 use crate::json::{Json, ToJson};
-use crate::spec::abi::{lookup as lookup_abi, Abi};
+use crate::spec::abi::Abi;
 use crate::spec::crt_objects::CrtObjects;
 use rustc_fs_util::try_canonicalize;
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
@@ -1915,6 +1915,19 @@
     }
 }
 
+/// Which C ABI to use for `wasm32-unknown-unknown`.
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum WasmCAbi {
+    /// Spec-compliant C ABI.
+    Spec,
+    /// Legacy ABI. Which is non-spec-compliant.
+    Legacy,
+}
+
+pub trait HasWasmCAbiOpt {
+    fn wasm_c_abi_opt(&self) -> WasmCAbi;
+}
+
 type StaticCow<T> = Cow<'static, T>;
 
 /// Optional aspects of a target specification.
@@ -2273,9 +2286,6 @@
     /// distributed with the target, the sanitizer should still appear in this list for the target.
     pub supported_sanitizers: SanitizerSet,
 
-    /// If present it's a default value to use for adjusting the C ABI.
-    pub default_adjusted_cabi: Option<Abi>,
-
     /// Minimum number of bits in #[repr(C)] enum. Defaults to the size of c_int
     pub c_enum_min_bits: Option<u64>,
 
@@ -2507,7 +2517,6 @@
             // `Off` is supported by default, but targets can remove this manually, e.g. Windows.
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
             supported_sanitizers: SanitizerSet::empty(),
-            default_adjusted_cabi: None,
             c_enum_min_bits: None,
             generate_arange_section: true,
             supports_stack_protector: true,
@@ -2538,9 +2547,21 @@
 
 impl Target {
     /// Given a function ABI, turn it into the correct ABI for this target.
-    pub fn adjust_abi(&self, abi: Abi, c_variadic: bool) -> Abi {
+    pub fn adjust_abi<C>(&self, cx: &C, abi: Abi, c_variadic: bool) -> Abi
+    where
+        C: HasWasmCAbiOpt,
+    {
         match abi {
-            Abi::C { .. } => self.default_adjusted_cabi.unwrap_or(abi),
+            Abi::C { .. } => {
+                if self.arch == "wasm32"
+                    && self.os == "unknown"
+                    && cx.wasm_c_abi_opt() == WasmCAbi::Legacy
+                {
+                    Abi::Wasm
+                } else {
+                    abi
+                }
+            }
 
             // On Windows, `extern "system"` behaves like msvc's `__stdcall`.
             // `__stdcall` only applies on x86 and on non-variadic functions:
@@ -3079,16 +3100,6 @@
                     }
                 }
             } );
-            ($key_name:ident, Option<Abi>) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match lookup_abi(s) {
-                        Ok(abi) => base.$key_name = Some(abi),
-                        _ => return Some(Err(format!("'{}' is not a valid value for abi", s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
             ($key_name:ident, TargetFamilies) => ( {
                 if let Some(value) = obj.remove("target-family") {
                     if let Some(v) = value.as_array() {
@@ -3238,7 +3249,6 @@
         key!(split_debuginfo, SplitDebuginfo)?;
         key!(supported_split_debuginfo, fallible_list)?;
         key!(supported_sanitizers, SanitizerSet)?;
-        key!(default_adjusted_cabi, Option<Abi>)?;
         key!(generate_arange_section, bool);
         key!(supports_stack_protector, bool);
         key!(entry_name);
@@ -3502,10 +3512,6 @@
         target_option_val!(entry_abi);
         target_option_val!(supports_xray);
 
-        if let Some(abi) = self.default_adjusted_cabi {
-            d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
-        }
-
         // Serializing `-Clink-self-contained` needs a dynamic key to support the
         // backwards-compatible variants.
         d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json());
diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs
index e743a18..23f4772 100644
--- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs
+++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs
@@ -10,23 +10,12 @@
 //! This target is more or less managed by the Rust and WebAssembly Working
 //! Group nowadays at <https://github.com/rustwasm>.
 
-use crate::spec::abi::Abi;
 use crate::spec::{base, Cc, LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut options = base::wasm::options();
     options.os = "unknown".into();
 
-    // This is a default for backwards-compatibility with the original
-    // definition of this target oh-so-long-ago. Once the "wasm" ABI is
-    // stable and the wasm-bindgen project has switched to using it then there's
-    // no need for this and it can be removed.
-    //
-    // Currently this is the reason that this target's ABI is mismatched with
-    // clang's ABI. This means that, in the limit, you can't merge C and Rust
-    // code on this target due to this ABI mismatch.
-    options.default_adjusted_cabi = Some(Abi::Wasm);
-
     options.add_pre_link_args(
         LinkerFlavor::WasmLld(Cc::No),
         &[
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index c003982..1b507bb 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -279,9 +279,9 @@
     ("d", Unstable(sym::riscv_target_feature)),
     ("e", Unstable(sym::riscv_target_feature)),
     ("f", Unstable(sym::riscv_target_feature)),
-    ("fast-unaligned-access", Unstable(sym::riscv_target_feature)),
     ("m", Stable),
     ("relax", Unstable(sym::riscv_target_feature)),
+    ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature)),
     ("v", Unstable(sym::riscv_target_feature)),
     ("zba", Stable),
     ("zbb", Stable),
@@ -312,14 +312,15 @@
 const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[
     // tidy-alphabetical-start
     ("atomics", Unstable(sym::wasm_target_feature)),
-    ("bulk-memory", Unstable(sym::wasm_target_feature)),
+    ("bulk-memory", Stable),
     ("exception-handling", Unstable(sym::wasm_target_feature)),
+    ("extended-const", Stable),
     ("multivalue", Unstable(sym::wasm_target_feature)),
-    ("mutable-globals", Unstable(sym::wasm_target_feature)),
-    ("nontrapping-fptoint", Unstable(sym::wasm_target_feature)),
+    ("mutable-globals", Stable),
+    ("nontrapping-fptoint", Stable),
     ("reference-types", Unstable(sym::wasm_target_feature)),
     ("relaxed-simd", Unstable(sym::wasm_target_feature)),
-    ("sign-ext", Unstable(sym::wasm_target_feature)),
+    ("sign-ext", Stable),
     ("simd128", Stable),
     // tidy-alphabetical-end
 ];
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 057d00a..d54f714 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -31,7 +31,7 @@
 
 #[macro_use]
 extern crate rustc_macros;
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 #[macro_use]
 extern crate rustc_data_structures;
 #[macro_use]
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 8b5c029..68b0db2 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -240,6 +240,11 @@
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
+    fn consider_builtin_async_destruct_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
     fn consider_builtin_destruct_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
@@ -520,6 +525,8 @@
             G::consider_builtin_coroutine_candidate(self, goal)
         } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
             G::consider_builtin_discriminant_kind_candidate(self, goal)
+        } else if lang_items.async_destruct_trait() == Some(trait_def_id) {
+            G::consider_builtin_async_destruct_candidate(self, goal)
         } else if lang_items.destruct_trait() == Some(trait_def_id) {
             G::consider_builtin_destruct_candidate(self, goal)
         } else if lang_items.transmute_trait() == Some(trait_def_id) {
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index ebf2a0d..c662ab2 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -814,6 +814,59 @@
         })
     }
 
+    fn consider_builtin_async_destruct_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let async_destructor_ty = match *self_ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(..)
+            | ty::Uint(..)
+            | ty::Float(..)
+            | ty::Array(..)
+            | ty::RawPtr(..)
+            | ty::Ref(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(..)
+            | ty::Closure(..)
+            | ty::CoroutineClosure(..)
+            | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
+            | ty::Never
+            | ty::Adt(_, _)
+            | ty::Str
+            | ty::Slice(_)
+            | ty::Tuple(_)
+            | ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env),
+
+            // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder
+            // types, which return `<self_ty as AsyncDestruct>::AsyncDestructor`
+            // (or ICE in the case of placeholders). Projecting a type to itself
+            // is never really productive.
+            ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
+                return Err(NoSolution);
+            }
+
+            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+            | ty::Foreign(..)
+            | ty::Bound(..) => bug!(
+                "unexpected self ty `{:?}` when normalizing `<T as AsyncDestruct>::AsyncDestructor`",
+                goal.predicate.self_ty()
+            ),
+
+            ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => bug!(
+                "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}"
+            ),
+        };
+
+        ecx.probe_misc_candidate("builtin async destruct").enter(|ecx| {
+            ecx.eq(goal.param_env, goal.predicate.term, async_destructor_ty.into())
+                .expect("expected goal term to be fully unconstrained");
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
     fn consider_builtin_destruct_candidate(
         _ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index e522339..ed76ea7 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -544,6 +544,18 @@
         ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 
+    fn consider_builtin_async_destruct_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::PredicatePolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        // `AsyncDestruct` is automatically implemented for every type.
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
     fn consider_builtin_destruct_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 73e94da..f9e7ed9 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -152,7 +152,6 @@
              with {:?}",
             trait_ref, full_env
         );
-        infcx.clear_caches();
 
         // At this point, we already have all of the bounds we need. FulfillmentContext is used
         // to store all of the necessary region/lifetime bounds in the InferContext, as well as
@@ -176,9 +175,7 @@
 
         AutoTraitResult::PositiveImpl(auto_trait_callback(info))
     }
-}
 
-impl<'tcx> AutoTraitFinder<'tcx> {
     /// The core logic responsible for computing the bounds for our synthesized impl.
     ///
     /// To calculate the bounds, we call `SelectionContext.select` in a loop. Like
@@ -255,8 +252,6 @@
         let dummy_cause = ObligationCause::dummy();
 
         while let Some(pred) = predicates.pop_front() {
-            infcx.clear_caches();
-
             if !already_visited.insert(pred) {
                 continue;
             }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 4c1784a..9d3caaa 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -124,7 +124,7 @@
     msg: &str,
     err: &mut Diag<'_, G>,
     fn_sig: Option<&hir::FnSig<'_>>,
-    projection: Option<&ty::AliasTy<'_>>,
+    projection: Option<ty::AliasTy<'_>>,
     trait_pred: ty::PolyTraitPredicate<'tcx>,
     // When we are dealing with a trait, `super_traits` will be `Some`:
     // Given `trait T: A + B + C {}`
@@ -142,7 +142,7 @@
     let generics = tcx.generics_of(item_id);
     // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...
     if let Some((param, bound_str, fn_sig)) =
-        fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() {
+        fn_sig.zip(projection).and_then(|(sig, p)| match *p.self_ty().kind() {
             // Shenanigans to get the `Trait` from the `impl Trait`.
             ty::Param(param) => {
                 let param_def = generics.type_param(param, tcx);
@@ -252,7 +252,7 @@
         let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
 
         let self_ty = trait_pred.skip_binder().self_ty();
-        let (param_ty, projection) = match self_ty.kind() {
+        let (param_ty, projection) = match *self_ty.kind() {
             ty::Param(_) => (true, None),
             ty::Alias(ty::Projection, projection) => (false, Some(projection)),
             _ => (false, None),
@@ -751,9 +751,7 @@
             // Get the local name of this closure. This can be inaccurate because
             // of the possibility of reassignment, but this should be good enough.
             match &kind {
-                hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => {
-                    Some(ident.name)
-                }
+                hir::PatKind::Binding(hir::BindingMode::NONE, _, ident, None) => Some(ident.name),
                 _ => {
                     err.note(msg);
                     None
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index a04a3bc..8cd9f39 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -72,7 +72,7 @@
 }
 
 // `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(PendingPredicateObligation<'_>, 72);
 
 impl<'tcx> FulfillmentContext<'tcx> {
@@ -758,9 +758,9 @@
             // no type variables present, can use evaluation for better caching.
             // FIXME: consider caching errors too.
             if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) {
-                if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate(
+                if let Some(key) = ProjectionCacheKey::from_poly_projection_obligation(
                     &mut self.selcx,
-                    project_obligation.predicate,
+                    &project_obligation,
                 ) {
                     // If `predicate_must_hold_considering_regions` succeeds, then we've
                     // evaluated all sub-obligations. We can therefore mark the 'root'
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 8f5a30c4..88ebf87 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -482,7 +482,7 @@
         type Result = ControlFlow<()>;
         fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
             // If this is a parameter from the trait item's own generics, then bail
-            if let ty::Param(param) = t.kind()
+            if let ty::Param(param) = *t.kind()
                 && let param_def_id = self.generics.type_param(param, self.tcx).def_id
                 && self.tcx.parent(param_def_id) == self.trait_item_def_id
             {
@@ -492,7 +492,7 @@
         }
         fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
             if let ty::ReEarlyParam(param) = r.kind()
-                && let param_def_id = self.generics.region_param(&param, self.tcx).def_id
+                && let param_def_id = self.generics.region_param(param, self.tcx).def_id
                 && self.tcx.parent(param_def_id) == self.trait_item_def_id
             {
                 return ControlFlow::Break(());
@@ -501,7 +501,7 @@
         }
         fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
             if let ty::ConstKind::Param(param) = ct.kind()
-                && let param_def_id = self.generics.const_param(&param, self.tcx).def_id
+                && let param_def_id = self.generics.const_param(param, self.tcx).def_id
                 && self.tcx.parent(param_def_id) == self.trait_item_def_id
             {
                 return ControlFlow::Break(());
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 4a8df6c..116e17c 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -344,7 +344,7 @@
     let use_cache = !selcx.is_intercrate();
 
     let projection_ty = infcx.resolve_vars_if_possible(projection_ty);
-    let cache_key = ProjectionCacheKey::new(projection_ty);
+    let cache_key = ProjectionCacheKey::new(projection_ty, param_env);
 
     // FIXME(#20304) For now, I am caching here, which is good, but it
     // means we don't capture the type variables that are created in
@@ -641,7 +641,7 @@
         );
     }
 
-    match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) {
+    match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::Yes, impl_ty, self_ty) {
         Ok(mut ok) => obligations.append(&mut ok.obligations),
         Err(_) => {
             tcx.dcx().span_bug(
@@ -1074,6 +1074,42 @@
                         | ty::Infer(..)
                         | ty::Error(_) => false,
                     }
+                } else if lang_items.async_destruct_trait() == Some(trait_ref.def_id) {
+                    match self_ty.kind() {
+                        ty::Bool
+                        | ty::Char
+                        | ty::Int(_)
+                        | ty::Uint(_)
+                        | ty::Float(_)
+                        | ty::Adt(..)
+                        | ty::Str
+                        | ty::Array(..)
+                        | ty::Slice(_)
+                        | ty::RawPtr(..)
+                        | ty::Ref(..)
+                        | ty::FnDef(..)
+                        | ty::FnPtr(..)
+                        | ty::Dynamic(..)
+                        | ty::Closure(..)
+                        | ty::CoroutineClosure(..)
+                        | ty::Coroutine(..)
+                        | ty::CoroutineWitness(..)
+                        | ty::Pat(..)
+                        | ty::Never
+                        | ty::Tuple(..)
+                        | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
+
+                        // type parameters, opaques, and unnormalized projections don't have
+                        // a known async destructor and may need to be normalized further or rely
+                        // on param env for async destructor projections
+                        ty::Param(_)
+                        | ty::Foreign(_)
+                        | ty::Alias(..)
+                        | ty::Bound(..)
+                        | ty::Placeholder(..)
+                        | ty::Infer(_)
+                        | ty::Error(_) => false,
+                    }
                 } else if lang_items.pointee_trait() == Some(trait_ref.def_id) {
                     let tail = selcx.tcx().struct_tail_with_normalize(
                         self_ty,
@@ -1488,15 +1524,20 @@
 ) -> Progress<'tcx> {
     let tcx = selcx.tcx();
     let self_ty = obligation.predicate.self_ty();
-    let args = tcx.mk_args(&[self_ty.into()]);
     let lang_items = tcx.lang_items();
     let item_def_id = obligation.predicate.def_id;
     let trait_def_id = tcx.trait_of_item(item_def_id).unwrap();
+    let args = tcx.mk_args(&[self_ty.into()]);
     let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
         let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None);
         assert_eq!(discriminant_def_id, item_def_id);
 
         (self_ty.discriminant_ty(tcx).into(), Vec::new())
+    } else if lang_items.async_destruct_trait() == Some(trait_def_id) {
+        let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0];
+        assert_eq!(destructor_def_id, item_def_id);
+
+        (self_ty.async_destructor_ty(tcx, obligation.param_env).into(), Vec::new())
     } else if lang_items.pointee_trait() == Some(trait_def_id) {
         let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
         assert_eq!(metadata_def_id, item_def_id);
@@ -2105,27 +2146,28 @@
 }
 
 pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized {
-    fn from_poly_projection_predicate(
+    fn from_poly_projection_obligation(
         selcx: &mut SelectionContext<'cx, 'tcx>,
-        predicate: ty::PolyProjectionPredicate<'tcx>,
+        obligation: &PolyProjectionObligation<'tcx>,
     ) -> Option<Self>;
 }
 
 impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> {
-    fn from_poly_projection_predicate(
+    fn from_poly_projection_obligation(
         selcx: &mut SelectionContext<'cx, 'tcx>,
-        predicate: ty::PolyProjectionPredicate<'tcx>,
+        obligation: &PolyProjectionObligation<'tcx>,
     ) -> Option<Self> {
         let infcx = selcx.infcx;
         // We don't do cross-snapshot caching of obligations with escaping regions,
         // so there's no cache key to use
-        predicate.no_bound_vars().map(|predicate| {
+        obligation.predicate.no_bound_vars().map(|predicate| {
             ProjectionCacheKey::new(
                 // We don't attempt to match up with a specific type-variable state
                 // from a specific call to `opt_normalize_projection_type` - if
                 // there's no precise match, the original cache entry is "stranded"
                 // anyway.
                 infcx.resolve_vars_if_possible(predicate.projection_ty),
+                obligation.param_env,
             )
         })
     }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index c415d28..40d206b 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -81,6 +81,9 @@
             } else if lang_items.discriminant_kind_trait() == Some(def_id) {
                 // `DiscriminantKind` is automatically implemented for every type.
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
+            } else if lang_items.async_destruct_trait() == Some(def_id) {
+                // `AsyncDestruct` is automatically implemented for every type.
+                candidates.vec.push(BuiltinCandidate { has_nested: false });
             } else if lang_items.pointee_trait() == Some(def_id) {
                 // `Pointee` is automatically implemented for every type.
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 10370c7..fc12fed 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -815,7 +815,10 @@
                                 // `EvaluatedToOkModuloRegions`), and skip re-evaluating the
                                 // sub-obligations.
                                 if let Some(key) =
-                                    ProjectionCacheKey::from_poly_projection_predicate(self, data)
+                                    ProjectionCacheKey::from_poly_projection_obligation(
+                                        self,
+                                        &project_obligation,
+                                    )
                                 {
                                     if let Some(cached_res) = self
                                         .infcx
@@ -844,8 +847,9 @@
                                     && (eval_rslt == EvaluatedToOk
                                         || eval_rslt == EvaluatedToOkModuloRegions)
                                     && let Some(key) =
-                                        ProjectionCacheKey::from_poly_projection_predicate(
-                                            self, data,
+                                        ProjectionCacheKey::from_poly_projection_obligation(
+                                            self,
+                                            &project_obligation,
                                         )
                                 {
                                     // If the result is something that we can cache, then mark this
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index 12c984f..edd3227 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -420,7 +420,7 @@
         fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
             use rustc_target::abi::Endian;
             let size = tag.size();
-            let bits = tag.to_bits(size).unwrap();
+            let bits = tag.assert_bits(size);
             let bytes: [u8; 16];
             let bytes = match tcx.data_layout.endian {
                 Endian::Little => {
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index 65c3cf1..f0cea1f 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -322,7 +322,7 @@
 #[inline]
 fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi, c_variadic: bool) -> Conv {
     use rustc_target::spec::abi::Abi::*;
-    match tcx.sess.target.adjust_abi(abi, c_variadic) {
+    match tcx.sess.target.adjust_abi(&tcx, abi, c_variadic) {
         RustIntrinsic | Rust | RustCall => Conv::Rust,
 
         // This is intentionally not using `Conv::Cold`, as that has to preserve
diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs
index 51b9088..cb95239 100644
--- a/compiler/rustc_ty_utils/src/common_traits.rs
+++ b/compiler/rustc_ty_utils/src/common_traits.rs
@@ -22,6 +22,17 @@
     is_item_raw(tcx, query, LangItem::Unpin)
 }
 
+fn has_surface_async_drop_raw<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+) -> bool {
+    is_item_raw(tcx, query, LangItem::AsyncDrop)
+}
+
+fn has_surface_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+    is_item_raw(tcx, query, LangItem::Drop)
+}
+
 fn is_item_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
     query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -34,5 +45,13 @@
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
-    *providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers };
+    *providers = Providers {
+        is_copy_raw,
+        is_sized_raw,
+        is_freeze_raw,
+        is_unpin_raw,
+        has_surface_async_drop_raw,
+        has_surface_drop_raw,
+        ..*providers
+    };
 }
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index a8f9afb..c1661fa 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -54,6 +54,28 @@
                 debug!(" => trivial drop glue");
                 ty::InstanceDef::DropGlue(def_id, None)
             }
+        } else if Some(def_id) == tcx.lang_items().async_drop_in_place_fn() {
+            let ty = args.type_at(0);
+
+            if !ty.is_async_destructor_noop(tcx, param_env) {
+                match *ty.kind() {
+                    ty::Closure(..)
+                    | ty::CoroutineClosure(..)
+                    | ty::Coroutine(..)
+                    | ty::Tuple(..)
+                    | ty::Adt(..)
+                    | ty::Dynamic(..)
+                    | ty::Array(..)
+                    | ty::Slice(..) => {}
+                    // Async destructor ctor shims can only be built from ADTs.
+                    _ => return Ok(None),
+                }
+                debug!(" => nontrivial async drop glue ctor");
+                ty::InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty))
+            } else {
+                debug!(" => trivial async drop glue ctor");
+                ty::InstanceDef::AsyncDropGlueCtorShim(def_id, None)
+            }
         } else {
             debug!(" => free item");
             // FIXME(effects): we may want to erase the effect param if that is present on this item.
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index a652bb7..d7d31a8 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -130,7 +130,7 @@
         TaitInBodyFinder { collector: self }.visit_expr(body);
     }
 
-    fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) {
+    fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) {
         if !self.seen.insert(alias_ty.def_id.expect_local()) {
             return;
         }
@@ -205,7 +205,7 @@
     #[instrument(skip(self), ret, level = "trace")]
     fn visit_ty(&mut self, t: Ty<'tcx>) {
         t.super_visit_with(self);
-        match t.kind() {
+        match *t.kind() {
             ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
                 self.visit_opaque_ty(alias_ty);
             }
@@ -279,7 +279,7 @@
                     // assumption to the `param_env` of the default method. We also separately
                     // rely on that assumption here.
                     let ty = self.tcx.type_of(alias_ty.def_id).instantiate(self.tcx, alias_ty.args);
-                    let ty::Alias(ty::Opaque, alias_ty) = ty.kind() else { bug!("{ty:?}") };
+                    let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!("{ty:?}") };
                     self.visit_opaque_ty(alias_ty);
                 }
             }
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs
index 94c5521..e82d1f4 100644
--- a/compiler/stable_mir/src/compiler_interface.rs
+++ b/compiler/stable_mir/src/compiler_interface.rs
@@ -158,6 +158,9 @@
     /// Check if this is an empty DropGlue shim.
     fn is_empty_drop_shim(&self, def: InstanceDef) -> bool;
 
+    /// Check if this is an empty AsyncDropGlueCtor shim.
+    fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool;
+
     /// Convert a non-generic crate item into an instance.
     /// This function will panic if the item is generic.
     fn mono_instance(&self, def_id: DefId) -> Instance;
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 1ad0563..6f66640 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -602,6 +602,7 @@
                 AggregateKind::Coroutine(def, ref args, mov) => {
                     Ok(Ty::new_coroutine(def, args.clone(), mov))
                 }
+                AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)),
             },
             Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)),
             Rvalue::CopyForDeref(place) => place.ty(locals),
@@ -617,6 +618,7 @@
     Closure(ClosureDef, GenericArgs),
     // FIXME(stable_mir): Movability here is redundant
     Coroutine(CoroutineDef, GenericArgs, Movability),
+    RawPtr(Ty, Mutability),
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs
index a032a18..3940389 100644
--- a/compiler/stable_mir/src/mir/mono.rs
+++ b/compiler/stable_mir/src/mir/mono.rs
@@ -157,7 +157,10 @@
     /// When generating code for a Drop terminator, users can ignore an empty drop glue.
     /// These shims are only needed to generate a valid Drop call done via VTable.
     pub fn is_empty_shim(&self) -> bool {
-        self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def))
+        self.kind == InstanceKind::Shim
+            && with(|cx| {
+                cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def)
+            })
     }
 
     /// Try to constant evaluate the instance into a constant with the given type.
diff --git a/config.example.toml b/config.example.toml
index 0d4b4e9..5c1fac7 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -517,11 +517,8 @@
 #overflow-checks-std = rust.overflow-checks (boolean)
 
 # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`.
-# `0` - no debug info
-# `1` - line tables only - sufficient to generate backtraces that include line
-#       information and inlined functions, set breakpoints at source code
-#       locations, and step through execution in a debugger.
-# `2` - full debug info with variable and type information
+# See https://doc.rust-lang.org/rustc/codegen-options/index.html#debuginfo for available options.
+#
 # Can be overridden for specific subsets of Rust code (rustc, std or tools).
 # Debuginfo for tests run with compiletest is not controlled by this option
 # and needs to be enabled separately with `debuginfo-level-tests`.
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index dec04d7..88faf5a 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -126,7 +126,6 @@
 #![feature(extend_one)]
 #![feature(fmt_internals)]
 #![feature(fn_traits)]
-#![feature(generic_nonzero)]
 #![feature(hasher_prefixfree_extras)]
 #![feature(hint_assert_unchecked)]
 #![feature(inline_const)]
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 1930be6..b2e22d8 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1991,15 +1991,17 @@
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_confusables("push_back", "put", "append")]
     pub fn push(&mut self, value: T) {
+        // Inform codegen that the length does not change across grow_one().
+        let len = self.len;
         // This will panic or abort if we would allocate > isize::MAX bytes
         // or if the length increment would overflow for zero-sized types.
-        if self.len == self.buf.capacity() {
+        if len == self.buf.capacity() {
             self.buf.grow_one();
         }
         unsafe {
-            let end = self.as_mut_ptr().add(self.len);
+            let end = self.as_mut_ptr().add(len);
             ptr::write(end, value);
-            self.len += 1;
+            self.len = len + 1;
         }
     }
 
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index a34bce6..b5175a8 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -14,7 +14,6 @@
 #![feature(core_intrinsics)]
 #![feature(extract_if)]
 #![feature(exact_size_is_empty)]
-#![feature(generic_nonzero)]
 #![feature(linked_list_cursors)]
 #![feature(map_try_insert)]
 #![feature(new_uninit)]
diff --git a/library/core/benches/num/int_log/mod.rs b/library/core/benches/num/int_log/mod.rs
index bb61224..3807cd5 100644
--- a/library/core/benches/num/int_log/mod.rs
+++ b/library/core/benches/num/int_log/mod.rs
@@ -1,7 +1,7 @@
 use rand::Rng;
 use test::{black_box, Bencher};
 
-macro_rules! int_log_bench {
+macro_rules! int_log10_bench {
     ($t:ty, $predictable:ident, $random:ident, $random_small:ident) => {
         #[bench]
         fn $predictable(bench: &mut Bencher) {
@@ -51,8 +51,75 @@
     };
 }
 
-int_log_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small}
-int_log_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small}
-int_log_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small}
-int_log_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small}
-int_log_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small}
+int_log10_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small}
+int_log10_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small}
+int_log10_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small}
+int_log10_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small}
+int_log10_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small}
+
+macro_rules! int_log_bench {
+    ($t:ty, $random:ident, $random_small:ident, $geometric:ident) => {
+        #[bench]
+        fn $random(bench: &mut Bencher) {
+            let mut rng = crate::bench_rng();
+            /* Exponentially distributed random numbers from the whole range of the type.  */
+            let numbers: Vec<$t> = (0..256)
+                .map(|_| {
+                    let x = rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS);
+                    if x >= 2 { x } else { 2 }
+                })
+                .collect();
+            bench.iter(|| {
+                for &b in &numbers {
+                    for &x in &numbers {
+                        black_box(black_box(x).ilog(b));
+                    }
+                }
+            });
+        }
+
+        #[bench]
+        fn $random_small(bench: &mut Bencher) {
+            let mut rng = crate::bench_rng();
+            /* Exponentially distributed random numbers from the range 0..256.  */
+            let numbers: Vec<$t> = (0..256)
+                .map(|_| {
+                    let x = (rng.gen::<u8>() >> rng.gen_range(0..u8::BITS)) as $t;
+                    if x >= 2 { x } else { 2 }
+                })
+                .collect();
+            bench.iter(|| {
+                for &b in &numbers {
+                    for &x in &numbers {
+                        black_box(black_box(x).ilog(b));
+                    }
+                }
+            });
+        }
+
+        #[bench]
+        fn $geometric(bench: &mut Bencher) {
+            let bases: [$t; 16] = [2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65];
+            let base_and_numbers: Vec<($t, Vec<$t>)> = bases
+                .iter()
+                .map(|&b| {
+                    let numbers = (0..=<$t>::MAX.ilog(b)).map(|exp| b.pow(exp)).collect();
+                    (b, numbers)
+                })
+                .collect();
+            bench.iter(|| {
+                for (b, numbers) in &base_and_numbers {
+                    for &x in numbers {
+                        black_box(black_box(x).ilog(black_box(*b)));
+                    }
+                }
+            });
+        }
+    };
+}
+
+int_log_bench! {u8, u8_log_random, u8_log_random_small, u8_log_geometric}
+int_log_bench! {u16, u16_log_random, u16_log_random_small, u16_log_geometric}
+int_log_bench! {u32, u32_log_random, u32_log_random_small, u32_log_geometric}
+int_log_bench! {u64, u64_log_random, u64_log_random_small, u64_log_geometric}
+int_log_bench! {u128, u128_log_random, u128_log_random_small, u128_log_geometric}
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index 2a447aa..05874ab 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -512,7 +512,8 @@
     /// # Examples
     ///
     /// ```
-    /// #![feature(array_try_map, generic_nonzero)]
+    /// #![feature(array_try_map)]
+    ///
     /// let a = ["1", "2", "3"];
     /// let b = a.try_map(|v| v.parse::<u32>()).unwrap().map(|v| v + 1);
     /// assert_eq!(b, [2, 3, 4]);
@@ -522,8 +523,10 @@
     /// assert!(b.is_err());
     ///
     /// use std::num::NonZero;
+    ///
     /// let z = [1, 2, 0, 3, 4];
     /// assert_eq!(z.try_map(NonZero::new), None);
+    ///
     /// let a = [1, 2, 3];
     /// let b = a.try_map(NonZero::new);
     /// let c = b.map(|x| x.map(NonZero::get));
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index 65ae483..a93b948 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -927,7 +927,7 @@
     #[must_use]
     #[inline]
     pub(crate) fn is_grapheme_extended(self) -> bool {
-        self > '\x7f' && unicode::Grapheme_Extend(self)
+        unicode::Grapheme_Extend(self)
     }
 
     /// Returns `true` if this `char` has one of the general categories for numbers.
diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs
new file mode 100644
index 0000000..0eb8d7b
--- /dev/null
+++ b/library/core/src/future/async_drop.rs
@@ -0,0 +1,271 @@
+#![unstable(feature = "async_drop", issue = "none")]
+
+use crate::fmt;
+use crate::future::{Future, IntoFuture};
+use crate::intrinsics::discriminant_value;
+use crate::marker::{DiscriminantKind, PhantomPinned};
+use crate::mem::MaybeUninit;
+use crate::pin::Pin;
+use crate::task::{ready, Context, Poll};
+
+/// Asynchronously drops a value by running `AsyncDrop::async_drop`
+/// on a value and its fields recursively.
+#[unstable(feature = "async_drop", issue = "none")]
+pub fn async_drop<T>(value: T) -> AsyncDropOwning<T> {
+    AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned }
+}
+
+/// A future returned by the [`async_drop`].
+#[unstable(feature = "async_drop", issue = "none")]
+pub struct AsyncDropOwning<T> {
+    value: MaybeUninit<T>,
+    dtor: Option<AsyncDropInPlace<T>>,
+    _pinned: PhantomPinned,
+}
+
+#[unstable(feature = "async_drop", issue = "none")]
+impl<T> fmt::Debug for AsyncDropOwning<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("AsyncDropOwning").finish_non_exhaustive()
+    }
+}
+
+#[unstable(feature = "async_drop", issue = "none")]
+impl<T> Future for AsyncDropOwning<T> {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        // SAFETY: Self is pinned thus it is ok to store references to self
+        unsafe {
+            let this = self.get_unchecked_mut();
+            let dtor = Pin::new_unchecked(
+                this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())),
+            );
+            // AsyncDestuctors are idempotent so Self gets idempotency as well
+            dtor.poll(cx)
+        }
+    }
+}
+
+#[lang = "async_drop_in_place"]
+#[allow(unconditional_recursion)]
+// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed?
+unsafe fn async_drop_in_place_raw<T: ?Sized>(
+    to_drop: *mut T,
+) -> <T as AsyncDestruct>::AsyncDestructor {
+    // Code here does not matter - this is replaced by the
+    // real async drop glue constructor by the compiler.
+
+    // SAFETY: see comment above
+    unsafe { async_drop_in_place_raw(to_drop) }
+}
+
+/// Creates the asynchronous destructor of the pointed-to value.
+///
+/// # Safety
+///
+/// Behavior is undefined if any of the following conditions are violated:
+///
+/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes.
+///
+/// * `to_drop` must be properly aligned, even if `T` has size 0.
+///
+/// * `to_drop` must be nonnull, even if `T` has size 0.
+///
+/// * The value `to_drop` points to must be valid for async dropping,
+///   which may mean it must uphold additional invariants. These
+///   invariants depend on the type of the value being dropped. For
+///   instance, when dropping a Box, the box's pointer to the heap must
+///   be valid.
+///
+/// * While `async_drop_in_place` is executing or the returned async
+///   destructor is alive, the only way to access parts of `to_drop`
+///   is through the `self: Pin<&mut Self>` references supplied to
+///   the `AsyncDrop::async_drop` methods that `async_drop_in_place`
+///   or `AsyncDropInPlace<T>::poll` invokes. This usually means the
+///   returned future stores the `to_drop` pointer and user is required
+///   to guarantee that dropped value doesn't move.
+///
+#[unstable(feature = "async_drop", issue = "none")]
+pub unsafe fn async_drop_in_place<T: ?Sized>(to_drop: *mut T) -> AsyncDropInPlace<T> {
+    // SAFETY: `async_drop_in_place_raw` has the same safety requirements
+    unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) }
+}
+
+/// A future returned by the [`async_drop_in_place`].
+#[unstable(feature = "async_drop", issue = "none")]
+pub struct AsyncDropInPlace<T: ?Sized>(<T as AsyncDestruct>::AsyncDestructor);
+
+#[unstable(feature = "async_drop", issue = "none")]
+impl<T: ?Sized> fmt::Debug for AsyncDropInPlace<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("AsyncDropInPlace").finish_non_exhaustive()
+    }
+}
+
+#[unstable(feature = "async_drop", issue = "none")]
+impl<T: ?Sized> Future for AsyncDropInPlace<T> {
+    type Output = ();
+
+    #[inline(always)]
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        // SAFETY: This code simply forwards poll call to the inner future
+        unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx)
+    }
+}
+
+// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as
+//   with Drop impls
+/// Custom code within the asynchronous destructor.
+#[unstable(feature = "async_drop", issue = "none")]
+#[lang = "async_drop"]
+pub trait AsyncDrop {
+    /// A future returned by the [`AsyncDrop::async_drop`] to be part
+    /// of the async destructor.
+    #[unstable(feature = "async_drop", issue = "none")]
+    type Dropper<'a>: Future<Output = ()>
+    where
+        Self: 'a;
+
+    /// Constructs the asynchronous destructor for this type.
+    #[unstable(feature = "async_drop", issue = "none")]
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>;
+}
+
+#[lang = "async_destruct"]
+#[rustc_deny_explicit_impl(implement_via_object = false)]
+trait AsyncDestruct {
+    type AsyncDestructor: Future<Output = ()>;
+}
+
+/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify
+/// generation of the code for `async_drop_in_place_raw`
+#[lang = "surface_async_drop_in_place"]
+async unsafe fn surface_async_drop_in_place<T: AsyncDrop + ?Sized>(ptr: *mut T) {
+    // SAFETY: We call this from async drop `async_drop_in_place_raw`
+    //   which has the same safety requirements
+    unsafe { <T as AsyncDrop>::async_drop(Pin::new_unchecked(&mut *ptr)).await }
+}
+
+/// Basically calls `Drop::drop` with pointer. Used to simplify generation
+/// of the code for `async_drop_in_place_raw`
+#[allow(drop_bounds)]
+#[lang = "async_drop_surface_drop_in_place"]
+async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) {
+    // SAFETY: We call this from async drop `async_drop_in_place_raw`
+    //   which has the same safety requirements
+    unsafe { crate::ops::fallback_surface_drop(&mut *ptr) }
+}
+
+/// Wraps a future to continue outputing `Poll::Ready(())` once after
+/// wrapped future completes by returning `Poll::Ready(())` on poll. This
+/// is useful for constructing async destructors to guarantee this
+/// "fuse" property
+struct Fuse<T> {
+    inner: Option<T>,
+}
+
+#[lang = "async_drop_fuse"]
+fn fuse<T>(inner: T) -> Fuse<T> {
+    Fuse { inner: Some(inner) }
+}
+
+impl<T> Future for Fuse<T>
+where
+    T: Future<Output = ()>,
+{
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        // SAFETY: pin projection into `self.inner`
+        unsafe {
+            let this = self.get_unchecked_mut();
+            if let Some(inner) = &mut this.inner {
+                ready!(Pin::new_unchecked(inner).poll(cx));
+                this.inner = None;
+            }
+        }
+        Poll::Ready(())
+    }
+}
+
+/// Async destructor for arrays and slices.
+#[lang = "async_drop_slice"]
+async unsafe fn slice<T>(s: *mut [T]) {
+    let len = s.len();
+    let ptr = s.as_mut_ptr();
+    for i in 0..len {
+        // SAFETY: we iterate over elements of `s` slice
+        unsafe { async_drop_in_place_raw(ptr.add(i)).await }
+    }
+}
+
+/// Construct a chain of two futures, which awaits them sequentially as
+/// a future.
+#[lang = "async_drop_chain"]
+async fn chain<F, G>(first: F, last: G)
+where
+    F: IntoFuture<Output = ()>,
+    G: IntoFuture<Output = ()>,
+{
+    first.await;
+    last.await;
+}
+
+/// Basically a lazy version of `async_drop_in_place`. Returns a future
+/// that would call `AsyncDrop::async_drop` on a first poll.
+///
+/// # Safety
+///
+/// Same as `async_drop_in_place` except is lazy to avoid creating
+/// multiple mutable refernces.
+#[lang = "async_drop_defer"]
+async unsafe fn defer<T: ?Sized>(to_drop: *mut T) {
+    // SAFETY: same safety requirements as `async_drop_in_place`
+    unsafe { async_drop_in_place(to_drop) }.await
+}
+
+/// If `T`'s discriminant is equal to the stored one then awaits `M`
+/// otherwise awaits the `O`.
+///
+/// # Safety
+///
+/// User should carefully manage returned future, since it would
+/// try creating an immutable referece from `this` and get pointee's
+/// discriminant.
+// FIXME(zetanumbers): Send and Sync impls
+#[lang = "async_drop_either"]
+async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T>(
+    other: O,
+    matched: M,
+    this: *mut T,
+    discr: <T as DiscriminantKind>::Discriminant,
+) {
+    // SAFETY: Guaranteed by the safety section of this funtion's documentation
+    if unsafe { discriminant_value(&*this) } == discr {
+        drop(other);
+        matched.await
+    } else {
+        drop(matched);
+        other.await
+    }
+}
+
+/// Used for noop async destructors. We don't use [`core::future::Ready`]
+/// because it panics after its second poll, which could be potentially
+/// bad if that would happen during the cleanup.
+#[derive(Clone, Copy)]
+struct Noop;
+
+#[lang = "async_drop_noop"]
+fn noop() -> Noop {
+    Noop
+}
+
+impl Future for Noop {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
+        Poll::Ready(())
+    }
+}
diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs
index 0f77a2d..c3bd18e 100644
--- a/library/core/src/future/mod.rs
+++ b/library/core/src/future/mod.rs
@@ -12,6 +12,8 @@
 use crate::ptr::NonNull;
 use crate::task::Context;
 
+#[cfg(not(bootstrap))]
+mod async_drop;
 mod future;
 mod into_future;
 mod join;
@@ -36,6 +38,10 @@
 #[stable(feature = "future_poll_fn", since = "1.64.0")]
 pub use poll_fn::{poll_fn, PollFn};
 
+#[cfg(not(bootstrap))]
+#[unstable(feature = "async_drop", issue = "none")]
+pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace};
+
 /// This type is needed because:
 ///
 /// a) Coroutines cannot implement `for<'a, 'b> Coroutine<&'a mut Context<'b>>`, so we need to pass
diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index b27d0db..6e2d88c 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -237,7 +237,7 @@
         crate::arch::riscv64::pause();
     }
 
-    #[cfg(target_arch = "aarch64")]
+    #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))]
     {
         // SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets.
         unsafe { crate::arch::aarch64::__isb(crate::arch::aarch64::SY) };
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 9406efd..92f1bd2 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2779,6 +2779,34 @@
     unreachable!()
 }
 
+/// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`.
+///
+/// This is used to implement functions like `slice::from_raw_parts_mut` and
+/// `ptr::from_raw_parts` in a way compatible with the compiler being able to
+/// change the possible layouts of pointers.
+#[rustc_nounwind]
+#[unstable(feature = "core_intrinsics", issue = "none")]
+#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
+#[rustc_intrinsic]
+#[rustc_intrinsic_must_be_overridden]
+#[cfg(not(bootstrap))]
+pub const fn aggregate_raw_ptr<P: AggregateRawPtr<D, Metadata = M>, D, M>(_data: D, _meta: M) -> P {
+    // To implement a fallback we'd have to assume the layout of the pointer,
+    // but the whole point of this intrinsic is that we shouldn't do that.
+    unreachable!()
+}
+
+#[unstable(feature = "core_intrinsics", issue = "none")]
+pub trait AggregateRawPtr<D> {
+    type Metadata: Copy;
+}
+impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*const T> for *const P {
+    type Metadata = <P as ptr::Pointee>::Metadata;
+}
+impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
+    type Metadata = <P as ptr::Pointee>::Metadata;
+}
+
 // Some functions are defined here because they accidentally got made
 // available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
 // (`transmute` also falls into this category, but it cannot be wrapped due to the
diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs
index 8c68ea1..8f1744f 100644
--- a/library/core/src/iter/adapters/array_chunks.rs
+++ b/library/core/src/iter/adapters/array_chunks.rs
@@ -34,9 +34,22 @@
     /// Returns an iterator over the remaining elements of the original iterator
     /// that are not going to be returned by this iterator. The returned
     /// iterator will yield at most `N-1` elements.
+    ///
+    /// # Example
+    /// ```
+    /// # // Also serves as a regression test for https://github.com/rust-lang/rust/issues/123333
+    /// # #![feature(iter_array_chunks)]
+    /// let x = [1,2,3,4,5].into_iter().array_chunks::<2>();
+    /// let mut rem = x.into_remainder().unwrap();
+    /// assert_eq!(rem.next(), Some(5));
+    /// assert_eq!(rem.next(), None);
+    /// ```
     #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
     #[inline]
-    pub fn into_remainder(self) -> Option<array::IntoIter<I::Item, N>> {
+    pub fn into_remainder(mut self) -> Option<array::IntoIter<I::Item, N>> {
+        if self.remainder.is_none() {
+            while let Some(_) = self.next() {}
+        }
         self.remainder
     }
 }
diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs
index 35092ec..3b12678 100644
--- a/library/core/src/iter/traits/double_ended.rs
+++ b/library/core/src/iter/traits/double_ended.rs
@@ -118,7 +118,8 @@
     /// Basic usage:
     ///
     /// ```
-    /// #![feature(generic_nonzero, iter_advance_by)]
+    /// #![feature(iter_advance_by)]
+    ///
     /// use std::num::NonZero;
     ///
     /// let a = [3, 4, 5, 6];
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 95c0304..7c1c612 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -288,7 +288,8 @@
     /// # Examples
     ///
     /// ```
-    /// #![feature(generic_nonzero, iter_advance_by)]
+    /// #![feature(iter_advance_by)]
+    ///
     /// use std::num::NonZero;
     ///
     /// let a = [1, 2, 3, 4];
@@ -2939,7 +2940,8 @@
     /// This also supports other types which implement [`Try`], not just [`Result`].
     ///
     /// ```
-    /// #![feature(generic_nonzero, try_find)]
+    /// #![feature(try_find)]
+    ///
     /// use std::num::NonZero;
     ///
     /// let a = [3, 5, 7, 4, 9, 0, 11u32];
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index e34e9b7f..a92da88 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -1163,12 +1163,19 @@
         /// ```
         #[stable(feature = "wrapping", since = "1.7.0")]
         #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
+        // We could always go back to wrapping
+        #[rustc_allow_const_fn_unstable(unchecked_shifts)]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
         pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
-            let (a, b) = self.overflowing_shl(rhs);
-            if unlikely!(b) { None } else { Some(a) }
+            // Not using overflowing_shl as that's a wrapping shift
+            if rhs < Self::BITS {
+                // SAFETY: just checked the RHS is in-range
+                Some(unsafe { self.unchecked_shl(rhs) })
+            } else {
+                None
+            }
         }
 
         /// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger
@@ -1254,12 +1261,19 @@
         /// ```
         #[stable(feature = "wrapping", since = "1.7.0")]
         #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
+        // We could always go back to wrapping
+        #[rustc_allow_const_fn_unstable(unchecked_shifts)]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
         pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
-            let (a, b) = self.overflowing_shr(rhs);
-            if unlikely!(b) { None } else { Some(a) }
+            // Not using overflowing_shr as that's a wrapping shift
+            if rhs < Self::BITS {
+                // SAFETY: just checked the RHS is in-range
+                Some(unsafe { self.unchecked_shr(rhs) })
+            } else {
+                None
+            }
         }
 
         /// Strict shift right. Computes `self >> rhs`, panicking `rhs` is
@@ -3109,21 +3123,9 @@
             if self <= 0 || base <= 1 {
                 None
             } else {
-                let mut n = 0;
-                let mut r = self;
-
-                // Optimization for 128 bit wide integers.
-                if Self::BITS == 128 {
-                    let b = Self::ilog2(self) / (Self::ilog2(base) + 1);
-                    n += b;
-                    r /= base.pow(b as u32);
-                }
-
-                while r >= base {
-                    r /= base;
-                    n += 1;
-                }
-                Some(n)
+                // Delegate to the unsigned implementation.
+                // The condition makes sure that both casts are exact.
+                (self as $UnsignedT).checked_ilog(base as $UnsignedT)
             }
         }
 
@@ -3185,7 +3187,8 @@
         /// that code in debug mode will trigger a panic on this case and
         /// optimized code will return
         #[doc = concat!("`", stringify!($SelfT), "::MIN`")]
-        /// without a panic.
+        /// without a panic. If you do not want this behavior, consider
+        /// using [`unsigned_abs`](Self::unsigned_abs) instead.
         ///
         /// # Examples
         ///
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 9ebbb4f..443401c 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -67,15 +67,15 @@
 )]
 pub use nonzero::ZeroablePrimitive;
 
-#[unstable(feature = "generic_nonzero", issue = "120257")]
+#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")]
 pub use nonzero::NonZero;
 
-#[stable(feature = "nonzero", since = "1.28.0")]
-pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
-
 #[stable(feature = "signed_nonzero", since = "1.34.0")]
 pub use nonzero::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize};
 
+#[stable(feature = "nonzero", since = "1.28.0")]
+pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
+
 #[stable(feature = "try_from", since = "1.34.0")]
 pub use error::TryFromIntError;
 
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index 62ea7ab..b9d771b 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -105,12 +105,11 @@
 /// For example, `Option<NonZero<u32>>` is the same size as `u32`:
 ///
 /// ```
-/// #![feature(generic_nonzero)]
-/// use core::mem::size_of;
+/// use core::{mem::size_of, num::NonZero};
 ///
-/// assert_eq!(size_of::<Option<core::num::NonZero<u32>>>(), size_of::<u32>());
+/// assert_eq!(size_of::<Option<NonZero<u32>>>(), size_of::<u32>());
 /// ```
-#[unstable(feature = "generic_nonzero", issue = "120257")]
+#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")]
 #[repr(transparent)]
 #[rustc_nonnull_optimization_guaranteed]
 #[rustc_diagnostic_item = "NonZero"]
@@ -562,7 +561,8 @@
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(generic_nonzero, non_zero_count_ones)]
+            /// #![feature(non_zero_count_ones)]
+            ///
             /// # fn main() { test().unwrap(); }
             /// # fn test() -> Option<()> {
             /// # use std::num::*;
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index ba6a243..c137632 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -579,8 +579,17 @@
                       without modifying the original"]
         #[inline]
         pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
-            let (a, b) = self.overflowing_sub(rhs);
-            if unlikely!(b) { None } else { Some(a) }
+            // Per PR#103299, there's no advantage to the `overflowing` intrinsic
+            // for *unsigned* subtraction and we just emit the manual check anyway.
+            // Thus, rather than using `overflowing_sub` that produces a wrapping
+            // subtraction, check it ourself so we can use an unchecked one.
+
+            if self >= rhs {
+                // SAFETY: just checked this can't overflow
+                Some(unsafe { intrinsics::unchecked_sub(self, rhs) })
+            } else {
+                None
+            }
         }
 
         /// Strict integer subtraction. Computes `self - rhs`, panicking if
@@ -1086,18 +1095,25 @@
                 None
             } else {
                 let mut n = 0;
-                let mut r = self;
+                let mut r = 1;
 
                 // Optimization for 128 bit wide integers.
                 if Self::BITS == 128 {
-                    let b = Self::ilog2(self) / (Self::ilog2(base) + 1);
-                    n += b;
-                    r /= base.pow(b as u32);
+                    // The following is a correct lower bound for ⌊log(base,self)⌋ because
+                    //
+                    // log(base,self) = log(2,self) / log(2,base)
+                    //                ≥ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1)
+                    //
+                    // hence
+                    //
+                    // ⌊log(base,self)⌋ ≥ ⌊ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) ⌋ .
+                    n = self.ilog2() / (base.ilog2() + 1);
+                    r = base.pow(n);
                 }
 
-                while r >= base {
-                    r /= base;
+                while r <= self / base {
                     n += 1;
+                    r *= base;
                 }
                 Some(n)
             }
@@ -1222,12 +1238,19 @@
         /// ```
         #[stable(feature = "wrapping", since = "1.7.0")]
         #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
+        // We could always go back to wrapping
+        #[rustc_allow_const_fn_unstable(unchecked_shifts)]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
         pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
-            let (a, b) = self.overflowing_shl(rhs);
-            if unlikely!(b) { None } else { Some(a) }
+            // Not using overflowing_shl as that's a wrapping shift
+            if rhs < Self::BITS {
+                // SAFETY: just checked the RHS is in-range
+                Some(unsafe { self.unchecked_shl(rhs) })
+            } else {
+                None
+            }
         }
 
         /// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger
@@ -1313,12 +1336,19 @@
         /// ```
         #[stable(feature = "wrapping", since = "1.7.0")]
         #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
+        // We could always go back to wrapping
+        #[rustc_allow_const_fn_unstable(unchecked_shifts)]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
         pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
-            let (a, b) = self.overflowing_shr(rhs);
-            if unlikely!(b) { None } else { Some(a) }
+            // Not using overflowing_shr as that's a wrapping shift
+            if rhs < Self::BITS {
+                // SAFETY: just checked the RHS is in-range
+                Some(unsafe { self.unchecked_shr(rhs) })
+            } else {
+                None
+            }
         }
 
         /// Strict shift right. Computes `self >> rhs`, panicking `rhs` is
diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs
index 34dfa9e..1325d90 100644
--- a/library/core/src/ops/drop.rs
+++ b/library/core/src/ops/drop.rs
@@ -238,3 +238,11 @@
     #[stable(feature = "rust1", since = "1.0.0")]
     fn drop(&mut self);
 }
+
+/// Fallback function to call surface level `Drop::drop` function
+#[cfg(not(bootstrap))]
+#[allow(drop_bounds)]
+#[lang = "fallback_surface_drop"]
+pub(crate) fn fallback_surface_drop<T: Drop + ?Sized>(x: &mut T) {
+    <T as Drop>::drop(x)
+}
diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs
index ac808be..81d5e5c 100644
--- a/library/core/src/ops/mod.rs
+++ b/library/core/src/ops/mod.rs
@@ -174,6 +174,9 @@
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::drop::Drop;
 
+#[cfg(not(bootstrap))]
+pub(crate) use self::drop::fallback_surface_drop;
+
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::function::{Fn, FnMut, FnOnce};
 
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index e8e23f2..bda1ee6 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -537,7 +537,11 @@
 /// ## 4. Get it from C.
 ///
 /// ```
-/// # #![feature(rustc_private)]
+/// # mod libc {
+/// # pub unsafe fn malloc(_size: usize) -> *mut core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() }
+/// # pub unsafe fn free(_ptr: *mut core::ffi::c_void) {}
+/// # }
+/// # #[cfg(any())]
 /// #[allow(unused_extern_crates)]
 /// extern crate libc;
 ///
@@ -548,7 +552,7 @@
 ///     if my_num.is_null() {
 ///         panic!("failed to allocate memory");
 ///     }
-///     libc::free(my_num as *mut libc::c_void);
+///     libc::free(my_num as *mut core::ffi::c_void);
 /// }
 /// ```
 ///
diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs
index 25a06f1..1226c8e 100644
--- a/library/core/src/ptr/metadata.rs
+++ b/library/core/src/ptr/metadata.rs
@@ -2,6 +2,8 @@
 
 use crate::fmt;
 use crate::hash::{Hash, Hasher};
+#[cfg(not(bootstrap))]
+use crate::intrinsics::aggregate_raw_ptr;
 use crate::marker::Freeze;
 
 /// Provides the pointer metadata type of any pointed-to type.
@@ -113,10 +115,17 @@
     data_pointer: *const (),
     metadata: <T as Pointee>::Metadata,
 ) -> *const T {
-    // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
-    // and PtrComponents<T> have the same memory layouts. Only std can make this
-    // guarantee.
-    unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr }
+    #[cfg(bootstrap)]
+    {
+        // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
+        // and PtrComponents<T> have the same memory layouts. Only std can make this
+        // guarantee.
+        unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr }
+    }
+    #[cfg(not(bootstrap))]
+    {
+        aggregate_raw_ptr(data_pointer, metadata)
+    }
 }
 
 /// Performs the same functionality as [`from_raw_parts`], except that a
@@ -130,10 +139,17 @@
     data_pointer: *mut (),
     metadata: <T as Pointee>::Metadata,
 ) -> *mut T {
-    // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
-    // and PtrComponents<T> have the same memory layouts. Only std can make this
-    // guarantee.
-    unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr }
+    #[cfg(bootstrap)]
+    {
+        // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
+        // and PtrComponents<T> have the same memory layouts. Only std can make this
+        // guarantee.
+        unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr }
+    }
+    #[cfg(not(bootstrap))]
+    {
+        aggregate_raw_ptr(data_pointer, metadata)
+    }
 }
 
 #[repr(C)]
diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs
index ba282f0..b6ffb0a 100644
--- a/library/core/src/str/converts.rs
+++ b/library/core/src/str/converts.rs
@@ -239,7 +239,7 @@
 #[must_use]
 #[unstable(feature = "str_from_raw_parts", issue = "119206")]
 #[rustc_const_unstable(feature = "const_str_from_raw_parts_mut", issue = "119206")]
-pub const unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a str {
+pub const unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut str {
     // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
     unsafe { &mut *ptr::from_raw_parts_mut(ptr.cast(), len) }
 }
diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs
index dd2ad9a..1b3d672 100644
--- a/library/core/src/unicode/unicode_data.rs
+++ b/library/core/src/unicode/unicode_data.rs
@@ -315,7 +315,11 @@
         15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 100, 1, 160, 7, 0, 1, 61, 4, 0, 4, 0, 7, 109, 7, 0, 96,
         128, 240, 0,
     ];
+    #[inline]
     pub fn lookup(c: char) -> bool {
+        (c as u32) >= 0x300 && lookup_slow(c)
+    }
+    fn lookup_slow(c: char) -> bool {
         super::skip_search(
             c as u32,
             &SHORT_OFFSET_RUNS,
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index e741149..7bd962f 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -42,7 +42,6 @@
 #![feature(float_minimum_maximum)]
 #![feature(future_join)]
 #![feature(generic_assert_internals)]
-#![feature(generic_nonzero)]
 #![feature(array_try_from_fn)]
 #![feature(hasher_prefixfree_extras)]
 #![feature(hashmap_internals)]
diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs
index 2c82eda..7b55c2b 100644
--- a/library/core/tests/ptr.rs
+++ b/library/core/tests/ptr.rs
@@ -1163,3 +1163,11 @@
     assert!(ptr.is_null());
     assert_eq!(ptr.len(), 4);
 }
+
+#[test]
+fn test_ptr_from_raw_parts_in_const() {
+    const EMPTY_SLICE_PTR: *const [i32] =
+        std::ptr::slice_from_raw_parts(std::ptr::without_provenance(123), 456);
+    assert_eq!(EMPTY_SLICE_PTR.addr(), 123);
+    assert_eq!(EMPTY_SLICE_PTR.len(), 456);
+}
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
index c5743ed..ffe8ffc 100644
--- a/library/core/tests/slice.rs
+++ b/library/core/tests/slice.rs
@@ -2678,3 +2678,16 @@
     let mut v = vec![1, 2, 3, 4, 5];
     assert!(v.get_many_mut([1, 3, 3, 4]).is_err());
 }
+
+#[test]
+fn test_slice_from_raw_parts_in_const() {
+    static FANCY: i32 = 4;
+    static FANCY_SLICE: &[i32] = unsafe { std::slice::from_raw_parts(&FANCY, 1) };
+    assert_eq!(FANCY_SLICE.as_ptr(), std::ptr::addr_of!(FANCY));
+    assert_eq!(FANCY_SLICE.len(), 1);
+
+    const EMPTY_SLICE: &[i32] =
+        unsafe { std::slice::from_raw_parts(std::ptr::without_provenance(123456), 0) };
+    assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456);
+    assert_eq!(EMPTY_SLICE.len(), 0);
+}
diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs
index a6d07fd..353de8c 100644
--- a/library/panic_abort/src/lib.rs
+++ b/library/panic_abort/src/lib.rs
@@ -8,6 +8,7 @@
 #![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]
 #![panic_runtime]
 #![allow(unused_features)]
+#![feature(asm_experimental_arch)]
 #![feature(core_intrinsics)]
 #![feature(panic_runtime)]
 #![feature(std_internals)]
@@ -78,7 +79,7 @@
                         core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
                     } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] {
                         core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
-                    } else if #[cfg(target_arch = "aarch64")] {
+                    } else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] {
                         core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
                     } else {
                         core::intrinsics::abort();
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index c8db028..1ceff2e 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -26,7 +26,6 @@
 #![feature(staged_api)]
 #![feature(allow_internal_unstable)]
 #![feature(decl_macro)]
-#![feature(generic_nonzero)]
 #![feature(maybe_uninit_write_slice)]
 #![feature(negative_impls)]
 #![feature(new_uninit)]
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 1293abd..409ead0 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -214,7 +214,7 @@
 /// A structure representing a type of file with accessors for each file type.
 /// It is returned by [`Metadata::file_type`] method.
 #[stable(feature = "file_type", since = "1.1.0")]
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 #[cfg_attr(not(test), rustc_diagnostic_item = "FileType")]
 pub struct FileType(fs_imp::FileType);
 
@@ -1410,15 +1410,20 @@
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Metadata {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Metadata")
-            .field("file_type", &self.file_type())
-            .field("is_dir", &self.is_dir())
-            .field("is_file", &self.is_file())
-            .field("permissions", &self.permissions())
-            .field("modified", &self.modified())
-            .field("accessed", &self.accessed())
-            .field("created", &self.created())
-            .finish_non_exhaustive()
+        let mut debug = f.debug_struct("Metadata");
+        debug.field("file_type", &self.file_type());
+        debug.field("permissions", &self.permissions());
+        debug.field("len", &self.len());
+        if let Ok(modified) = self.modified() {
+            debug.field("modified", &modified);
+        }
+        if let Ok(accessed) = self.accessed() {
+            debug.field("accessed", &accessed);
+        }
+        if let Ok(created) = self.created() {
+            debug.field("created", &created);
+        }
+        debug.finish_non_exhaustive()
     }
 }
 
@@ -1684,6 +1689,17 @@
     }
 }
 
+#[stable(feature = "std_debug", since = "1.16.0")]
+impl fmt::Debug for FileType {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("FileType")
+            .field("is_file", &self.is_file())
+            .field("is_dir", &self.is_dir())
+            .field("is_symlink", &self.is_symlink())
+            .finish_non_exhaustive()
+    }
+}
+
 impl AsInner<fs_imp::FileType> for FileType {
     #[inline]
     fn as_inner(&self) -> &fs_imp::FileType {
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index d152626..aa908f0 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -277,6 +277,7 @@
 #![feature(allocator_internals)]
 #![feature(allow_internal_unsafe)]
 #![feature(allow_internal_unstable)]
+#![feature(asm_experimental_arch)]
 #![feature(c_unwind)]
 #![feature(cfg_sanitizer_cfi)]
 #![feature(cfg_target_thread_local)]
@@ -334,7 +335,6 @@
 #![feature(float_minimum_maximum)]
 #![feature(float_next_up_down)]
 #![feature(fmt_internals)]
-#![feature(generic_nonzero)]
 #![feature(hasher_prefixfree_extras)]
 #![feature(hashmap_internals)]
 #![feature(hint_assert_unchecked)]
diff --git a/library/std/src/num.rs b/library/std/src/num.rs
index 1343fdf..fbe68f7 100644
--- a/library/std/src/num.rs
+++ b/library/std/src/num.rs
@@ -23,11 +23,12 @@
 )]
 pub use core::num::ZeroablePrimitive;
 
-#[unstable(feature = "generic_nonzero", issue = "120257")]
+#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")]
 pub use core::num::NonZero;
 
 #[stable(feature = "signed_nonzero", since = "1.34.0")]
 pub use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize};
+
 #[stable(feature = "nonzero", since = "1.28.0")]
 pub use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
 
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 0052fcb..5699937 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -277,6 +277,13 @@
                         "note: run with `RUST_BACKTRACE=1` environment variable to display a \
                              backtrace"
                     );
+                    if cfg!(miri) {
+                        let _ = writeln!(
+                            err,
+                            "note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` \
+                                for the environment variable to have an effect"
+                        );
+                    }
                 }
             }
             // If backtraces aren't supported or are forced-off, do nothing.
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 69cc61b..4a73a9b 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1865,7 +1865,8 @@
     /// # Examples
     ///
     /// ```
-    /// #![feature(exit_status_error, generic_nonzero)]
+    /// #![feature(exit_status_error)]
+    ///
     /// # if cfg!(unix) {
     /// use std::num::NonZero;
     /// use std::process::Command;
diff --git a/library/std/src/sys/pal/common/alloc.rs b/library/std/src/sys/pal/common/alloc.rs
index 8cf9ef6..598b6db 100644
--- a/library/std/src/sys/pal/common/alloc.rs
+++ b/library/std/src/sys/pal/common/alloc.rs
@@ -23,6 +23,7 @@
 #[cfg(any(
     target_arch = "x86_64",
     target_arch = "aarch64",
+    target_arch = "arm64ec",
     target_arch = "loongarch64",
     target_arch = "mips64",
     target_arch = "mips64r6",
diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs
index 48e83b0..203c718 100644
--- a/library/std/src/sys/pal/unix/fd.rs
+++ b/library/std/src/sys/pal/unix/fd.rs
@@ -45,13 +45,9 @@
 #[cfg(any(
     target_os = "dragonfly",
     target_os = "freebsd",
-    target_os = "ios",
-    target_os = "tvos",
-    target_os = "macos",
     target_os = "netbsd",
     target_os = "openbsd",
-    target_os = "watchos",
-    target_os = "visionos",
+    target_vendor = "apple",
 ))]
 const fn max_iov() -> usize {
     libc::IOV_MAX as usize
@@ -72,17 +68,13 @@
     target_os = "dragonfly",
     target_os = "emscripten",
     target_os = "freebsd",
-    target_os = "ios",
-    target_os = "tvos",
     target_os = "linux",
-    target_os = "macos",
     target_os = "netbsd",
     target_os = "nto",
     target_os = "openbsd",
     target_os = "horizon",
     target_os = "vita",
-    target_os = "watchos",
-    target_os = "visionos",
+    target_vendor = "apple",
 )))]
 const fn max_iov() -> usize {
     16 // The minimum value required by POSIX.
@@ -201,13 +193,10 @@
         target_os = "fuchsia",
         target_os = "hurd",
         target_os = "illumos",
-        target_os = "ios",
-        target_os = "tvos",
         target_os = "linux",
-        target_os = "macos",
         target_os = "netbsd",
         target_os = "openbsd",
-        target_os = "watchos",
+        target_vendor = "apple",
     )))]
     pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
         io::default_read_vectored(|b| self.read_at(b, offset), bufs)
@@ -241,15 +230,7 @@
         Ok(ret as usize)
     }
 
-    // We support old MacOS and iOS versions that do not have `preadv`. There is
-    // no `syscall` possible in these platform.
-    #[cfg(any(
-        all(target_os = "android", target_pointer_width = "32"),
-        target_os = "ios", // ios 14.0
-        target_os = "tvos", // tvos 14.0
-        target_os = "macos", // macos 11.0
-        target_os = "watchos", // watchos 7.0
-    ))]
+    #[cfg(all(target_os = "android", target_pointer_width = "32"))]
     pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
         super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
 
@@ -269,6 +250,35 @@
         }
     }
 
+    // We support old MacOS, iOS, watchOS, tvOS and visionOS. `preadv` was added in the following
+    // Apple OS versions:
+    // ios 14.0
+    // tvos 14.0
+    // macos 11.0
+    // watchos 7.0
+    //
+    // These versions may be newer than the minimum supported versions of OS's we support so we must
+    // use "weak" linking.
+    #[cfg(target_vendor = "apple")]
+    pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        super::weak::weak!(fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
+
+        match preadv.get() {
+            Some(preadv) => {
+                let ret = cvt(unsafe {
+                    preadv(
+                        self.as_raw_fd(),
+                        bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
+                        cmp::min(bufs.len(), max_iov()) as libc::c_int,
+                        offset as _,
+                    )
+                })?;
+                Ok(ret as usize)
+            }
+            None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
+        }
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         let ret = cvt(unsafe {
             libc::write(
@@ -360,13 +370,10 @@
         target_os = "fuchsia",
         target_os = "hurd",
         target_os = "illumos",
-        target_os = "ios",
-        target_os = "tvos",
         target_os = "linux",
-        target_os = "macos",
         target_os = "netbsd",
         target_os = "openbsd",
-        target_os = "watchos",
+        target_vendor = "apple",
     )))]
     pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
         io::default_write_vectored(|b| self.write_at(b, offset), bufs)
@@ -400,15 +407,7 @@
         Ok(ret as usize)
     }
 
-    // We support old MacOS and iOS versions that do not have `pwritev`. There is
-    // no `syscall` possible in these platform.
-    #[cfg(any(
-        all(target_os = "android", target_pointer_width = "32"),
-        target_os = "ios", // ios 14.0
-        target_os = "tvos", // tvos 14.0
-        target_os = "macos", // macos 11.0
-        target_os = "watchos", // watchos 7.0
-    ))]
+    #[cfg(all(target_os = "android", target_pointer_width = "32"))]
     pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
         super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
 
@@ -428,6 +427,35 @@
         }
     }
 
+    // We support old MacOS, iOS, watchOS, tvOS and visionOS. `pwritev` was added in the following
+    // Apple OS versions:
+    // ios 14.0
+    // tvos 14.0
+    // macos 11.0
+    // watchos 7.0
+    //
+    // These versions may be newer than the minimum supported versions of OS's we support so we must
+    // use "weak" linking.
+    #[cfg(target_vendor = "apple")]
+    pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        super::weak::weak!(fn pwritev(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
+
+        match pwritev.get() {
+            Some(pwritev) => {
+                let ret = cvt(unsafe {
+                    pwritev(
+                        self.as_raw_fd(),
+                        bufs.as_ptr() as *const libc::iovec,
+                        cmp::min(bufs.len(), max_iov()) as libc::c_int,
+                        offset as _,
+                    )
+                })?;
+                Ok(ret as usize)
+            }
+            None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
+        }
+    }
+
     #[cfg(not(any(
         target_env = "newlib",
         target_os = "solaris",
diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs
index 48cc863..4765a28 100644
--- a/library/std/src/sys/pal/unix/weak.rs
+++ b/library/std/src/sys/pal/unix/weak.rs
@@ -28,7 +28,7 @@
 use crate::sync::atomic::{self, AtomicPtr, Ordering};
 
 // We can use true weak linkage on ELF targets.
-#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos")))]
+#[cfg(all(unix, not(target_vendor = "apple")))]
 pub(crate) macro weak {
     (fn $name:ident($($t:ty),*) -> $ret:ty) => (
         let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
@@ -43,7 +43,7 @@
 }
 
 // On non-ELF targets, use the dlsym approximation of weak linkage.
-#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))]
+#[cfg(target_vendor = "apple")]
 pub(crate) use self::dlsym as weak;
 
 pub(crate) struct ExternWeak<F: Copy> {
diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs
index 1c828ba..9d58ce0 100644
--- a/library/std/src/sys/pal/windows/c.rs
+++ b/library/std/src/sys/pal/windows/c.rs
@@ -357,7 +357,19 @@
 }
 
 #[cfg(not(target_vendor = "win7"))]
-#[link(name = "synchronization")]
+// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library.
+#[cfg_attr(
+    target_arch = "x86",
+    link(
+        name = "api-ms-win-core-synch-l1-2-0",
+        kind = "raw-dylib",
+        import_name_type = "undecorated"
+    )
+)]
+#[cfg_attr(
+    not(target_arch = "x86"),
+    link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib")
+)]
 extern "system" {
     pub fn WaitOnAddress(
         address: *const c_void,
diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs
index d180122..1da8871 100644
--- a/library/std/src/sys/pal/windows/c/windows_sys.rs
+++ b/library/std/src/sys/pal/windows/c/windows_sys.rs
@@ -1,4 +1,4 @@
-// Bindings generated by `windows-bindgen` 0.55.0
+// Bindings generated by `windows-bindgen` 0.56.0
 
 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
 #[link(name = "advapi32")]
@@ -345,7 +345,7 @@
 }
 #[link(name = "kernel32")]
 extern "system" {
-    pub fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime: *mut FILETIME) -> ();
+    pub fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime: *mut FILETIME);
 }
 #[link(name = "kernel32")]
 extern "system" {
@@ -1018,7 +1018,7 @@
     }
 }
 #[repr(C)]
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 pub struct CONTEXT {
     pub P1Home: u64,
     pub P2Home: u64,
@@ -1067,30 +1067,30 @@
     pub LastExceptionToRip: u64,
     pub LastExceptionFromRip: u64,
 }
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Copy for CONTEXT {}
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Clone for CONTEXT {
     fn clone(&self) -> Self {
         *self
     }
 }
 #[repr(C)]
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 pub union CONTEXT_0 {
     pub FltSave: XSAVE_FORMAT,
     pub Anonymous: CONTEXT_0_0,
 }
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Copy for CONTEXT_0 {}
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Clone for CONTEXT_0 {
     fn clone(&self) -> Self {
         *self
     }
 }
 #[repr(C)]
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 pub struct CONTEXT_0_0 {
     pub Header: [M128A; 2],
     pub Legacy: [M128A; 8],
@@ -1111,9 +1111,9 @@
     pub Xmm14: M128A,
     pub Xmm15: M128A,
 }
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Copy for CONTEXT_0_0 {}
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Clone for CONTEXT_0_0 {
     fn clone(&self) -> Self {
         *self
@@ -3339,7 +3339,7 @@
 pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32;
 pub const FIONBIO: i32 = -2147195266i32;
 #[repr(C)]
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 pub struct FLOATING_SAVE_AREA {
     pub ControlWord: u32,
     pub StatusWord: u32,
@@ -3351,9 +3351,9 @@
     pub RegisterArea: [u8; 80],
     pub Cr0NpxState: u32,
 }
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Copy for FLOATING_SAVE_AREA {}
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Clone for FLOATING_SAVE_AREA {
     fn clone(&self) -> Self {
         *self
@@ -4106,7 +4106,7 @@
     }
 }
 #[repr(C)]
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 pub struct WSADATA {
     pub wVersion: u16,
     pub wHighVersion: u16,
@@ -4116,9 +4116,9 @@
     pub szDescription: [i8; 257],
     pub szSystemStatus: [i8; 129],
 }
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Copy for WSADATA {}
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Clone for WSADATA {
     fn clone(&self) -> Self {
         *self
@@ -4286,7 +4286,7 @@
 pub const WSA_WAIT_EVENT_0: WSA_ERROR = 0i32;
 pub const WSA_WAIT_IO_COMPLETION: WSA_ERROR = 192i32;
 #[repr(C)]
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 pub struct XSAVE_FORMAT {
     pub ControlWord: u16,
     pub StatusWord: u16,
@@ -4305,9 +4305,9 @@
     pub XmmRegisters: [M128A; 16],
     pub Reserved4: [u8; 96],
 }
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Copy for XSAVE_FORMAT {}
-#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
+#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
 impl Clone for XSAVE_FORMAT {
     fn clone(&self) -> Self {
         *self
diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs
index a734c2bd..b495855 100644
--- a/library/std/src/sys/pal/windows/mod.rs
+++ b/library/std/src/sys/pal/windows/mod.rs
@@ -332,7 +332,7 @@
                 core::arch::asm!("int $$0x29", in("ecx") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
             } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] {
                 core::arch::asm!(".inst 0xDEFB", in("r0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
-            } else if #[cfg(target_arch = "aarch64")] {
+            } else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] {
                 core::arch::asm!("brk 0xF003", in("x0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
             } else {
                 core::intrinsics::abort();
diff --git a/library/std/src/sys/thread_local/static_local.rs b/library/std/src/sys/thread_local/static_local.rs
index 206e62b..162c3fb 100644
--- a/library/std/src/sys/thread_local/static_local.rs
+++ b/library/std/src/sys/thread_local/static_local.rs
@@ -11,8 +11,6 @@
     (@key $t:ty, const $init:expr) => {{
         #[inline] // see comments below
         #[deny(unsafe_op_in_unsafe_fn)]
-        // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
-        #[allow(static_mut_refs)]
         unsafe fn __getit(
             _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
         ) -> $crate::option::Option<&'static $t> {
@@ -25,7 +23,8 @@
             // FIXME(#84224) this should come after the `target_thread_local`
             // block.
             static mut VAL: $t = INIT_EXPR;
-            unsafe { $crate::option::Option::Some(&VAL) }
+            // SAFETY: we only ever create shared references, so there's no mutable aliasing.
+            unsafe { $crate::option::Option::Some(&*$crate::ptr::addr_of!(VAL)) }
         }
 
         unsafe {
diff --git a/library/stdarch b/library/stdarch
index 7df81ba..c0257c1 160000
--- a/library/stdarch
+++ b/library/stdarch
@@ -1 +1 @@
-Subproject commit 7df81ba8c3e2d02c2ace0c5a6f4f32d800c09e56
+Subproject commit c0257c1660e78c80ad1b9136fcc5555b14da5b4c
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index f3c2206..f3a26e2 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -17,7 +17,6 @@
 #![unstable(feature = "test", issue = "50297")]
 #![doc(test(attr(deny(warnings))))]
 #![doc(rust_logo)]
-#![feature(generic_nonzero)]
 #![feature(rustdoc_internals)]
 #![feature(internal_output_capture)]
 #![feature(staged_api)]
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index e2c90cc8..9c2b750 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -237,12 +237,6 @@
 ]
 
 [[package]]
-name = "either"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
-
-[[package]]
 name = "errno"
 version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -449,26 +443,6 @@
 ]
 
 [[package]]
-name = "rayon"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
-dependencies = [
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
-dependencies = [
- "crossbeam-deque",
- "crossbeam-utils",
-]
-
-[[package]]
 name = "redox_syscall"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -598,7 +572,6 @@
  "libc",
  "ntapi",
  "once_cell",
- "rayon",
  "windows",
 ]
 
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index b232885..c7a513d 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -64,7 +64,7 @@
 xz2 = "0.1"
 
 # Dependencies needed by the build-metrics feature
-sysinfo = { version = "0.30", optional = true }
+sysinfo = { version = "0.30", default-features = false, optional = true }
 
 [target.'cfg(windows)'.dependencies.junction]
 version = "1.0.0"
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index d6e60d5..3cfd024 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -76,10 +76,11 @@
 		$(BOOTSTRAP) miri --stage 2 library/std \
 		--doc -- \
 		--skip fs:: --skip net:: --skip process:: --skip sys::pal::
-	# Also test some very target-specific modules on other targets.
+	# Also test some very target-specific modules on other targets
+	# (making sure to cover an i686 target as well).
 	$(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \
 		$(BOOTSTRAP) miri --stage 2 library/std \
-		--target aarch64-apple-darwin,i686-pc-windows-gnu \
+		--target aarch64-apple-darwin,i686-pc-windows-msvc \
 		--no-doc -- \
 		time:: sync:: thread:: env::
 dist:
diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs
index 4cb67b7..44fb191 100644
--- a/src/bootstrap/src/bin/main.rs
+++ b/src/bootstrap/src/bin/main.rs
@@ -37,6 +37,7 @@
 
         build_lock = fd_lock::RwLock::new(t!(fs::OpenOptions::new()
             .write(true)
+            .truncate(true)
             .create(true)
             .open(&lock_path)));
         _build_lock_guard = match build_lock.try_write() {
@@ -143,8 +144,8 @@
         // then use the one from the config.toml. This way we never show the same warnings
         // more than once.
         if let Ok(t) = fs::read_to_string(&warned_id_path) {
-            let last_warned_id =
-                usize::from_str(&t).expect(&format!("{} is corrupted.", warned_id_path.display()));
+            let last_warned_id = usize::from_str(&t)
+                .unwrap_or_else(|_| panic!("{} is corrupted.", warned_id_path.display()));
 
             // We only use the last_warned_id if it exists in `CONFIG_CHANGE_HISTORY`.
             // Otherwise, we may retrieve all the changes if it's not the highest value.
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 927d72e..8235d46 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -394,7 +394,7 @@
         impl Step for $name {
             type Output = ();
             const ONLY_HOSTS: bool = true;
-            // don't ever check out-of-tree tools by default, they'll fail when toolstate is broken
+            /// don't ever check out-of-tree tools by default, they'll fail when toolstate is broken
             const DEFAULT: bool = matches!($source_type, SourceType::InTree) $( && $default )?;
 
             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs
index 9e103a3..5bcaeed 100644
--- a/src/bootstrap/src/core/build_steps/clean.rs
+++ b/src/bootstrap/src/core/build_steps/clean.rs
@@ -181,39 +181,33 @@
         }
         Ok(metadata) => {
             if metadata.file_type().is_file() || metadata.file_type().is_symlink() {
-                do_op(path, "remove file", |p| {
-                    fs::remove_file(p).or_else(|e| {
-                        // Work around the fact that we cannot
-                        // delete an executable while it runs on Windows.
-                        #[cfg(windows)]
+                do_op(path, "remove file", |p| match fs::remove_file(p) {
+                    #[cfg(windows)]
+                    Err(e)
                         if e.kind() == std::io::ErrorKind::PermissionDenied
                             && p.file_name().and_then(std::ffi::OsStr::to_str)
-                                == Some("bootstrap.exe")
-                        {
-                            eprintln!("WARNING: failed to delete '{}'.", p.display());
-                            return Ok(());
-                        }
-                        Err(e)
-                    })
+                                == Some("bootstrap.exe") =>
+                    {
+                        eprintln!("WARNING: failed to delete '{}'.", p.display());
+                        Ok(())
+                    }
+                    r => r,
                 });
+
                 return;
             }
 
             for file in t!(fs::read_dir(path)) {
                 rm_rf(&t!(file).path());
             }
-            do_op(path, "remove dir", |p| {
-                fs::remove_dir(p).or_else(|e| {
-                    // Check for dir not empty on Windows
-                    // FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized,
-                    // match on `e.kind()` instead.
-                    #[cfg(windows)]
-                    if e.raw_os_error() == Some(145) {
-                        return Ok(());
-                    }
 
-                    Err(e)
-                })
+            do_op(path, "remove dir", |p| match fs::remove_dir(p) {
+                // Check for dir not empty on Windows
+                // FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized,
+                // match on `e.kind()` instead.
+                #[cfg(windows)]
+                Err(e) if e.raw_os_error() == Some(145) => Ok(()),
+                r => r,
             });
         }
     };
@@ -228,14 +222,14 @@
         // On windows we can't remove a readonly file, and git will often clone files as readonly.
         // As a result, we have some special logic to remove readonly files on windows.
         // This is also the reason that we can't use things like fs::remove_dir_all().
-        Err(ref e) if cfg!(windows) && e.kind() == ErrorKind::PermissionDenied => {
+        #[cfg(windows)]
+        Err(ref e) if e.kind() == ErrorKind::PermissionDenied => {
             let m = t!(path.symlink_metadata());
             let mut p = m.permissions();
             p.set_readonly(false);
             t!(fs::set_permissions(path, p));
             f(path).unwrap_or_else(|e| {
                 // Delete symlinked directories on Windows
-                #[cfg(windows)]
                 if m.file_type().is_symlink() && path.is_dir() && fs::remove_dir(path).is_ok() {
                     return;
                 }
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index 33323ec..01b5e99 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -24,7 +24,7 @@
 use super::tool::prepare_tool_cargo;
 use super::tool::SourceType;
 
-// Disable the most spammy clippy lints
+/// Disable the most spammy clippy lints
 const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[
     "many_single_char_names", // there are a lot in stdarch
     "collapsible_if",
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 50149d3..c20653b 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -835,13 +835,13 @@
 }
 
 impl Step for Rustc {
-    // We return the stage of the "actual" compiler (not the uplifted one).
-    //
-    // By "actual" we refer to the uplifting logic where we may not compile the requested stage;
-    // instead, we uplift it from the previous stages. Which can lead to bootstrap failures in
-    // specific situations where we request stage X from other steps. However we may end up
-    // uplifting it from stage Y, causing the other stage to fail when attempting to link with
-    // stage X which was never actually built.
+    /// We return the stage of the "actual" compiler (not the uplifted one).
+    ///
+    /// By "actual" we refer to the uplifting logic where we may not compile the requested stage;
+    /// instead, we uplift it from the previous stages. Which can lead to bootstrap failures in
+    /// specific situations where we request stage X from other steps. However we may end up
+    /// uplifting it from stage Y, causing the other stage to fail when attempting to link with
+    /// stage X which was never actually built.
     type Output = u32;
     const ONLY_HOSTS: bool = true;
     const DEFAULT: bool = false;
@@ -1139,7 +1139,7 @@
     // busting caches (e.g. like #71152).
     if builder.config.llvm_enabled(target) {
         let building_is_expensive =
-            crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target).is_err();
+            crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target).should_build();
         // `top_stage == stage` might be false for `check --stage 1`, if we are building the stage 1 compiler
         let can_skip_build = builder.kind == Kind::Check && builder.top_stage == stage;
         let should_skip_build = building_is_expensive && can_skip_build;
@@ -1282,11 +1282,7 @@
     if path.path.to_str().unwrap().contains(CODEGEN_BACKEND_PREFIX) {
         let mut needs_codegen_backend_config = true;
         for backend in run.builder.config.codegen_backends(run.target) {
-            if path
-                .path
-                .to_str()
-                .unwrap()
-                .ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + &backend))
+            if path.path.to_str().unwrap().ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend))
             {
                 needs_codegen_backend_config = false;
             }
@@ -1306,7 +1302,7 @@
 impl Step for CodegenBackend {
     type Output = ();
     const ONLY_HOSTS: bool = true;
-    // Only the backends specified in the `codegen-backends` entry of `config.toml` are built.
+    /// Only the backends specified in the `codegen-backends` entry of `config.toml` are built.
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -1846,7 +1842,7 @@
                 extra_features: vec![],
             });
             let tool_exe = exe("llvm-bitcode-linker", target_compiler.host);
-            builder.copy_link(&src_path, &libdir_bin.join(&tool_exe));
+            builder.copy_link(&src_path, &libdir_bin.join(tool_exe));
         }
 
         // Ensure that `libLLVM.so` ends up in the newly build compiler directory,
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 7817666..0eca209 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -107,7 +107,7 @@
             builder.top_stage,
             host,
             builder,
-            DocumentationFormat::JSON,
+            DocumentationFormat::Json,
         ));
 
         let dest = "share/doc/rust/json";
@@ -1131,7 +1131,7 @@
         let rls = builder.ensure(tool::Rls { compiler, target, extra_features: Vec::new() });
 
         let mut tarball = Tarball::new(builder, "rls", &target.triple);
-        tarball.set_overlay(OverlayKind::RLS);
+        tarball.set_overlay(OverlayKind::Rls);
         tarball.is_preview(true);
         tarball.add_file(rls, "bin", 0o755);
         tarball.add_legal_and_readme_to("share/doc/rls");
@@ -2059,7 +2059,7 @@
         if install_symlink {
             // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a
             // symlink is fine here, as this is not a rustup component.
-            builder.copy_link(&source, &full_dest);
+            builder.copy_link(source, &full_dest);
         } else {
             // Otherwise, replace the symlink with an equivalent linker script. This is used when
             // projects like miri link against librustc_driver.so. We don't use a symlink, as
@@ -2076,7 +2076,7 @@
             }
         }
     } else {
-        builder.install(&source, destination, 0o644);
+        builder.install(source, destination, 0o644);
     }
 }
 
@@ -2121,7 +2121,7 @@
             builder.install(&llvm_dylib_path, dst_libdir, 0o644);
         }
         !builder.config.dry_run()
-    } else if let Ok(llvm::LlvmResult { llvm_config, .. }) =
+    } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
         llvm::prebuilt_llvm_config(builder, target)
     {
         let mut cmd = Command::new(llvm_config);
@@ -2202,7 +2202,7 @@
         builder.ensure(crate::core::build_steps::llvm::Llvm { target });
 
         let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple);
-        tarball.set_overlay(OverlayKind::LLVM);
+        tarball.set_overlay(OverlayKind::Llvm);
         tarball.is_preview(true);
 
         if builder.config.llvm_tools_enabled {
@@ -2272,9 +2272,9 @@
     }
 }
 
-// Tarball intended for internal consumption to ease rustc/std development.
-//
-// Should not be considered stable by end users.
+/// Tarball intended for internal consumption to ease rustc/std development.
+///
+/// Should not be considered stable by end users.
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
 pub struct RustDev {
     pub target: TargetSelection,
@@ -2305,7 +2305,7 @@
         }
 
         let mut tarball = Tarball::new(builder, "rust-dev", &target.triple);
-        tarball.set_overlay(OverlayKind::LLVM);
+        tarball.set_overlay(OverlayKind::Llvm);
         // LLVM requires a shared object symlink to exist on some platforms.
         tarball.permit_symlinks(true);
 
@@ -2356,9 +2356,9 @@
     }
 }
 
-// Tarball intended for internal consumption to ease rustc/std development.
-//
-// Should not be considered stable by end users.
+/// Tarball intended for internal consumption to ease rustc/std development.
+///
+/// Should not be considered stable by end users.
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
 pub struct Bootstrap {
     pub target: TargetSelection,
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index a22cbea..38c48bd 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -506,7 +506,7 @@
         run.never()
     }
 
-    // Generate shared resources used by other pieces of documentation.
+    /// Generate shared resources used by other pieces of documentation.
     fn run(self, builder: &Builder<'_>) -> Self::Output {
         let out = builder.doc_out(self.target);
 
@@ -567,9 +567,9 @@
             stage: run.builder.top_stage,
             target: run.target,
             format: if run.builder.config.cmd.json() {
-                DocumentationFormat::JSON
+                DocumentationFormat::Json
             } else {
-                DocumentationFormat::HTML
+                DocumentationFormat::Html
             },
             crates: run.make_run_crates(Alias::Library),
         });
@@ -583,13 +583,13 @@
         let stage = self.stage;
         let target = self.target;
         let out = match self.format {
-            DocumentationFormat::HTML => builder.doc_out(target),
-            DocumentationFormat::JSON => builder.json_doc_out(target),
+            DocumentationFormat::Html => builder.doc_out(target),
+            DocumentationFormat::Json => builder.json_doc_out(target),
         };
 
         t!(fs::create_dir_all(&out));
 
-        if self.format == DocumentationFormat::HTML {
+        if self.format == DocumentationFormat::Html {
             builder.ensure(SharedAssets { target: self.target });
         }
 
@@ -600,10 +600,10 @@
             .into_string()
             .expect("non-utf8 paths are unsupported");
         let mut extra_args = match self.format {
-            DocumentationFormat::HTML => {
+            DocumentationFormat::Html => {
                 vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page]
             }
-            DocumentationFormat::JSON => vec!["--output-format", "json"],
+            DocumentationFormat::Json => vec!["--output-format", "json"],
         };
 
         if !builder.config.docs_minification {
@@ -613,7 +613,7 @@
         doc_std(builder, self.format, stage, target, &out, &extra_args, &self.crates);
 
         // Don't open if the format is json
-        if let DocumentationFormat::JSON = self.format {
+        if let DocumentationFormat::Json = self.format {
             return;
         }
 
@@ -646,15 +646,15 @@
 
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
 pub enum DocumentationFormat {
-    HTML,
-    JSON,
+    Html,
+    Json,
 }
 
 impl DocumentationFormat {
     fn as_str(&self) -> &str {
         match self {
-            DocumentationFormat::HTML => "HTML",
-            DocumentationFormat::JSON => "JSON",
+            DocumentationFormat::Html => "HTML",
+            DocumentationFormat::Json => "JSON",
         }
     }
 }
@@ -678,7 +678,7 @@
 
     let compiler = builder.compiler(stage, builder.config.build);
 
-    let target_doc_dir_name = if format == DocumentationFormat::JSON { "json-doc" } else { "doc" };
+    let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" };
     let target_dir =
         builder.stage_out(compiler, Mode::Std).join(target.triple).join(target_doc_dir_name);
 
diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs
index fc9f978..9fc65a0 100644
--- a/src/bootstrap/src/core/build_steps/format.rs
+++ b/src/bootstrap/src/core/build_steps/format.rs
@@ -225,12 +225,12 @@
         Some(first) => {
             let find_shortcut_candidates = |p: &PathBuf| {
                 let mut candidates = Vec::new();
-                for candidate in WalkBuilder::new(src.clone()).max_depth(Some(3)).build() {
-                    if let Ok(entry) = candidate {
-                        if let Some(dir_name) = p.file_name() {
-                            if entry.path().is_dir() && entry.file_name() == dir_name {
-                                candidates.push(entry.into_path());
-                            }
+                for entry in
+                    WalkBuilder::new(src.clone()).max_depth(Some(3)).build().map_while(Result::ok)
+                {
+                    if let Some(dir_name) = p.file_name() {
+                        if entry.path().is_dir() && entry.file_name() == dir_name {
+                            candidates.push(entry.into_path());
                         }
                     }
                 }
diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs
index 767c0f6..6a75f35 100644
--- a/src/bootstrap/src/core/build_steps/install.rs
+++ b/src/bootstrap/src/core/build_steps/install.rs
@@ -20,8 +20,8 @@
 #[cfg(not(target_os = "illumos"))]
 const SHELL: &str = "sh";
 
-// We have to run a few shell scripts, which choke quite a bit on both `\`
-// characters and on `C:\` paths, so normalize both of them away.
+/// We have to run a few shell scripts, which choke quite a bit on both `\`
+/// characters and on `C:\` paths, so normalize both of them away.
 fn sanitize_sh(path: &Path) -> String {
     let path = path.to_str().unwrap().replace('\\', "/");
     return change_drive(unc_to_lfs(&path)).unwrap_or(path);
@@ -130,7 +130,7 @@
     // https://www.gnu.org/prep/standards/html_node/DESTDIR.html
     if let Some(destdir) = destdir_env {
         let without_destdir = path.clone();
-        path = destdir.clone();
+        path.clone_from(destdir);
         // Custom .join() which ignores disk roots.
         for part in without_destdir.components() {
             if let Component::Normal(s) = part {
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 7cb15fe..d4473e2 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -42,14 +42,28 @@
     root: String,
 }
 
-// Linker flags to pass to LLVM's CMake invocation.
+pub enum LlvmBuildStatus {
+    AlreadyBuilt(LlvmResult),
+    ShouldBuild(Meta),
+}
+
+impl LlvmBuildStatus {
+    pub fn should_build(&self) -> bool {
+        match self {
+            LlvmBuildStatus::AlreadyBuilt(_) => false,
+            LlvmBuildStatus::ShouldBuild(_) => true,
+        }
+    }
+}
+
+/// Linker flags to pass to LLVM's CMake invocation.
 #[derive(Debug, Clone, Default)]
 struct LdFlags {
-    // CMAKE_EXE_LINKER_FLAGS
+    /// CMAKE_EXE_LINKER_FLAGS
     exe: OsString,
-    // CMAKE_SHARED_LINKER_FLAGS
+    /// CMAKE_SHARED_LINKER_FLAGS
     shared: OsString,
-    // CMAKE_MODULE_LINKER_FLAGS
+    /// CMAKE_MODULE_LINKER_FLAGS
     module: OsString,
 }
 
@@ -72,10 +86,7 @@
 ///
 /// This will return the llvm-config if it can get it (but it will not build it
 /// if not).
-pub fn prebuilt_llvm_config(
-    builder: &Builder<'_>,
-    target: TargetSelection,
-) -> Result<LlvmResult, Meta> {
+pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> LlvmBuildStatus {
     // If we have llvm submodule initialized already, sync it.
     builder.update_existing_submodule(&Path::new("src").join("llvm-project"));
 
@@ -93,7 +104,7 @@
             llvm_cmake_dir.push("lib");
             llvm_cmake_dir.push("cmake");
             llvm_cmake_dir.push("llvm");
-            return Ok(LlvmResult { llvm_config, llvm_cmake_dir });
+            return LlvmBuildStatus::AlreadyBuilt(LlvmResult { llvm_config, llvm_cmake_dir });
         }
     }
 
@@ -131,10 +142,10 @@
                 stamp.path.display()
             ));
         }
-        return Ok(res);
+        return LlvmBuildStatus::AlreadyBuilt(res);
     }
 
-    Err(Meta { stamp, res, out_dir, root: root.into() })
+    LlvmBuildStatus::ShouldBuild(Meta { stamp, res, out_dir, root: root.into() })
 }
 
 /// This retrieves the LLVM sha we *want* to use, according to git history.
@@ -282,8 +293,8 @@
 
         // If LLVM has already been built or been downloaded through download-ci-llvm, we avoid building it again.
         let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) {
-            Ok(p) => return p,
-            Err(m) => m,
+            LlvmBuildStatus::AlreadyBuilt(p) => return p,
+            LlvmBuildStatus::ShouldBuild(m) => m,
         };
 
         if builder.llvm_link_shared() && target.is_windows() {
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 7bc68b5..c0683cd 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -93,10 +93,9 @@
                 Ok(Profile::Tools)
             }
             "none" => Ok(Profile::None),
-            "llvm" | "codegen" => Err(format!(
-                "the \"llvm\" and \"codegen\" profiles have been removed,\
+            "llvm" | "codegen" => Err("the \"llvm\" and \"codegen\" profiles have been removed,\
                 use \"compiler\" instead which has the same functionality"
-            )),
+                .to_string()),
             _ => Err(format!("unknown profile: '{s}'")),
         }
     }
@@ -474,10 +473,10 @@
 
 // install a git hook to automatically run tidy, if they want
 fn install_git_hook_maybe(config: &Config) -> io::Result<()> {
-    let git = t!(config.git().args(["rev-parse", "--git-common-dir"]).output().map(|output| {
+    let git = config.git().args(["rev-parse", "--git-common-dir"]).output().map(|output| {
         assert!(output.status.success(), "failed to run `git`");
         PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
-    }));
+    })?;
     let hooks_dir = git.join("hooks");
     let dst = hooks_dir.join("pre-push");
     if dst.exists() {
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 1980980..ca10a12 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -923,7 +923,7 @@
             builder.top_stage,
             self.target,
             builder,
-            DocumentationFormat::HTML,
+            DocumentationFormat::Html,
         ));
         let _guard = builder.msg(
             Kind::Test,
@@ -1389,10 +1389,9 @@
         builder.run(&mut cargo);
 
         let lib_name = "librun_make_support.rlib";
-        let lib = builder.tools_dir(self.compiler).join(&lib_name);
+        let lib = builder.tools_dir(self.compiler).join(lib_name);
 
-        let cargo_out =
-            builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(&lib_name);
+        let cargo_out = builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(lib_name);
         builder.copy_link(&cargo_out, &lib);
         lib
     }
@@ -1434,8 +1433,8 @@
 
 host_test!(Pretty { path: "tests/pretty", mode: "pretty", suite: "pretty" });
 
-// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake`
-// tests.
+/// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake`
+/// tests.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct RunMake {
     pub compiler: Compiler,
@@ -1528,10 +1527,10 @@
 
 impl Step for Coverage {
     type Output = ();
-    // We rely on the individual CoverageMap/CoverageRun steps to run themselves.
+    /// We rely on the individual CoverageMap/CoverageRun steps to run themselves.
     const DEFAULT: bool = false;
-    // When manually invoked, try to run as much as possible.
-    // Compiletest will automatically skip the "coverage-run" tests if necessary.
+    /// When manually invoked, try to run as much as possible.
+    /// Compiletest will automatically skip the "coverage-run" tests if necessary.
     const ONLY_HOSTS: bool = false;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -2483,6 +2482,7 @@
 /// Given a `cargo test` subcommand, add the appropriate flags and run it.
 ///
 /// Returns whether the test succeeded.
+#[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this.
 fn run_cargo_test<'a>(
     cargo: impl Into<Command>,
     libtest_args: &[&str],
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 45b1d5a..994f0be 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -135,6 +135,7 @@
     }
 }
 
+#[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this.
 pub fn prepare_tool_cargo(
     builder: &Builder<'_>,
     compiler: Compiler,
@@ -545,7 +546,7 @@
     }
 
     fn run(self, builder: &Builder<'_>) -> PathBuf {
-        let cargo_bin_path = builder.ensure(ToolBuild {
+        builder.ensure(ToolBuild {
             compiler: self.compiler,
             target: self.target,
             tool: "cargo",
@@ -554,8 +555,7 @@
             source_type: SourceType::Submodule,
             extra_features: Vec::new(),
             allow_features: "",
-        });
-        cargo_bin_path
+        })
     }
 }
 
@@ -573,7 +573,7 @@
     }
 
     fn run(self, builder: &Builder<'_>) -> PathBuf {
-        let src_exe = builder.ensure(ToolBuild {
+        builder.ensure(ToolBuild {
             compiler: self.compiler,
             target: self.target,
             tool: "lld-wrapper",
@@ -582,9 +582,7 @@
             source_type: SourceType::InTree,
             extra_features: Vec::new(),
             allow_features: "",
-        });
-
-        src_exe
+        })
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs
index deb782c..f88c1b3 100644
--- a/src/bootstrap/src/core/build_steps/toolstate.rs
+++ b/src/bootstrap/src/core/build_steps/toolstate.rs
@@ -245,8 +245,12 @@
                 // Ensure the parent directory always exists
                 t!(std::fs::create_dir_all(parent));
             }
-            let mut file =
-                t!(fs::OpenOptions::new().create(true).write(true).read(true).open(path));
+            let mut file = t!(fs::OpenOptions::new()
+                .create(true)
+                .truncate(false)
+                .write(true)
+                .read(true)
+                .open(path));
 
             serde_json::from_reader(&mut file).unwrap_or_default()
         } else {
@@ -278,8 +282,12 @@
                 // Ensure the parent directory always exists
                 t!(std::fs::create_dir_all(parent));
             }
-            let mut file =
-                t!(fs::OpenOptions::new().create(true).read(true).write(true).open(path));
+            let mut file = t!(fs::OpenOptions::new()
+                .create(true)
+                .truncate(false)
+                .read(true)
+                .write(true)
+                .open(path));
 
             let mut current_toolstates: HashMap<Box<str>, ToolState> =
                 serde_json::from_reader(&mut file).unwrap_or_default();
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 499a74b..adef9eb 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -132,8 +132,7 @@
             Alias::Compiler => self.builder.in_tree_crates("rustc-main", Some(self.target)),
         };
 
-        let crate_names = crates.into_iter().map(|krate| krate.name.to_string()).collect();
-        crate_names
+        crates.into_iter().map(|krate| krate.name.to_string()).collect()
     }
 }
 
@@ -323,16 +322,13 @@
 fn remap_paths(paths: &mut Vec<&Path>) {
     let mut remove = vec![];
     let mut add = vec![];
-    for (i, path) in paths
-        .iter()
-        .enumerate()
-        .filter_map(|(i, path)| if let Some(s) = path.to_str() { Some((i, s)) } else { None })
+    for (i, path) in paths.iter().enumerate().filter_map(|(i, path)| path.to_str().map(|s| (i, s)))
     {
         for &(search, replace) in PATH_REMAP {
             // Remove leading and trailing slashes so `tests/` and `tests` are equivalent
             if path.trim_matches(std::path::is_separator) == search {
                 remove.push(i);
-                add.extend(replace.into_iter().map(Path::new));
+                add.extend(replace.iter().map(Path::new));
                 break;
             }
         }
@@ -1318,7 +1314,7 @@
         } else if let Some(subcmd) = cmd.strip_prefix("miri") {
             // Command must be "miri-X".
             let subcmd = subcmd
-                .strip_prefix("-")
+                .strip_prefix('-')
                 .unwrap_or_else(|| panic!("expected `miri-$subcommand`, but got {}", cmd));
             cargo = self.cargo_miri_cmd(compiler);
             cargo.arg("miri").arg(subcmd);
@@ -1438,7 +1434,7 @@
             // rustc_llvm. But if LLVM is stale, that'll be a tiny amount
             // of work comparatively, and we'd likely need to rebuild it anyway,
             // so that's okay.
-            if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target).is_err() {
+            if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target).should_build() {
                 cargo.env("RUST_CHECK", "1");
             }
         }
@@ -1540,7 +1536,7 @@
         // `rustflags` without `cargo` making it required.
         rustflags.arg("-Zunstable-options");
         for (restricted_mode, name, values) in EXTRA_CHECK_CFGS {
-            if *restricted_mode == None || *restricted_mode == Some(mode) {
+            if restricted_mode.is_none() || *restricted_mode == Some(mode) {
                 rustflags.arg(&check_cfg_arg(name, *values));
             }
         }
@@ -2102,6 +2098,14 @@
             // break when incremental compilation is enabled. So this overrides the "no inlining
             // during incremental builds" heuristic for the standard library.
             rustflags.arg("-Zinline-mir");
+
+            // FIXME: always pass this after the next `#[cfg(bootstrap)]` update.
+            if compiler.stage != 0 {
+                // Similarly, we need to keep debug info for functions inlined into other std functions,
+                // even if we're not going to output debuginfo for the crate we're currently building,
+                // so that it'll be available when downstream consumers of std try to use it.
+                rustflags.arg("-Zinline-mir-preserve-debug");
+            }
         }
 
         if self.config.rustc_parallel
@@ -2208,22 +2212,18 @@
             let file = File::open(src.join(".gitmodules")).unwrap();
 
             let mut submodules_paths = vec![];
-            for line in BufReader::new(file).lines() {
-                if let Ok(line) = line {
-                    let line = line.trim();
-
-                    if line.starts_with("path") {
-                        let actual_path =
-                            line.split(' ').last().expect("Couldn't get value of path");
-                        submodules_paths.push(actual_path.to_owned());
-                    }
+            for line in BufReader::new(file).lines().map_while(Result::ok) {
+                let line = line.trim();
+                if line.starts_with("path") {
+                    let actual_path = line.split(' ').last().expect("Couldn't get value of path");
+                    submodules_paths.push(actual_path.to_owned());
                 }
             }
 
             submodules_paths
         };
 
-        &SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src))
+        SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src))
     }
 
     /// Ensure that a given step is built *only if it's supposed to be built by default*, returning
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 178df63..caec463 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -75,7 +75,7 @@
             $stage,
             TargetSelection::from_user(stringify!($target)),
             &builder,
-            DocumentationFormat::HTML,
+            DocumentationFormat::Html,
         )
     }};
 }
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index a272d8b..2acce62 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -55,6 +55,7 @@
 pub enum DebuginfoLevel {
     #[default]
     None,
+    LineDirectivesOnly,
     LineTablesOnly,
     Limited,
     Full,
@@ -70,16 +71,22 @@
         use serde::de::Error;
 
         Ok(match Deserialize::deserialize(deserializer)? {
-            StringOrInt::String("none") | StringOrInt::Int(0) => DebuginfoLevel::None,
-            StringOrInt::String("line-tables-only") => DebuginfoLevel::LineTablesOnly,
-            StringOrInt::String("limited") | StringOrInt::Int(1) => DebuginfoLevel::Limited,
-            StringOrInt::String("full") | StringOrInt::Int(2) => DebuginfoLevel::Full,
+            StringOrInt::String(s) if s == "none" => DebuginfoLevel::None,
+            StringOrInt::Int(0) => DebuginfoLevel::None,
+            StringOrInt::String(s) if s == "line-directives-only" => {
+                DebuginfoLevel::LineDirectivesOnly
+            }
+            StringOrInt::String(s) if s == "line-tables-only" => DebuginfoLevel::LineTablesOnly,
+            StringOrInt::String(s) if s == "limited" => DebuginfoLevel::Limited,
+            StringOrInt::Int(1) => DebuginfoLevel::Limited,
+            StringOrInt::String(s) if s == "full" => DebuginfoLevel::Full,
+            StringOrInt::Int(2) => DebuginfoLevel::Full,
             StringOrInt::Int(n) => {
                 let other = serde::de::Unexpected::Signed(n);
                 return Err(D::Error::invalid_value(other, &"expected 0, 1, or 2"));
             }
             StringOrInt::String(s) => {
-                let other = serde::de::Unexpected::Str(s);
+                let other = serde::de::Unexpected::Str(&s);
                 return Err(D::Error::invalid_value(
                     other,
                     &"expected none, line-tables-only, limited, or full",
@@ -95,6 +102,7 @@
         use DebuginfoLevel::*;
         f.write_str(match self {
             None => "0",
+            LineDirectivesOnly => "line-directives-only",
             LineTablesOnly => "line-tables-only",
             Limited => "1",
             Full => "2",
@@ -1021,8 +1029,8 @@
 
 #[derive(Deserialize)]
 #[serde(untagged)]
-enum StringOrInt<'a> {
-    String(&'a str),
+enum StringOrInt {
+    String(String),
     Int(i64),
 }
 
@@ -1209,10 +1217,7 @@
                 .and_then(|table: toml::Value| TomlConfig::deserialize(table))
                 .unwrap_or_else(|err| {
                     if let Ok(Some(changes)) = toml::from_str(&contents)
-                        .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table))
-                        .and_then(|change_id| {
-                            Ok(change_id.inner.map(|id| crate::find_recent_config_change_ids(id)))
-                        })
+                        .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table)).map(|change_id| change_id.inner.map(crate::find_recent_config_change_ids))
                     {
                         if !changes.is_empty() {
                             println!(
@@ -1791,17 +1796,17 @@
             if let Some(v) = link_shared {
                 config.llvm_link_shared.set(Some(v));
             }
-            config.llvm_targets = targets.clone();
-            config.llvm_experimental_targets = experimental_targets.clone();
+            config.llvm_targets.clone_from(&targets);
+            config.llvm_experimental_targets.clone_from(&experimental_targets);
             config.llvm_link_jobs = link_jobs;
-            config.llvm_version_suffix = version_suffix.clone();
-            config.llvm_clang_cl = clang_cl.clone();
+            config.llvm_version_suffix.clone_from(&version_suffix);
+            config.llvm_clang_cl.clone_from(&clang_cl);
 
-            config.llvm_cflags = cflags.clone();
-            config.llvm_cxxflags = cxxflags.clone();
-            config.llvm_ldflags = ldflags.clone();
+            config.llvm_cflags.clone_from(&cflags);
+            config.llvm_cxxflags.clone_from(&cxxflags);
+            config.llvm_ldflags.clone_from(&ldflags);
             set(&mut config.llvm_use_libcxx, use_libcxx);
-            config.llvm_use_linker = use_linker.clone();
+            config.llvm_use_linker.clone_from(&use_linker);
             config.llvm_allow_old_toolchain = allow_old_toolchain.unwrap_or(false);
             config.llvm_polly = polly.unwrap_or(false);
             config.llvm_clang = clang.unwrap_or(false);
diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs
index 9941284..23556e8 100644
--- a/src/bootstrap/src/core/config/mod.rs
+++ b/src/bootstrap/src/core/config/mod.rs
@@ -1,4 +1,5 @@
-pub(crate) mod config;
+#[allow(clippy::module_inception)]
+mod config;
 pub(crate) mod flags;
 #[cfg(test)]
 mod tests;
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 0b6d987..663a2d8 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -76,6 +76,7 @@
 
 /// Extra --check-cfg to add when building
 /// (Mode restriction, config name, config values (if any))
+#[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above.
 const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
     (None, "bootstrap", None),
     (Some(Mode::Rustc), "parallel_compiler", None),
@@ -1625,10 +1626,7 @@
     /// Returns `true` if unstable features should be enabled for the compiler
     /// we're building.
     fn unstable_features(&self) -> bool {
-        match &self.config.channel[..] {
-            "stable" | "beta" => false,
-            "nightly" | _ => true,
-        }
+        !matches!(&self.config.channel[..], "stable" | "beta")
     }
 
     /// Returns a Vec of all the dependencies of the given root crate,
diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs
index 7e59b7f..540b867 100644
--- a/src/bootstrap/src/utils/cc_detect.rs
+++ b/src/bootstrap/src/utils/cc_detect.rs
@@ -41,9 +41,7 @@
         Some(PathBuf::from(ar))
     } else if target.is_msvc() {
         None
-    } else if target.contains("musl") {
-        Some(PathBuf::from("ar"))
-    } else if target.contains("openbsd") {
+    } else if target.contains("musl") || target.contains("openbsd") {
         Some(PathBuf::from("ar"))
     } else if target.contains("vxworks") {
         Some(PathBuf::from("wr-ar"))
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index a40ee18..928c9aa 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -549,7 +549,12 @@
 where
     T: AsRef<[u8]>,
 {
-    input.as_ref().iter().map(|x| format!("{:02x}", x)).collect()
+    use std::fmt::Write;
+
+    input.as_ref().iter().fold(String::with_capacity(input.as_ref().len() * 2), |mut acc, &byte| {
+        write!(&mut acc, "{:02x}", byte).expect("Failed to write byte to the hex String.");
+        acc
+    })
 }
 
 /// Create a `--check-cfg` argument invocation for a given name
diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs
index c5c718a..d7d20e5 100644
--- a/src/bootstrap/src/utils/job.rs
+++ b/src/bootstrap/src/utils/job.rs
@@ -11,37 +11,35 @@
     }
 }
 
+/// Job management on Windows for bootstrapping
+///
+/// Most of the time when you're running a build system (e.g., make) you expect
+/// Ctrl-C or abnormal termination to actually terminate the entire tree of
+/// process in play, not just the one at the top. This currently works "by
+/// default" on Unix platforms because Ctrl-C actually sends a signal to the
+/// *process group* rather than the parent process, so everything will get torn
+/// down. On Windows, however, this does not happen and Ctrl-C just kills the
+/// parent process.
+///
+/// To achieve the same semantics on Windows we use Job Objects to ensure that
+/// all processes die at the same time. Job objects have a mode of operation
+/// where when all handles to the object are closed it causes all child
+/// processes associated with the object to be terminated immediately.
+/// Conveniently whenever a process in the job object spawns a new process the
+/// child will be associated with the job object as well. This means if we add
+/// ourselves to the job object we create then everything will get torn down!
+///
+/// Unfortunately most of the time the build system is actually called from a
+/// python wrapper (which manages things like building the build system) so this
+/// all doesn't quite cut it so far. To go the last mile we duplicate the job
+/// object handle into our parent process (a python process probably) and then
+/// close our own handle. This means that the only handle to the job object
+/// resides in the parent python process, so when python dies the whole build
+/// system dies (as one would probably expect!).
+///
+/// Note that this is a Windows specific module as none of this logic is required on Unix.
 #[cfg(windows)]
 mod for_windows {
-    //! Job management on Windows for bootstrapping
-    //!
-    //! Most of the time when you're running a build system (e.g., make) you expect
-    //! Ctrl-C or abnormal termination to actually terminate the entire tree of
-    //! process in play, not just the one at the top. This currently works "by
-    //! default" on Unix platforms because Ctrl-C actually sends a signal to the
-    //! *process group* rather than the parent process, so everything will get torn
-    //! down. On Windows, however, this does not happen and Ctrl-C just kills the
-    //! parent process.
-    //!
-    //! To achieve the same semantics on Windows we use Job Objects to ensure that
-    //! all processes die at the same time. Job objects have a mode of operation
-    //! where when all handles to the object are closed it causes all child
-    //! processes associated with the object to be terminated immediately.
-    //! Conveniently whenever a process in the job object spawns a new process the
-    //! child will be associated with the job object as well. This means if we add
-    //! ourselves to the job object we create then everything will get torn down!
-    //!
-    //! Unfortunately most of the time the build system is actually called from a
-    //! python wrapper (which manages things like building the build system) so this
-    //! all doesn't quite cut it so far. To go the last mile we duplicate the job
-    //! object handle into our parent process (a python process probably) and then
-    //! close our own handle. This means that the only handle to the job object
-    //! resides in the parent python process, so when python dies the whole build
-    //! system dies (as one would probably expect!).
-    //!
-    //! Note that this module has a #[cfg(windows)] above it as none of this logic
-    //! is required on Unix.
-
     use crate::Build;
     use std::env;
     use std::ffi::c_void;
diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs
index 16e0c2a..462b76d 100644
--- a/src/bootstrap/src/utils/render_tests.rs
+++ b/src/bootstrap/src/utils/render_tests.rs
@@ -239,7 +239,7 @@
             suite.filtered_out,
             time = match suite.exec_time {
                 Some(t) => format!("; finished in {:.2?}", Duration::from_secs_f64(t)),
-                None => format!(""),
+                None => String::new(),
             }
         );
     }
diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs
index cc86e3b..2a950e6 100644
--- a/src/bootstrap/src/utils/tarball.rs
+++ b/src/bootstrap/src/utils/tarball.rs
@@ -18,13 +18,13 @@
 #[derive(Copy, Clone)]
 pub(crate) enum OverlayKind {
     Rust,
-    LLVM,
+    Llvm,
     Cargo,
     Clippy,
     Miri,
     Rustfmt,
     RustDemangler,
-    RLS,
+    Rls,
     RustAnalyzer,
     RustcCodegenCranelift,
     LlvmBitcodeLinker,
@@ -34,7 +34,7 @@
     fn legal_and_readme(&self) -> &[&str] {
         match self {
             OverlayKind::Rust => &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"],
-            OverlayKind::LLVM => {
+            OverlayKind::Llvm => {
                 &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"]
             }
             OverlayKind::Cargo => &[
@@ -61,7 +61,7 @@
             OverlayKind::RustDemangler => {
                 &["src/tools/rust-demangler/README.md", "LICENSE-APACHE", "LICENSE-MIT"]
             }
-            OverlayKind::RLS => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"],
+            OverlayKind::Rls => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"],
             OverlayKind::RustAnalyzer => &[
                 "src/tools/rust-analyzer/README.md",
                 "src/tools/rust-analyzer/LICENSE-APACHE",
@@ -84,7 +84,7 @@
     fn version(&self, builder: &Builder<'_>) -> String {
         match self {
             OverlayKind::Rust => builder.rust_version(),
-            OverlayKind::LLVM => builder.rust_version(),
+            OverlayKind::Llvm => builder.rust_version(),
             OverlayKind::RustDemangler => builder.release_num("rust-demangler"),
             OverlayKind::Cargo => {
                 builder.cargo_info.version(builder, &builder.release_num("cargo"))
@@ -96,7 +96,7 @@
             OverlayKind::Rustfmt => {
                 builder.rustfmt_info.version(builder, &builder.release_num("rustfmt"))
             }
-            OverlayKind::RLS => builder.release(&builder.release_num("rls")),
+            OverlayKind::Rls => builder.release(&builder.release_num("rls")),
             OverlayKind::RustAnalyzer => builder
                 .rust_analyzer_info
                 .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")),
@@ -357,7 +357,7 @@
             &self.builder.config.dist_compression_profile
         };
 
-        cmd.args(&["--compression-profile", compression_profile]);
+        cmd.args(["--compression-profile", compression_profile]);
         self.builder.run(&mut cmd);
 
         // Ensure there are no symbolic links in the tarball. In particular,
diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
index 5c459e5..00552db 100644
--- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
@@ -55,6 +55,9 @@
 COPY host-x86_64/dist-various-1/install-riscv32-none-elf.sh /build
 RUN ./install-riscv32-none-elf.sh
 
+COPY host-x86_64/dist-various-1/install-llvm-mingw.sh /build
+RUN ./install-llvm-mingw.sh
+
 # Suppress some warnings in the openwrt toolchains we downloaded
 ENV STAGING_DIR=/tmp
 
@@ -111,6 +114,9 @@
 ENV TARGETS=$TARGETS,armv7r-none-eabihf
 ENV TARGETS=$TARGETS,thumbv7neon-unknown-linux-gnueabihf
 ENV TARGETS=$TARGETS,armv7a-none-eabi
+ENV TARGETS=$TARGETS,aarch64-pc-windows-gnullvm
+ENV TARGETS=$TARGETS,i686-pc-windows-gnullvm
+ENV TARGETS=$TARGETS,x86_64-pc-windows-gnullvm
 
 ENV CFLAGS_armv5te_unknown_linux_musleabi="-march=armv5te -marm -mfloat-abi=soft" \
     CFLAGS_arm_unknown_linux_musleabi="-march=armv6 -marm" \
@@ -142,7 +148,10 @@
     CC_riscv64imac_unknown_none_elf=riscv64-unknown-elf-gcc \
     CFLAGS_riscv64imac_unknown_none_elf=-march=rv64imac -mabi=lp64 \
     CC_riscv64gc_unknown_none_elf=riscv64-unknown-elf-gcc \
-    CFLAGS_riscv64gc_unknown_none_elf=-march=rv64gc -mabi=lp64
+    CFLAGS_riscv64gc_unknown_none_elf=-march=rv64gc -mabi=lp64 \
+    CC_aarch64_pc_windows_gnullvm=aarch64-w64-mingw32-clang \
+    CC_i686_pc_windows_gnullvm=i686-w64-mingw32-clang \
+    CC_x86_64_pc_windows_gnullvm=x86_64-w64-mingw32-clang
 
 ENV RUST_CONFIGURE_ARGS \
       --musl-root-armv5te=/musl-armv5te \
diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh b/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh
new file mode 100755
index 0000000..9547189
--- /dev/null
+++ b/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -ex
+
+release_date=20240404
+archive=llvm-mingw-${release_date}-ucrt-ubuntu-20.04-x86_64.tar.xz
+curl -L https://github.com/mstorsjo/llvm-mingw/releases/download/${release_date}/${archive} | \
+tar --extract --lzma --strip 1 --directory /usr/local
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index ae8dfad..0eff7ca 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -21,10 +21,12 @@
   mingw-w64 \
   && rm -rf /var/lib/apt/lists/*
 
-RUN curl -sL https://nodejs.org/dist/v16.9.0/node-v16.9.0-linux-x64.tar.xz | tar -xJ
-ENV PATH="/node-v16.9.0-linux-x64/bin:${PATH}"
 ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3"
 
+COPY scripts/nodejs.sh /scripts/
+RUN sh /scripts/nodejs.sh /node
+ENV PATH="/node/bin:${PATH}"
+
 # Install es-check
 # Pin its version to prevent unrelated CI failures due to future es-check versions.
 RUN npm install es-check@6.1.1 eslint@8.6.0 -g
@@ -46,6 +48,7 @@
            # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs.
            python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
+           python3 ../x.py clippy bootstrap -Dwarnings && \
            python3 ../x.py clippy compiler library -Aclippy::all -Dclippy::correctness && \
            python3 ../x.py build --stage 0 src/tools/build-manifest && \
            python3 ../x.py test --stage 0 src/tools/compiletest && \
diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile
index 8875e22..f874b2e 100644
--- a/src/ci/docker/host-x86_64/test-various/Dockerfile
+++ b/src/ci/docker/host-x86_64/test-various/Dockerfile
@@ -28,8 +28,9 @@
   qemu-system-x86 \
   && rm -rf /var/lib/apt/lists/*
 
-RUN curl -sL https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-x64.tar.xz | \
-  tar -xJ
+COPY scripts/nodejs.sh /scripts/
+RUN sh /scripts/nodejs.sh /node
+ENV PATH="/node/bin:${PATH}"
 
 WORKDIR /build/
 COPY scripts/musl-toolchain.sh /build/
@@ -45,7 +46,6 @@
 
 ENV RUST_CONFIGURE_ARGS \
   --musl-root-x86_64=/usr/local/x86_64-linux-musl \
-  --set build.nodejs=/node-v18.12.0-linux-x64/bin/node \
   --set rust.lld
 
 # Some run-make tests have assertions about code size, and enabling debug
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
index a3e8f61..6a09ab3 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
@@ -72,9 +72,9 @@
 
 COPY host-x86_64/x86_64-gnu-tools/checktools.sh /tmp/
 
-RUN curl -sL https://nodejs.org/dist/v14.20.0/node-v14.20.0-linux-x64.tar.xz | tar -xJ
-ENV NODE_FOLDER=/node-v14.20.0-linux-x64/bin
-ENV PATH="$NODE_FOLDER:${PATH}"
+COPY scripts/nodejs.sh /scripts/
+RUN sh /scripts/nodejs.sh /node
+ENV PATH="/node/bin:${PATH}"
 
 COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/
 
diff --git a/src/ci/docker/scripts/nodejs.sh b/src/ci/docker/scripts/nodejs.sh
new file mode 100644
index 0000000..ae28da0
--- /dev/null
+++ b/src/ci/docker/scripts/nodejs.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+set -ex
+
+NODEJS_VERSION=v20.12.2
+INSTALL_PATH=${1:-/node}
+
+url="https://nodejs.org/dist/${NODEJS_VERSION}/node-${NODEJS_VERSION}-linux-x64.tar.xz"
+curl -sL "$url" | tar -xJ
+mv node-${NODEJS_VERSION}-linux-x64 "${INSTALL_PATH}"
diff --git a/src/doc/book b/src/doc/book
index 3131aa4..d207d89 160000
--- a/src/doc/book
+++ b/src/doc/book
@@ -1 +1 @@
-Subproject commit 3131aa4642c627a24f523c82566b94a7d920f68c
+Subproject commit d207d894cc5e1d496ab99beeacd1a420e5d4d238
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
index eb3eb80..0c68e90 160000
--- a/src/doc/edition-guide
+++ b/src/doc/edition-guide
@@ -1 +1 @@
-Subproject commit eb3eb80e106d03250c1fb7c5666b1c8c59672862
+Subproject commit 0c68e90acaae5a611f8f5098a3c2980de9845ab2
diff --git a/src/doc/reference b/src/doc/reference
index 5569491..5854fcc 160000
--- a/src/doc/reference
+++ b/src/doc/reference
@@ -1 +1 @@
-Subproject commit 55694913b1301cc809f9bf4a1ad1b3d6920efbd9
+Subproject commit 5854fcc286557ad3ab34d325073d11d8118096b6
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
index b77a34b..07425fe 160000
--- a/src/doc/rustc-dev-guide
+++ b/src/doc/rustc-dev-guide
@@ -1 +1 @@
-Subproject commit b77a34bd46399687b4ce6a17198e9f316c988794
+Subproject commit 07425fed36b00e60341c5e29e28d37d40cbd4451
diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index 35bf2c2..673ff12b 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -568,22 +568,23 @@
 
 Supported values for this option are:
 
-- `none` - debuginfo and symbols (if they exist) are copied to the produced
-  binary or separate files depending on the target (e.g. `.pdb` files in case
-  of MSVC).
+- `none` - debuginfo and symbols are not modified.
 - `debuginfo` - debuginfo sections and debuginfo symbols from the symbol table
-  section are stripped at link time and are not copied to the produced binary
-  or separate files. This should leave backtraces mostly-intact but may make
-  using a debugger like gdb or lldb ineffectual.
-- `symbols` - same as `debuginfo`, but the rest of the symbol table section is stripped as well,
-  depending on platform support. On platforms which depend on this symbol table for backtraces,
-  profiling, and similar, this can affect them so negatively as to make the trace incomprehensible.
-  Programs which may be combined with others, such as CLI pipelines and developer tooling,
-  or even anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`.
+  section are stripped at link time and are not copied to the produced binary.
+  This should leave backtraces mostly-intact but may make using a debugger like
+  gdb or lldb ineffectual. Prior to 1.79, this unintentionally disabled the
+  generation of `*.pdb` files on MSVC, resulting in the absence of symbols.
+- `symbols` - same as `debuginfo`, but the rest of the symbol table section is
+  stripped as well, depending on platform support. On platforms which depend on
+  this symbol table for backtraces, profiling, and similar, this can affect
+  them so negatively as to make the trace incomprehensible. Programs which may
+  be combined with others, such as CLI pipelines and developer tooling, or even
+  anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`.
 
-Note that, at any level, removing debuginfo only necessarily impacts "friendly" introspection.
-`-Cstrip` cannot be relied on as a meaningful security or obfuscation measure, as disassemblers
-and decompilers can extract considerable information even in the absence of symbols.
+Note that, at any level, removing debuginfo only necessarily impacts "friendly"
+introspection. `-Cstrip` cannot be relied on as a meaningful security or
+obfuscation measure, as disassemblers and decompilers can extract considerable
+information even in the absence of symbols.
 
 ## symbol-mangling-version
 
diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md
index 185a3ba..bbd81e74 100644
--- a/src/doc/rustc/src/instrument-coverage.md
+++ b/src/doc/rustc/src/instrument-coverage.md
@@ -351,8 +351,8 @@
 This unstable option provides finer control over some aspects of coverage
 instrumentation. Pass one or more of the following values, separated by commas.
 
-- `branch` or `no-branch`
-  - Enables or disables branch coverage instrumentation.
+- Either `no-branch`, `branch` or `mcdc`
+  - `branch` enables branch coverage instrumentation and `mcdc` further enables modified condition/decision coverage instrumentation. `no-branch` disables branch coverage instrumentation, which is same as do not pass `branch` or `mcdc`.
 
 ## Other references
 
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index f7836f1..2578b71 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -138,6 +138,7 @@
 `aarch64-fuchsia` | ✓ | Alias for `aarch64-unknown-fuchsia`
 [`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia
 [`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android
+[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI
 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | ARM64 OpenHarmony
 `aarch64-unknown-none-softfloat` | * | Bare ARM64, softfloat
 `aarch64-unknown-none` | * | Bare ARM64, hardfloat
@@ -161,6 +162,7 @@
 `i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 3.2, glibc 2.17) [^x86_32-floats-x87]
 `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87]
 [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI]
+[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI]
 `i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI]
 `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI]
 [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI
@@ -196,6 +198,7 @@
 [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia
 [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android
 `x86_64-pc-solaris` | ✓ | 64-bit Solaris 11, illumos
+[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 64-bit x86 MinGW (Windows 10+), LLVM ABI
 `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27)
 [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | x86_64 OpenHarmony
 [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat
@@ -247,7 +250,6 @@
 [`aarch64-apple-visionos-sim`](platform-support/apple-visionos.md) | ✓ |  | ARM64 Apple visionOS Simulator
 [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ |  | ARM64 SOLID with TOPPERS/ASP3
 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * |  | ARM64 Nintendo Switch, Horizon
-[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
 [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? |  | ARM64 TEEOS |
 [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | ARM64 QNX Neutrino 7.1 RTOS |
 `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
@@ -294,7 +296,6 @@
 [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * |  | 32-bit x86 QNX Neutrino 7.0 RTOS  [^x86_32-floats-return-ABI]
 [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ |  | 32-bit x86, restricted to Pentium
 `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI]
-[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | [^x86_32-floats-return-ABI]
 `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku [^x86_32-floats-return-ABI]
 [`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI]
 [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI]
@@ -370,7 +371,6 @@
 [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? |  | x86 64-bit tvOS
 [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ |  | x86 64-bit Apple WatchOS simulator
 [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | x86 64-bit QNX Neutrino 7.1 RTOS |
-[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
 [`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ |   | 64-bit Unikraft with musl 1.2.3
 `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
 `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku
diff --git a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md
index 1bb2c67..9fd0ac4 100644
--- a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md
+++ b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md
@@ -11,48 +11,69 @@
 
 ## Requirements
 
-Target only supports cross-compilation, `core` and `alloc` are supported but
-`std` is not.
-
 Builds Arm64EC static and dynamic libraries and executables which can be run on
 AArch64 Windows 11 devices. Arm64EC static libraries can also be linked into
 Arm64X dynamic libraries and executables.
 
-Uses `arm64ec` as its `target_arch` - code built for Arm64EC must be compatible
-with x86_64 code (e.g., same structure layouts, function signatures, etc.) but
-use AArch64 intrinsics.
+Only supported backend is LLVM 18 or above:
+* 18.1.0 added initial support for Arm64EC.
+* 18.1.2 fixed import library generation (required for `raw-dylib` support).
+* 18.1.4 fixed linking issue for some intrinsics implemented in
+  `compiler_builtins`.
 
-Only supported backend is LLVM 18 (or above).
+### Reusing code from other architectures - x86_64 or AArch64?
+
+Arm64EC uses `arm64ec` as its `target_arch`, but it is possible to reuse
+existing architecture-specific code in most cases. The best mental model for
+deciding which architecture to reuse is to is to think of Arm64EC as an x86_64
+process that happens to use the AArch64 instruction set (with some caveats) and
+has a completely custom ABI.
+
+To put this in practice:
+* Arm64EC interacts with the operating system, other processes and other DLLs as
+  x86_64.
+  - For example, [in `backtrace`](https://github.com/rust-lang/backtrace-rs/commit/ef39a7d7da58b4cae8c8f3fc67a8300fd8d2d0d9)
+    we use the x86_64 versions of `CONTEXT` and `RtlVirtualUnwind`.
+  - If you are configuring a search path to find DLLs (e.g., to load plugins or
+    addons into your application), you should use the same path as the x86_64
+    version of your application, not the AArch64 path (since Arm64EC (i.e.,
+    x86_64) processes cannot load native AArch64 DLLs).
+* Arm64EC uses AArch64 intrinsics.
+  - For example, <https://github.com/rust-lang/portable-simd/commit/ca4033f49b1f6019561b8b161b4097b4a07f2e1b>
+    and <https://github.com/rust-lang/stdarch/commit/166ef7ba22b6a1d908d4b29a36e68ceca324808a>.
+* Assembly for AArch64 might be reusable for Arm64EC, but there are many
+  caveats. For full details see [Microsoft's documentation on the Arm64EC ABI](https://learn.microsoft.com/en-us/windows/arm/arm64ec-abi)
+  but in brief:
+  - Arm64EC uses a subset of AArch64 registers.
+  - Arm64EC uses a different name mangling scheme than AArch64.
+  - Arm64EC requires entry and exit thunks be generated for some functions.
+  - Indirect calls must be done via a call checker.
+  - Control Flow Guard and stack checks use different functions than AArch64.
 
 ## Building the target
 
 You can build Rust with support for the targets by adding it to the `target`
-list in `config.toml` and disabling `std`:
+list in `config.toml`:
 
 ```toml
 [build]
 target = [ "arm64ec-pc-windows-msvc" ]
-
-[target.arm64ec-pc-windows-msvc]
-no-std = true
 ```
 
 ## Building Rust programs
 
 Rust does not yet ship pre-compiled artifacts for this target. To compile for
 this target, you will either need to build Rust with the target enabled (see
-"Building the target" above), or build your own copy of `core` by using
-`build-std` or similar.
+"Building the target" above), or build your own copy using `build-std` or
+similar.
 
 ## Testing
 
 Tests can be run on AArch64 Windows 11 devices.
 
-Since this is a `no_std` target, the Rust test suite is not supported.
-
 ## Cross-compilation toolchains and C code
 
-C code can be built using the Arm64-targetting MSVC toolchain.
+C code can be built using the Arm64-targetting MSVC or Clang toolchain.
 
 To compile:
 
diff --git a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md
index a6246fa..ed55bcf 100644
--- a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md
+++ b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md
@@ -1,6 +1,6 @@
 # \*-pc-windows-gnullvm
 
-**Tier: 3**
+**Tier: 2 (without host tools)**
 
 Windows targets similar to `*-pc-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of GCC/Binutils.
 
@@ -12,38 +12,52 @@
 ## Target maintainers
 
 - [@mati865](https://github.com/mati865)
+- [@thomcc](https://github.com/thomcc)
 
 ## Requirements
 
-The easiest way to obtain these targets is cross-compilation but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend.
+The easiest way to obtain these targets is cross-compilation, but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend.
 Std support is expected to be on pair with `*-pc-windows-gnu`.
 
 Binaries for this target should be at least on pair with `*-pc-windows-gnu` in terms of requirements and functionality.
 
 Those targets follow Windows calling convention for `extern "C"`.
 
-Like with any other Windows target created binaries are in PE format.
+Like with any other Windows target, created binaries are in PE format.
 
 ## Building the target
 
-For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring cross compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors.
-Native bootstrapping builds require rather fragile hacks until host artifacts are available so I won't describe them here.
+These targets can be easily cross-compiled
+using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain or [MSYS2 CLANG*](https://www.msys2.org/docs/environments/) environments.
+Just fill `[target.*]` sections for both build and resulting compiler and set installation prefix in `config.toml`.
+Then run `./x.py install`.
+In my case I had ran `./x.py install --host x86_64-pc-windows-gnullvm --target x86_64-pc-windows-gnullvm` inside MSYS2 MINGW64 shell
+so `x86_64-pc-windows-gnu` was my build toolchain.
+
+Native bootstrapping is doable in two ways:
+- cross-compile gnullvm host toolchain and use it as build toolchain for the next build,
+- copy libunwind libraries and rename them to mimic libgcc like here: https://github.com/msys2/MINGW-packages/blob/68e640756df2df6df6afa60f025e3f936e7b977c/mingw-w64-rust/PKGBUILD#L108-L109, stage0 compiler will be mostly broken but good enough to build the next stage.
+
+The second option might stop working anytime, so it's not recommended.
 
 ## Building Rust programs
 
-Rust does not yet ship pre-compiled artifacts for this target. To compile for
-this target, you will either need to build Rust with the target enabled (see
-"Building the target" above), or build your own copy of `core` by using
-`build-std` or similar.
+Rust does ship a pre-compiled std library for those targets.
+That means one can easily cross-compile for those targets from other hosts if C proper toolchain is installed.
+
+Alternatively full toolchain can be built as described in the previous section.
 
 ## Testing
 
 Created binaries work fine on Windows or Wine using native hardware. Testing AArch64 on x86_64 is problematic though and requires spending some time with QEMU.
-Once these targets bootstrap themselves on native hardware they should pass Rust testsuite.
+Most of x86_64 testsuite does pass when cross-compiling,
+with exception for `rustdoc` and `ui-fulldeps` that fail with and error regarding a missing library,
+they do pass in native builds though.
+The only failing test is std's `process::tests::test_proc_thread_attributes` for unknown reason.
 
 ## Cross-compilation toolchains and C code
 
-Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM based C toolchains are used.
+Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM-based C toolchains are used.
 Those include:
 - [llvm-mingw](https://github.com/mstorsjo/llvm-mingw)
 - [MSYS2 with CLANG* environment](https://www.msys2.org/docs/environments)
diff --git a/src/doc/unstable-book/src/compiler-flags/coverage-options.md b/src/doc/unstable-book/src/compiler-flags/coverage-options.md
index 450573c..5e192d9 100644
--- a/src/doc/unstable-book/src/compiler-flags/coverage-options.md
+++ b/src/doc/unstable-book/src/compiler-flags/coverage-options.md
@@ -5,4 +5,4 @@
 
 Multiple options can be passed, separated by commas. Valid options are:
 
-- `branch` or `no-branch`: Enables or disables branch coverage instrumentation.
+- `no-branch`, `branch` or `mcdc`: `branch` enables branch coverage instrumentation and `mcdc` further enables modified condition/decision coverage instrumentation. `no-branch` disables branch coverage instrumentation as well as mcdc instrumentation, which is same as do not pass `branch` or `mcdc`.
diff --git a/src/doc/unstable-book/src/compiler-flags/wasm-c-abi.md b/src/doc/unstable-book/src/compiler-flags/wasm-c-abi.md
new file mode 100644
index 0000000..138a98d
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/wasm-c-abi.md
@@ -0,0 +1,10 @@
+# `wasm-c-abi`
+
+This option controls whether Rust uses the spec-compliant C ABI when compiling
+for the `wasm32-unknown-unknown` target.
+
+This makes it possible to be ABI-compatible with all other spec-compliant Wasm
+like Rusts `wasm32-wasi`.
+
+This compiler flag is perma-unstable, as it will be enabled by default in the
+future with no option to fall back to the old non-spec-compliant ABI.
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index aeb7137..af81cc6 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -600,13 +600,6 @@
         }
     }
 
-    pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option<StableSince> {
-        match self.const_stability(tcx)?.level {
-            StabilityLevel::Stable { since, .. } => Some(since),
-            StabilityLevel::Unstable { .. } => None,
-        }
-    }
-
     pub(crate) fn is_non_exhaustive(&self) -> bool {
         self.attrs.other_attrs.iter().any(|a| a.has_name(sym::non_exhaustive))
     }
@@ -2567,7 +2560,7 @@
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 11fc99e..a3b88a8 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -346,16 +346,28 @@
                     {
                         let desc =
                             short_markdown_summary(&item.doc_value(), &item.link_names(self.cache));
+                        // For searching purposes, a re-export is a duplicate if:
+                        //
+                        // - It's either an inline, or a true re-export
+                        // - It's got the same name
+                        // - Both of them have the same exact path
+                        let defid = (match &*item.kind {
+                            &clean::ItemKind::ImportItem(ref import) => import.source.did,
+                            _ => None,
+                        })
+                        .or_else(|| item.item_id.as_def_id());
                         // In case this is a field from a tuple struct, we don't add it into
                         // the search index because its name is something like "0", which is
                         // not useful for rustdoc search.
                         self.cache.search_index.push(IndexItem {
                             ty,
+                            defid,
                             name: s,
                             path: join_with_double_colon(path),
                             desc,
                             parent,
                             parent_idx: None,
+                            exact_path: None,
                             impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) =
                                 self.cache.parent_stack.last()
                             {
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index aa59988..336d18a 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -876,9 +876,10 @@
                 },
                 Some(c) => c,
             },
-            TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => {
-                Class::Ident(self.new_span(before, text))
-            }
+            TokenKind::RawIdent
+            | TokenKind::UnknownPrefix
+            | TokenKind::InvalidPrefix
+            | TokenKind::InvalidIdent => Class::Ident(self.new_span(before, text)),
             TokenKind::Lifetime { .. } => Class::Lifetime,
             TokenKind::Eof => panic!("Eof in advance"),
         };
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 4cab2d6..db1119e 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -78,8 +78,10 @@
 }
 
 // `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
-#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(not(windows), target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(Context<'_>, 160);
+#[cfg(all(windows, target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(Context<'_>, 168);
 
 /// Shared mutable state used in [`Context`] and elsewhere.
 pub(crate) struct SharedContext<'tcx> {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index a949f79..09a53af 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -111,11 +111,13 @@
 #[derive(Debug)]
 pub(crate) struct IndexItem {
     pub(crate) ty: ItemType,
+    pub(crate) defid: Option<DefId>,
     pub(crate) name: Symbol,
     pub(crate) path: String,
     pub(crate) desc: String,
     pub(crate) parent: Option<DefId>,
     pub(crate) parent_idx: Option<isize>,
+    pub(crate) exact_path: Option<String>,
     pub(crate) impl_id: Option<DefId>,
     pub(crate) search_type: Option<IndexItemFunctionType>,
     pub(crate) aliases: Box<[Symbol]>,
@@ -994,32 +996,20 @@
 /// consequence of the above rules.
 fn render_stability_since_raw_with_extra(
     w: &mut Buffer,
-    ver: Option<StableSince>,
+    stable_version: Option<StableSince>,
     const_stability: Option<ConstStability>,
-    containing_ver: Option<StableSince>,
-    containing_const_ver: Option<StableSince>,
     extra_class: &str,
 ) -> bool {
-    let stable_version = if ver != containing_ver
-        && let Some(ver) = &ver
-    {
-        since_to_string(ver)
-    } else {
-        None
-    };
-
     let mut title = String::new();
     let mut stability = String::new();
 
-    if let Some(ver) = stable_version {
-        stability.push_str(ver.as_str());
-        title.push_str(&format!("Stable since Rust version {ver}"));
+    if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
+        stability.push_str(&version);
+        title.push_str(&format!("Stable since Rust version {version}"));
     }
 
     let const_title_and_stability = match const_stability {
-        Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. })
-            if Some(since) != containing_const_ver =>
-        {
+        Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
             since_to_string(&since)
                 .map(|since| (format!("const since {since}"), format!("const: {since}")))
         }
@@ -1074,17 +1064,8 @@
     w: &mut Buffer,
     ver: Option<StableSince>,
     const_stability: Option<ConstStability>,
-    containing_ver: Option<StableSince>,
-    containing_const_ver: Option<StableSince>,
 ) -> bool {
-    render_stability_since_raw_with_extra(
-        w,
-        ver,
-        const_stability,
-        containing_ver,
-        containing_const_ver,
-        "",
-    )
+    render_stability_since_raw_with_extra(w, ver, const_stability, "")
 }
 
 fn render_assoc_item(
@@ -1583,7 +1564,6 @@
         cx: &mut Context<'_>,
         item: &clean::Item,
         parent: &clean::Item,
-        containing_item: &clean::Item,
         link: AssocItemLink<'_>,
         render_mode: RenderMode,
         is_default_item: bool,
@@ -1679,7 +1659,7 @@
                         })
                         .map(|item| format!("{}.{name}", item.type_()));
                     write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
-                    render_rightside(w, cx, item, containing_item, render_mode);
+                    render_rightside(w, cx, item, render_mode);
                     if trait_.is_some() {
                         // Anchors are only used on trait impls.
                         write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
@@ -1701,7 +1681,7 @@
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
                 write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
-                render_rightside(w, cx, item, containing_item, render_mode);
+                render_rightside(w, cx, item, render_mode);
                 if trait_.is_some() {
                     // Anchors are only used on trait impls.
                     write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
@@ -1787,7 +1767,6 @@
             cx,
             trait_item,
             if trait_.is_some() { &i.impl_item } else { parent },
-            parent,
             link,
             render_mode,
             false,
@@ -1803,7 +1782,6 @@
         t: &clean::Trait,
         i: &clean::Impl,
         parent: &clean::Item,
-        containing_item: &clean::Item,
         render_mode: RenderMode,
         rendering_params: ImplRenderingParameters,
     ) {
@@ -1831,7 +1809,6 @@
                 cx,
                 trait_item,
                 parent,
-                containing_item,
                 assoc_link,
                 render_mode,
                 true,
@@ -1854,7 +1831,6 @@
                 t,
                 i.inner_impl(),
                 &i.impl_item,
-                parent,
                 render_mode,
                 rendering_params,
             );
@@ -1876,7 +1852,6 @@
             cx,
             i,
             parent,
-            parent,
             rendering_params.show_def_docs,
             use_absolute,
             aliases,
@@ -1924,20 +1899,14 @@
 
 // Render the items that appear on the right side of methods, impls, and
 // associated types. For example "1.0.0 (const: 1.39.0) · source".
-fn render_rightside(
-    w: &mut Buffer,
-    cx: &Context<'_>,
-    item: &clean::Item,
-    containing_item: &clean::Item,
-    render_mode: RenderMode,
-) {
+fn render_rightside(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, render_mode: RenderMode) {
     let tcx = cx.tcx();
 
     // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
     // this condition.
-    let (const_stability, const_stable_since) = match render_mode {
-        RenderMode::Normal => (item.const_stability(tcx), containing_item.const_stable_since(tcx)),
-        RenderMode::ForDeref { .. } => (None, None),
+    let const_stability = match render_mode {
+        RenderMode::Normal => item.const_stability(tcx),
+        RenderMode::ForDeref { .. } => None,
     };
     let src_href = cx.src_href(item);
     let has_src_ref = src_href.is_some();
@@ -1947,8 +1916,6 @@
         &mut rightside,
         item.stable_since(tcx),
         const_stability,
-        containing_item.stable_since(tcx),
-        const_stable_since,
         if has_src_ref { "" } else { " rightside" },
     );
     if let Some(link) = src_href {
@@ -1970,7 +1937,6 @@
     cx: &mut Context<'_>,
     i: &Impl,
     parent: &clean::Item,
-    containing_item: &clean::Item,
     show_def_docs: bool,
     use_absolute: Option<bool>,
     // This argument is used to reference same type with different paths to avoid duplication
@@ -1985,7 +1951,7 @@
         format!(" data-aliases=\"{}\"", aliases.join(","))
     };
     write!(w, "<section id=\"{id}\" class=\"impl\"{aliases}>");
-    render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal);
+    render_rightside(w, cx, &i.impl_item, RenderMode::Normal);
     write!(
         w,
         "<a href=\"#{id}\" class=\"anchor\">§</a>\
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 168db5c..7de2aea 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -214,8 +214,6 @@
         &mut stability_since_raw,
         item.stable_since(cx.tcx()),
         item.const_stability(cx.tcx()),
-        None,
-        None,
     );
     let stability_since_raw: String = stability_since_raw.into_inner();
 
@@ -825,7 +823,7 @@
             write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>");
         }
         write!(w, "<section id=\"{id}\" class=\"method\">");
-        render_rightside(w, cx, m, t, RenderMode::Normal);
+        render_rightside(w, cx, m, RenderMode::Normal);
         write!(w, "<h4 class=\"code-header\">");
         render_assoc_item(
             w,
@@ -1686,8 +1684,6 @@
             w,
             variant.stable_since(tcx),
             variant.const_stability(tcx),
-            it.stable_since(tcx),
-            it.const_stable_since(tcx),
             " rightside",
         );
         w.write_str("<h3 class=\"code-header\">");
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 7083999..6d2bb31 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -59,10 +59,13 @@
     cache: &mut Cache,
     tcx: TyCtxt<'tcx>,
 ) -> SerializedSearchIndex {
+    // Maps from ID to position in the `crate_paths` array.
     let mut itemid_to_pathid = FxHashMap::default();
     let mut primitives = FxHashMap::default();
     let mut associated_types = FxHashMap::default();
-    let mut crate_paths = vec![];
+
+    // item type, display path, re-exported internal path
+    let mut crate_paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)> = vec![];
 
     // Attach all orphan items to the type's definition if the type
     // has since been learned.
@@ -72,11 +75,13 @@
             let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
             cache.search_index.push(IndexItem {
                 ty: item.type_(),
+                defid: item.item_id.as_def_id(),
                 name: item.name.unwrap(),
                 path: join_with_double_colon(&fqp[..fqp.len() - 1]),
                 desc,
                 parent: Some(parent),
                 parent_idx: None,
+                exact_path: None,
                 impl_id,
                 search_type: get_function_type_for_search(
                     item,
@@ -126,9 +131,10 @@
             map: &mut FxHashMap<F, isize>,
             itemid: F,
             lastpathid: &mut isize,
-            crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
+            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
             item_type: ItemType,
             path: &[Symbol],
+            exact_path: Option<&[Symbol]>,
         ) -> RenderTypeId {
             match map.entry(itemid) {
                 Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()),
@@ -136,7 +142,11 @@
                     let pathid = *lastpathid;
                     entry.insert(pathid);
                     *lastpathid += 1;
-                    crate_paths.push((item_type, path.to_vec()));
+                    crate_paths.push((
+                        item_type,
+                        path.to_vec(),
+                        exact_path.map(|path| path.to_vec()),
+                    ));
                     RenderTypeId::Index(pathid)
                 }
             }
@@ -149,14 +159,24 @@
             primitives: &mut FxHashMap<Symbol, isize>,
             associated_types: &mut FxHashMap<Symbol, isize>,
             lastpathid: &mut isize,
-            crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
+            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
         ) -> Option<RenderTypeId> {
-            let Cache { ref paths, ref external_paths, .. } = *cache;
+            let Cache { ref paths, ref external_paths, ref exact_paths, .. } = *cache;
             match id {
                 RenderTypeId::DefId(defid) => {
                     if let Some(&(ref fqp, item_type)) =
                         paths.get(&defid).or_else(|| external_paths.get(&defid))
                     {
+                        let exact_fqp = exact_paths
+                            .get(&defid)
+                            .or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp))
+                            // Re-exports only count if the name is exactly the same.
+                            // This is a size optimization, since it means we only need
+                            // to store the name once (and the path is re-used for everything
+                            // exported from this same module). It's also likely to Do
+                            // What I Mean, since if a re-export changes the name, it might
+                            // also be a change in semantic meaning.
+                            .filter(|fqp| fqp.last() == fqp.last());
                         Some(insert_into_map(
                             itemid_to_pathid,
                             ItemId::DefId(defid),
@@ -164,6 +184,7 @@
                             crate_paths,
                             item_type,
                             fqp,
+                            exact_fqp.map(|x| &x[..]).filter(|exact_fqp| exact_fqp != fqp),
                         ))
                     } else {
                         None
@@ -178,6 +199,7 @@
                         crate_paths,
                         ItemType::Primitive,
                         &[sym],
+                        None,
                     ))
                 }
                 RenderTypeId::Index(_) => Some(id),
@@ -188,6 +210,7 @@
                     crate_paths,
                     ItemType::AssocType,
                     &[sym],
+                    None,
                 )),
             }
         }
@@ -199,7 +222,7 @@
             primitives: &mut FxHashMap<Symbol, isize>,
             associated_types: &mut FxHashMap<Symbol, isize>,
             lastpathid: &mut isize,
-            crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
+            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
         ) {
             if let Some(generics) = &mut ty.generics {
                 for item in generics {
@@ -296,7 +319,7 @@
         }
     }
 
-    let Cache { ref paths, .. } = *cache;
+    let Cache { ref paths, ref exact_paths, ref external_paths, .. } = *cache;
 
     // Then, on parent modules
     let crate_items: Vec<&IndexItem> = search_index
@@ -311,7 +334,13 @@
                         lastpathid += 1;
 
                         if let Some(&(ref fqp, short)) = paths.get(&defid) {
-                            crate_paths.push((short, fqp.clone()));
+                            let exact_fqp = exact_paths
+                                .get(&defid)
+                                .or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp))
+                                .filter(|exact_fqp| {
+                                    exact_fqp.last() == Some(&item.name) && *exact_fqp != fqp
+                                });
+                            crate_paths.push((short, fqp.clone(), exact_fqp.cloned()));
                             Some(pathid)
                         } else {
                             None
@@ -319,6 +348,42 @@
                     }
                 });
 
+            if let Some(defid) = item.defid
+                && item.parent_idx.is_none()
+            {
+                // If this is a re-export, retain the original path.
+                // Associated items don't use this.
+                // Their parent carries the exact fqp instead.
+                let exact_fqp = exact_paths
+                    .get(&defid)
+                    .or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp));
+                item.exact_path = exact_fqp.and_then(|fqp| {
+                    // Re-exports only count if the name is exactly the same.
+                    // This is a size optimization, since it means we only need
+                    // to store the name once (and the path is re-used for everything
+                    // exported from this same module). It's also likely to Do
+                    // What I Mean, since if a re-export changes the name, it might
+                    // also be a change in semantic meaning.
+                    if fqp.last() != Some(&item.name) {
+                        return None;
+                    }
+                    let path =
+                        if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) {
+                            // `#[macro_export]` always exports to the crate root.
+                            tcx.crate_name(defid.krate).to_string()
+                        } else {
+                            if fqp.len() < 2 {
+                                return None;
+                            }
+                            join_with_double_colon(&fqp[..fqp.len() - 1])
+                        };
+                    if path == item.path {
+                        return None;
+                    }
+                    Some(path)
+                });
+            }
+
             // Omit the parent path if it is same to that of the prior item.
             if lastpath == &item.path {
                 item.path.clear();
@@ -356,7 +421,7 @@
 
     struct CrateData<'a> {
         items: Vec<&'a IndexItem>,
-        paths: Vec<(ItemType, Vec<Symbol>)>,
+        paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
         // The String is alias name and the vec is the list of the elements with this alias.
         //
         // To be noted: the `usize` elements are indexes to `items`.
@@ -374,6 +439,7 @@
         ty: ItemType,
         name: Symbol,
         path: Option<usize>,
+        exact_path: Option<usize>,
     }
 
     impl Serialize for Paths {
@@ -387,6 +453,10 @@
             if let Some(ref path) = self.path {
                 seq.serialize_element(path)?;
             }
+            if let Some(ref path) = self.exact_path {
+                assert!(self.path.is_some());
+                seq.serialize_element(path)?;
+            }
             seq.end()
         }
     }
@@ -409,14 +479,39 @@
                 mod_paths.insert(&item.path, index);
             }
             let mut paths = Vec::with_capacity(self.paths.len());
-            for (ty, path) in &self.paths {
+            for (ty, path, exact) in &self.paths {
                 if path.len() < 2 {
-                    paths.push(Paths { ty: *ty, name: path[0], path: None });
+                    paths.push(Paths { ty: *ty, name: path[0], path: None, exact_path: None });
                     continue;
                 }
                 let full_path = join_with_double_colon(&path[..path.len() - 1]);
+                let full_exact_path = exact
+                    .as_ref()
+                    .filter(|exact| exact.last() == path.last() && exact.len() >= 2)
+                    .map(|exact| join_with_double_colon(&exact[..exact.len() - 1]));
+                let exact_path = extra_paths.len() + self.items.len();
+                let exact_path = full_exact_path.as_ref().map(|full_exact_path| match extra_paths
+                    .entry(full_exact_path.clone())
+                {
+                    Entry::Occupied(entry) => *entry.get(),
+                    Entry::Vacant(entry) => {
+                        if let Some(index) = mod_paths.get(&full_exact_path) {
+                            return *index;
+                        }
+                        entry.insert(exact_path);
+                        if !revert_extra_paths.contains_key(&exact_path) {
+                            revert_extra_paths.insert(exact_path, full_exact_path.clone());
+                        }
+                        exact_path
+                    }
+                });
                 if let Some(index) = mod_paths.get(&full_path) {
-                    paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*index) });
+                    paths.push(Paths {
+                        ty: *ty,
+                        name: *path.last().unwrap(),
+                        path: Some(*index),
+                        exact_path,
+                    });
                     continue;
                 }
                 // It means it comes from an external crate so the item and its path will be
@@ -424,28 +519,54 @@
                 //
                 // `index` is put after the last `mod_paths`
                 let index = extra_paths.len() + self.items.len();
-                if !revert_extra_paths.contains_key(&index) {
-                    revert_extra_paths.insert(index, full_path.clone());
-                }
-                match extra_paths.entry(full_path) {
+                match extra_paths.entry(full_path.clone()) {
                     Entry::Occupied(entry) => {
                         paths.push(Paths {
                             ty: *ty,
                             name: *path.last().unwrap(),
                             path: Some(*entry.get()),
+                            exact_path,
                         });
                     }
                     Entry::Vacant(entry) => {
                         entry.insert(index);
+                        if !revert_extra_paths.contains_key(&index) {
+                            revert_extra_paths.insert(index, full_path);
+                        }
                         paths.push(Paths {
                             ty: *ty,
                             name: *path.last().unwrap(),
                             path: Some(index),
+                            exact_path,
                         });
                     }
                 }
             }
 
+            // Direct exports use adjacent arrays for the current crate's items,
+            // but re-exported exact paths don't.
+            let mut re_exports = Vec::new();
+            for (item_index, item) in self.items.iter().enumerate() {
+                if let Some(exact_path) = item.exact_path.as_ref() {
+                    if let Some(path_index) = mod_paths.get(&exact_path) {
+                        re_exports.push((item_index, *path_index));
+                    } else {
+                        let path_index = extra_paths.len() + self.items.len();
+                        let path_index = match extra_paths.entry(exact_path.clone()) {
+                            Entry::Occupied(entry) => *entry.get(),
+                            Entry::Vacant(entry) => {
+                                entry.insert(path_index);
+                                if !revert_extra_paths.contains_key(&path_index) {
+                                    revert_extra_paths.insert(path_index, exact_path.clone());
+                                }
+                                path_index
+                            }
+                        };
+                        re_exports.push((item_index, path_index));
+                    }
+                }
+            }
+
             let mut names = Vec::with_capacity(self.items.len());
             let mut types = String::with_capacity(self.items.len());
             let mut full_paths = Vec::with_capacity(self.items.len());
@@ -501,6 +622,7 @@
             crate_data.serialize_field("f", &functions)?;
             crate_data.serialize_field("D", &self.desc_index)?;
             crate_data.serialize_field("p", &paths)?;
+            crate_data.serialize_field("r", &re_exports)?;
             crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
             crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?;
             crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?;
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index e9c687b4..74c9130 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1627,24 +1627,27 @@
 	color: var(--copy-path-button-color);
 	background: var(--main-background-color);
 	height: 34px;
+	width: 33px;
 	margin-left: 10px;
 	padding: 0;
 	padding-left: 2px;
 	border: 0;
-	width: 33px;
-	line-height: 0;
 	font-size: 0;
 }
-
-#copy-path:before {
+#copy-path::before {
 	filter: var(--copy-path-img-filter);
 	content: url('clipboard-24048e6d87f63d07.svg');
-	width: 19px;
-	height: 18px;
 }
-#copy-path:hover:before {
+#copy-path:hover::before {
 	filter: var(--copy-path-img-hover-filter);
 }
+#copy-path.clicked::before {
+	/* Checkmark <https://www.svgrepo.com/svg/335033/checkmark> */
+	content: url('data:image/svg+xml,<svg viewBox="-1 -1 23 23" xmlns="http://www.w3.org/2000/svg" \
+		fill="black" height="18px">\
+		<g><path d="M9 19.414l-6.707-6.707 1.414-1.414L9 16.586 20.293 5.293l1.414 1.414"></path>\
+		</g></svg>');
+}
 
 @keyframes rotating {
 	from {
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index d24148b..8cebce7 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -239,20 +239,27 @@
  * `doc` contains the description of the crate.
  *
  * `p` is a list of path/type pairs. It is used for parents and function parameters.
+ * The first item is the type, the second is the name, the third is the visible path (if any) and
+ * the fourth is the canonical path used for deduplication (if any).
+ *
+ * `r` is the canonical path used for deduplication of re-exported items.
+ * It is not used for associated items like methods (that's the fourth element
+ * of `p`) but is used for modules items like free functions.
  *
  * `c` is an array of item indices that are deprecated.
  * @typedef {{
  *   doc: string,
  *   a: Object,
  *   n: Array<string>,
- *   t: String,
+ *   t: string,
  *   d: Array<string>,
- *   q: Array<[Number, string]>,
- *   i: Array<Number>,
+ *   q: Array<[number, string]>,
+ *   i: Array<number>,
  *   f: string,
- *   p: Array<Object>,
- *   b: Array<[Number, String]>,
- *   c: Array<Number>
+ *   p: Array<[number, string] | [number, string, number] | [number, string, number, number]>,
+ *   b: Array<[number, String]>,
+ *   c: Array<number>,
+ *   r: Array<[number, number]>,
  * }}
  */
 let RawSearchIndexCrate;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index ee7d196..64c3566 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1798,31 +1798,15 @@
         document.execCommand("copy");
         document.body.removeChild(el);
 
-        // There is always one children, but multiple childNodes.
-        but.children[0].style.display = "none";
-
-        let tmp;
-        if (but.childNodes.length < 2) {
-            tmp = document.createTextNode("✓");
-            but.appendChild(tmp);
-        } else {
-            onEachLazy(but.childNodes, e => {
-                if (e.nodeType === Node.TEXT_NODE) {
-                    tmp = e;
-                    return true;
-                }
-            });
-            tmp.textContent = "✓";
-        }
+        but.classList.add("clicked");
 
         if (reset_button_timeout !== null) {
             window.clearTimeout(reset_button_timeout);
         }
 
         function reset_button() {
-            tmp.textContent = "";
             reset_button_timeout = null;
-            but.children[0].style.display = "";
+            but.classList.remove("clicked");
         }
 
         reset_button_timeout = window.setTimeout(reset_button, 1000);
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 3daf1ad..41a9897 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -79,6 +79,7 @@
 
 // used for special search precedence
 const TY_GENERIC = itemTypes.indexOf("generic");
+const TY_IMPORT = itemTypes.indexOf("import");
 const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
 
 // Hard limit on how deep to recurse into generics when doing type-driven search.
@@ -1324,14 +1325,23 @@
                     obj.dist = result.dist;
                     const res = buildHrefAndPath(obj);
                     obj.displayPath = pathSplitter(res[0]);
-                    obj.fullPath = obj.displayPath + obj.name;
-                    // To be sure than it some items aren't considered as duplicate.
-                    obj.fullPath += "|" + obj.ty;
 
+                    // To be sure than it some items aren't considered as duplicate.
+                    obj.fullPath = res[2] + "|" + obj.ty;
                     if (duplicates.has(obj.fullPath)) {
                         continue;
                     }
+
+                    // Exports are specifically not shown if the items they point at
+                    // are already in the results.
+                    if (obj.ty === TY_IMPORT && duplicates.has(res[2])) {
+                        continue;
+                    }
+                    if (duplicates.has(res[2] + "|" + TY_IMPORT)) {
+                        continue;
+                    }
                     duplicates.add(obj.fullPath);
+                    duplicates.add(res[2]);
 
                     obj.href = res[1];
                     out.push(obj);
@@ -1454,16 +1464,7 @@
                 return 0;
             });
 
-            const transformed = transformResults(result_list);
-            const descs = await Promise.all(transformed.map(result => {
-                return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ?
-                    "" :
-                    searchState.loadDesc(result);
-            }));
-            for (const [i, result] of transformed.entries()) {
-                result.desc = descs[i];
-            }
-            return transformed;
+            return transformResults(result_list);
         }
 
         /**
@@ -2085,6 +2086,7 @@
                 path: item.path,
                 descShard: item.descShard,
                 descIndex: item.descIndex,
+                exactPath: item.exactPath,
                 ty: item.ty,
                 parent: item.parent,
                 type: item.type,
@@ -2506,6 +2508,16 @@
             sorted_others,
             parsedQuery);
         handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate);
+        await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => {
+            const descs = await Promise.all(list.map(result => {
+                return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ?
+                    "" :
+                    searchState.loadDesc(result);
+            }));
+            for (const [i, result] of list.entries()) {
+                result.desc = descs[i];
+            }
+        }));
         if (parsedQuery.error !== null && ret.others.length !== 0) {
             // It means some doc aliases were found so let's "remove" the error!
             ret.query.error = null;
@@ -2538,6 +2550,7 @@
         const type = itemTypes[item.ty];
         const name = item.name;
         let path = item.path;
+        let exactPath = item.exactPath;
 
         if (type === "mod") {
             displayPath = path + "::";
@@ -2559,6 +2572,7 @@
             const parentType = itemTypes[myparent.ty];
             let pageType = parentType;
             let pageName = myparent.name;
+            exactPath = `${myparent.exactPath}::${myparent.name}`;
 
             if (parentType === "primitive") {
                 displayPath = myparent.name + "::";
@@ -2587,7 +2601,7 @@
             href = ROOT_PATH + item.path.replace(/::/g, "/") +
                 "/" + type + "." + name + ".html";
         }
-        return [displayPath, href];
+        return [displayPath, href, `${exactPath}::${name}`];
     }
 
     function pathSplitter(path) {
@@ -2980,6 +2994,7 @@
                 id: pathIndex,
                 ty: TY_GENERIC,
                 path: null,
+                exactPath: null,
                 generics,
                 bindings,
             };
@@ -2989,6 +3004,7 @@
                 id: null,
                 ty: null,
                 path: null,
+                exactPath: null,
                 generics,
                 bindings,
             };
@@ -2998,6 +3014,7 @@
                 id: buildTypeMapIndex(item.name, isAssocType),
                 ty: item.ty,
                 path: item.path,
+                exactPath: item.exactPath,
                 generics,
                 bindings,
             };
@@ -3453,6 +3470,8 @@
                 path: "",
                 descShard,
                 descIndex,
+                exactPath: "",
+                desc: crateCorpus.doc,
                 parent: undefined,
                 type: null,
                 id,
@@ -3478,6 +3497,9 @@
             // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present,
             // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11
             const itemPaths = new Map(crateCorpus.q);
+            // An array of [(Number) item index, (Number) path index]
+            // Used to de-duplicate inlined and re-exported stuff
+            const itemReexports = new Map(crateCorpus.r);
             // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
             const itemParentIdxs = crateCorpus.i;
             // a map Number, string for impl disambiguators
@@ -3511,9 +3533,10 @@
                     path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
                     lastPath = path;
                 }
+                const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path;
 
-                lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
-                paths[i] = {ty: ty, name: name, path: path};
+                lowercasePaths.push({ty, name: name.toLowerCase(), path, exactPath});
+                paths[i] = {ty, name, path, exactPath};
             }
 
             // convert `item*` into an object form, and construct word indices.
@@ -3572,6 +3595,7 @@
                     path,
                     descShard,
                     descIndex,
+                    exactPath: itemReexports.has(i) ? itemPaths.get(itemReexports.get(i)) : path,
                     parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
                     type,
                     id,
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index 392a5a1..f397234 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -56,6 +56,7 @@
     "aarch64-apple-ios-sim",
     "aarch64-unknown-fuchsia",
     "aarch64-linux-android",
+    "aarch64-pc-windows-gnullvm",
     "aarch64-pc-windows-msvc",
     "aarch64-unknown-hermit",
     "aarch64-unknown-linux-gnu",
@@ -96,6 +97,7 @@
     "i686-apple-darwin",
     "i686-linux-android",
     "i686-pc-windows-gnu",
+    "i686-pc-windows-gnullvm",
     "i686-pc-windows-msvc",
     "i686-unknown-freebsd",
     "i686-unknown-linux-gnu",
@@ -157,6 +159,7 @@
     "x86_64-unknown-fuchsia",
     "x86_64-linux-android",
     "x86_64-pc-windows-gnu",
+    "x86_64-pc-windows-gnullvm",
     "x86_64-pc-windows-msvc",
     "x86_64-pc-solaris",
     "x86_64-unikraft-linux-musl",
@@ -464,7 +467,8 @@
                 | PkgType::LlvmTools
                 | PkgType::RustAnalysis
                 | PkgType::JsonDocs
-                | PkgType::RustcCodegenCranelift => {
+                | PkgType::RustcCodegenCranelift
+                | PkgType::LlvmBitcodeLinker => {
                     extensions.push(host_component(pkg));
                 }
                 PkgType::RustcDev | PkgType::RustcDocs => {
diff --git a/src/tools/build-manifest/src/versions.rs b/src/tools/build-manifest/src/versions.rs
index e4cdf96..233a267 100644
--- a/src/tools/build-manifest/src/versions.rs
+++ b/src/tools/build-manifest/src/versions.rs
@@ -58,6 +58,7 @@
     Miri = "miri"; preview = true,
     JsonDocs = "rust-docs-json"; preview = true,
     RustcCodegenCranelift = "rustc-codegen-cranelift"; preview = true,
+    LlvmBitcodeLinker = "llvm-bitcode-linker"; preview = true,
 }
 
 impl PkgType {
@@ -94,6 +95,7 @@
             PkgType::ReproducibleArtifacts => true,
             PkgType::RustMingw => true,
             PkgType::RustAnalysis => true,
+            PkgType::LlvmBitcodeLinker => true,
         }
     }
 
@@ -121,6 +123,7 @@
             Rustfmt => HOSTS,
             RustAnalysis => TARGETS,
             LlvmTools => TARGETS,
+            LlvmBitcodeLinker => HOSTS,
         }
     }
 
diff --git a/src/tools/cargo b/src/tools/cargo
index 6f06fe9..80d5b60 160000
--- a/src/tools/cargo
+++ b/src/tools/cargo
@@ -1 +1 @@
-Subproject commit 6f06fe908a5ee0f415c187f868ea627e82efe07d
+Subproject commit 80d5b607dde6ef97dfff4e23923822c01d2bb036
diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml
index 05e1b3b..348d520 100644
--- a/src/tools/clippy/.github/workflows/remark.yml
+++ b/src/tools/clippy/.github/workflows/remark.yml
@@ -24,7 +24,7 @@
         node-version: '18.x'
 
     - name: Install remark
-      run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
+      run: npm install remark-cli remark-lint remark-lint-maximum-line-length@^3.1.3 remark-preset-lint-recommended remark-gfm
 
     - name: Install mdbook
       run: |
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index f7e7ed8..bd3a04e 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -5894,6 +5894,7 @@
 [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
 [`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates
 [`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars
+[`allowed-prefixes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-prefixes
 [`allowed-scripts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-scripts
 [`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports
 [`arithmetic-side-effects-allowed`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed
diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md
index e7972b0..7bdfb97 100644
--- a/src/tools/clippy/book/src/README.md
+++ b/src/tools/clippy/book/src/README.md
@@ -18,17 +18,27 @@
 | `clippy::all`         | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
 | `clippy::correctness` | code that is outright wrong or useless                                              | **deny**      |
 | `clippy::suspicious`  | code that is most likely wrong or useless                                           | **warn**      |
+| `clippy::style`       | code that should be written in a more idiomatic way                                 | **warn**      |
 | `clippy::complexity`  | code that does something simple but in a complex way                                | **warn**      |
 | `clippy::perf`        | code that can be written to run faster                                              | **warn**      |
-| `clippy::style`       | code that should be written in a more idiomatic way                                 | **warn**      |
-| `clippy::pedantic`    | lints which are rather strict or might have false positives                         | allow         |
+| `clippy::pedantic`    | lints which are rather strict or have occasional false positives                    | allow         |
+| `clippy::restriction` | lints which prevent the use of language and library features[^restrict]             | allow         |
 | `clippy::nursery`     | new lints that are still under development                                          | allow         |
-| `clippy::cargo`       | lints for the cargo manifest                                                        | allow         |                                   | allow         |
+| `clippy::cargo`       | lints for the cargo manifest                                                        | allow         |
 
-More to come, please [file an
-issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
+More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
 
-The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also
-contains "restriction lints", which are for things which are usually not
-considered "bad", but may be useful to turn on in specific cases. These should
-be used very selectively, if at all.
+The `restriction` category should, *emphatically*, not be enabled as a whole. The contained
+lints may lint against perfectly reasonable code, may not have an alternative suggestion,
+and may contradict any other lints (including other categories). Lints should be considered
+on a case-by-case basis before enabling.
+
+[^restrict]: Some use cases for `restriction` lints include:
+    - Strict coding styles (e.g. [`clippy::else_if_without_else`]).
+    - Additional restrictions on CI (e.g. [`clippy::todo`]).
+    - Preventing panicking in certain functions (e.g. [`clippy::unwrap_used`]).
+    - Running a lint only on a subset of code (e.g. `#[forbid(clippy::float_arithmetic)]` on a module).
+
+[`clippy::else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
+[`clippy::todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
+[`clippy::unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index e30a5f9..b80ac63 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -18,7 +18,6 @@
     - [Cargo lints](#cargo-lints)
   - [Rustfix tests](#rustfix-tests)
   - [Testing manually](#testing-manually)
-  - [Running directly](#running-directly)
   - [Lint declaration](#lint-declaration)
   - [Lint registration](#lint-registration)
   - [Lint passes](#lint-passes)
@@ -176,23 +175,26 @@
 
 Manually testing against an example file can be useful if you have added some
 `println!`s and the test suite output becomes unreadable. To try Clippy with
-your local modifications, run
+your local modifications, run the following from the Clippy directory:
 
-```
+```bash
 cargo dev lint input.rs
 ```
 
-from the working copy root. With tests in place, let's have a look at
-implementing our lint now.
+To run Clippy on an existing project rather than a single file you can use
 
-## Running directly
+```bash
+cargo dev lint /path/to/project
+```
 
-While it's easier to just use `cargo dev lint`, it might be desirable to get
-`target/release/cargo-clippy` and `target/release/clippy-driver` to work as well in some cases.
-By default, they don't work because clippy dynamically links rustc. To help them find rustc,
-add the path printed by`rustc --print target-libdir` (ran inside this workspace so that the rustc version matches)
-to your library search path.
-On linux, this can be done by setting the `LD_LIBRARY_PATH` environment variable to that path.
+Or set up a rustup toolchain that points to the local Clippy binaries
+
+```bash
+cargo dev setup toolchain
+
+# Then in `/path/to/project` you can run
+cargo +clippy clippy
+```
 
 ## Lint declaration
 
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 4a2727c..7cefa68 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -164,6 +164,32 @@
 * [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars)
 
 
+## `allowed-prefixes`
+List of prefixes to allow when determining whether an item's name ends with the module's name.
+If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
+then don't emit a warning.
+
+#### Example
+
+```toml
+allowed-prefixes = [ "to", "from" ]
+```
+
+#### Noteworthy
+
+- By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from`
+- PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included,
+  `TryInto` will also be included)
+- Use `".."` as part of the list to indicate that the configured values should be appended to the
+default configuration of Clippy. By default, any configuration will replace the default value
+
+**Default Value:** `["to", "as", "into", "from", "try_into", "try_from"]`
+
+---
+**Affected lints:**
+* [`module_name_repetitions`](https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions)
+
+
 ## `allowed-scripts`
 The list of unicode scripts allowed to be used in the scope.
 
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 53c10f7..7812822 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -39,6 +39,7 @@
 ];
 const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
 const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
+const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
 
 /// Conf with parse errors
 #[derive(Default)]
@@ -589,6 +590,26 @@
     /// 2. Paths with any segment that containing the word 'prelude'
     /// are already allowed by default.
     (allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default()),
+    /// Lint: MODULE_NAME_REPETITIONS.
+    ///
+    /// List of prefixes to allow when determining whether an item's name ends with the module's name.
+    /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
+    /// then don't emit a warning.
+    ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allowed-prefixes = [ "to", "from" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from`
+    /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included,
+    ///   `TryInto` will also be included)
+    /// - Use `".."` as part of the list to indicate that the configured values should be appended to the
+    /// default configuration of Clippy. By default, any configuration will replace the default value
+    (allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()),
 }
 
 /// Search for the configuration file.
@@ -649,6 +670,7 @@
         Ok(mut conf) => {
             extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
             extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
+            extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES);
             // TODO: THIS SHOULD BE TESTED, this comment will be gone soon
             if conf.conf.allowed_idents_below_min_chars.contains("..") {
                 conf.conf
diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs
index ee559d4..2562314 100644
--- a/src/tools/clippy/clippy_dev/src/fmt.rs
+++ b/src/tools/clippy/clippy_dev/src/fmt.rs
@@ -35,7 +35,6 @@
 }
 
 // the "main" function of cargo dev fmt
-#[allow(clippy::missing_panics_doc)]
 pub fn run(check: bool, verbose: bool) {
     fn try_run(context: &FmtContext) -> Result<bool, CliError> {
         let mut success = true;
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index bb62e90..385191e 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -9,6 +9,7 @@
     unused_lifetimes,
     unused_qualifications
 )]
+#![allow(clippy::missing_panics_doc)]
 
 // The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate.
 #[allow(unused_extern_crates)]
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index 5bd9994..397a0e9 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -46,6 +46,13 @@
             }
         },
         Some(("setup", sub_command)) => match sub_command.subcommand() {
+            Some(("git-hook", matches)) => {
+                if matches.get_flag("remove") {
+                    setup::git_hook::remove_hook();
+                } else {
+                    setup::git_hook::install_hook(matches.get_flag("force-override"));
+                }
+            },
             Some(("intellij", matches)) => {
                 if matches.get_flag("remove") {
                     setup::intellij::remove_rustc_src();
@@ -57,12 +64,12 @@
                     );
                 }
             },
-            Some(("git-hook", matches)) => {
-                if matches.get_flag("remove") {
-                    setup::git_hook::remove_hook();
-                } else {
-                    setup::git_hook::install_hook(matches.get_flag("force-override"));
-                }
+            Some(("toolchain", matches)) => {
+                setup::toolchain::create(
+                    matches.get_flag("force"),
+                    matches.get_flag("release"),
+                    matches.get_one::<String>("name").unwrap(),
+                );
             },
             Some(("vscode-tasks", matches)) => {
                 if matches.get_flag("remove") {
@@ -210,6 +217,19 @@
                 .about("Support for setting up your personal development environment")
                 .arg_required_else_help(true)
                 .subcommands([
+                    Command::new("git-hook")
+                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
+                        .args([
+                            Arg::new("remove")
+                                .long("remove")
+                                .action(ArgAction::SetTrue)
+                                .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
+                            Arg::new("force-override")
+                                .long("force-override")
+                                .short('f')
+                                .action(ArgAction::SetTrue)
+                                .help("Forces the override of an existing git pre-commit hook"),
+                        ]),
                     Command::new("intellij")
                         .about("Alter dependencies so Intellij Rust can find rustc internals")
                         .args([
@@ -225,18 +245,23 @@
                                 .conflicts_with("remove")
                                 .required(true),
                         ]),
-                    Command::new("git-hook")
-                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
+                    Command::new("toolchain")
+                        .about("Install a rustup toolchain pointing to the local clippy build")
                         .args([
-                            Arg::new("remove")
-                                .long("remove")
-                                .action(ArgAction::SetTrue)
-                                .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
-                            Arg::new("force-override")
-                                .long("force-override")
+                            Arg::new("force")
+                                .long("force")
                                 .short('f')
                                 .action(ArgAction::SetTrue)
-                                .help("Forces the override of an existing git pre-commit hook"),
+                                .help("Override an existing toolchain"),
+                            Arg::new("release")
+                                .long("release")
+                                .short('r')
+                                .action(ArgAction::SetTrue)
+                                .help("Point to --release clippy binaries"),
+                            Arg::new("name")
+                                .long("name")
+                                .default_value("clippy")
+                                .help("The name of the created toolchain"),
                         ]),
                     Command::new("vscode-tasks")
                         .about("Add several tasks to vscode for formatting, validation and testing")
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 5d9cde0..2940d56 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -36,7 +36,6 @@
 /// # Errors
 ///
 /// This function errors out if the files couldn't be created or written to.
-#[allow(clippy::missing_panics_doc)]
 pub fn create(
     pass: &String,
     lint_name: Option<&String>,
diff --git a/src/tools/clippy/clippy_dev/src/setup/mod.rs b/src/tools/clippy/clippy_dev/src/setup/mod.rs
index f691ae4..b0d3181 100644
--- a/src/tools/clippy/clippy_dev/src/setup/mod.rs
+++ b/src/tools/clippy/clippy_dev/src/setup/mod.rs
@@ -1,5 +1,6 @@
 pub mod git_hook;
 pub mod intellij;
+pub mod toolchain;
 pub mod vscode;
 
 use std::path::Path;
diff --git a/src/tools/clippy/clippy_dev/src/setup/toolchain.rs b/src/tools/clippy/clippy_dev/src/setup/toolchain.rs
new file mode 100644
index 0000000..8d98c6c
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/setup/toolchain.rs
@@ -0,0 +1,75 @@
+use std::env::consts::EXE_SUFFIX;
+use std::env::current_dir;
+use std::ffi::OsStr;
+use std::fs;
+use std::path::{Path, PathBuf};
+use walkdir::WalkDir;
+
+use super::verify_inside_clippy_dir;
+
+pub fn create(force: bool, release: bool, name: &str) {
+    if !verify_inside_clippy_dir() {
+        return;
+    }
+
+    let rustup_home = std::env::var("RUSTUP_HOME").unwrap();
+    let toolchain = std::env::var("RUSTUP_TOOLCHAIN").unwrap();
+
+    let src = PathBuf::from_iter([&rustup_home, "toolchains", &toolchain]);
+    let dest = PathBuf::from_iter([&rustup_home, "toolchains", name]);
+
+    if dest.exists() {
+        if force {
+            fs::remove_dir_all(&dest).unwrap();
+        } else {
+            println!("{} already exists, pass `--force` to override it", dest.display());
+            return;
+        }
+    }
+
+    for entry in WalkDir::new(&src) {
+        let entry = entry.unwrap();
+        let relative = entry.path().strip_prefix(&src).unwrap();
+
+        if relative.starts_with("bin")
+            && matches!(
+                relative.file_stem().and_then(OsStr::to_str),
+                Some("cargo-clippy" | "clippy-driver")
+            )
+        {
+            continue;
+        }
+
+        let target = dest.join(relative);
+        if entry.file_type().is_dir() {
+            fs::create_dir(&target).unwrap();
+        } else {
+            fs::hard_link(entry.path(), target).unwrap();
+        }
+    }
+
+    symlink_bin("cargo-clippy", &dest, release);
+    symlink_bin("clippy-driver", &dest, release);
+
+    println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`");
+    println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes");
+}
+
+fn symlink_bin(bin: &str, dest: &Path, release: bool) {
+    #[cfg(windows)]
+    use std::os::windows::fs::symlink_file as symlink;
+
+    #[cfg(not(windows))]
+    use std::os::unix::fs::symlink;
+
+    let profile = if release { "release" } else { "debug" };
+    let file_name = format!("{bin}{EXE_SUFFIX}");
+
+    let mut src = current_dir().unwrap();
+    src.extend(["target", profile, &file_name]);
+
+    let mut dest = dest.to_path_buf();
+    dest.extend(["bin", &file_name]);
+
+    symlink(src, dest).unwrap();
+}
diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
index 1102c7f..3893389 100644
--- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
+++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
@@ -56,7 +56,12 @@
             && let Some(send) = cx.tcx.get_diagnostic_item(sym::Send)
             && let Some(sync) = cx.tcx.lang_items().sync_trait()
             && let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[]))
-            && !(is_send && is_sync)
+            && let reason = match (is_send, is_sync) {
+                (false, false) => "neither `Send` nor `Sync`",
+                (false, true) => "not `Send`",
+                (true, false) => "not `Sync`",
+                _ => return,
+            }
             && !is_from_proc_macro(cx, expr)
         {
             span_lint_and_then(
@@ -66,21 +71,12 @@
                 "usage of an `Arc` that is not `Send` and `Sync`",
                 |diag| {
                     with_forced_trimmed_paths!({
-                        diag.note(format!("`Arc<{arg_ty}>` is not `Send` and `Sync` as:"));
-
-                        if !is_send {
-                            diag.note(format!("- the trait `Send` is not implemented for `{arg_ty}`"));
-                        }
-                        if !is_sync {
-                            diag.note(format!("- the trait `Sync` is not implemented for `{arg_ty}`"));
-                        }
-
-                        diag.help("consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types");
-
-                        diag.note("if you intend to use `Arc` with `Send` and `Sync` traits");
-
                         diag.note(format!(
-                            "wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `{arg_ty}`"
+                            "`Arc<{arg_ty}>` is not `Send` and `Sync` as `{arg_ty}` is {reason}"
+                        ));
+                        diag.help("if the `Arc` will not used be across threads replace it with an `Rc`");
+                        diag.help(format!(
+                            "otherwise make `{arg_ty}` `Send` and `Sync` or consider a wrapper type such as `Mutex`"
                         ));
                     });
                 },
diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
index 3a8844d..736ee48 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
@@ -2,12 +2,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::{Attribute, MetaItem};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_lint::EarlyContext;
+use rustc_lint::LateContext;
 use rustc_span::{sym, Span};
 use std::collections::hash_map::Entry;
 
 fn emit_if_duplicated(
-    cx: &EarlyContext<'_>,
+    cx: &LateContext<'_>,
     attr: &MetaItem,
     attr_paths: &mut FxHashMap<String, Span>,
     complete_path: String,
@@ -26,7 +26,7 @@
 }
 
 fn check_duplicated_attr(
-    cx: &EarlyContext<'_>,
+    cx: &LateContext<'_>,
     attr: &MetaItem,
     attr_paths: &mut FxHashMap<String, Span>,
     parent: &mut Vec<String>,
@@ -64,7 +64,7 @@
     }
 }
 
-pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
+pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
     let mut attr_paths = FxHashMap::default();
 
     for attr in attrs {
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
index 684ad7d..8f47bc7 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
@@ -17,7 +17,7 @@
 mod utils;
 
 use clippy_config::msrvs::Msrv;
-use rustc_ast::{Attribute, Crate, MetaItemKind, NestedMetaItem};
+use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
 use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, impl_lint_pass};
@@ -534,11 +534,13 @@
     BLANKET_CLIPPY_RESTRICTION_LINTS,
     SHOULD_PANIC_WITHOUT_EXPECT,
     MIXED_ATTRIBUTES_STYLE,
+    DUPLICATED_ATTRIBUTES,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Attributes {
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
         blanket_clippy_restriction_lints::check_command_line(cx);
+        duplicated_attributes::check(cx, cx.tcx.hir().krate_attrs());
     }
 
     fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
@@ -578,6 +580,7 @@
             _ => {},
         }
         mixed_attributes_style::check(cx, item.span, attrs);
+        duplicated_attributes::check(cx, attrs);
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
@@ -606,17 +609,11 @@
     MAYBE_MISUSED_CFG,
     DEPRECATED_CLIPPY_CFG_ATTR,
     UNNECESSARY_CLIPPY_CFG,
-    DUPLICATED_ATTRIBUTES,
 ]);
 
 impl EarlyLintPass for EarlyAttributes {
-    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
-        duplicated_attributes::check(cx, &krate.attrs);
-    }
-
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
         empty_line_after::check(cx, item);
-        duplicated_attributes::check(cx, &item.attrs);
     }
 
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 6edfebb..b6341b3 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -346,11 +346,18 @@
                 _ => None,
             }
             .and_then(|op| {
-                Some(format!(
-                    "{}{op}{}",
-                    snippet_opt(cx, lhs.span)?,
-                    snippet_opt(cx, rhs.span)?
-                ))
+                let lhs_snippet = snippet_opt(cx, lhs.span)?;
+                let rhs_snippet = snippet_opt(cx, rhs.span)?;
+
+                if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) {
+                    if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) {
+                        // e.g. `(a as u64) < b`. Without the parens the `<` is
+                        // interpreted as a start of generic arguments for `u64`
+                        return Some(format!("({lhs_snippet}){op}{rhs_snippet}"));
+                    }
+                }
+
+                Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
             })
         },
         ExprKind::MethodCall(path, receiver, [], _) => {
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index 063aab2..d14898a 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -754,11 +754,7 @@
 
 impl<'tcx> LateLintPass<'tcx> for Casts {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !in_external_macro(cx.sess(), expr.span) {
-            ptr_as_ptr::check(cx, expr, &self.msrv);
-        }
-
-        if expr.span.from_expansion() {
+        if in_external_macro(cx.sess(), expr.span) {
             return;
         }
 
@@ -771,7 +767,7 @@
                 cx.typeck_results().expr_ty(expr),
             );
 
-            if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
+            if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
                 return;
             }
             cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
@@ -782,7 +778,7 @@
             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
             zero_ptr::check(cx, expr, cast_expr, cast_to_hir);
 
-            if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
+            if cast_to.is_numeric() {
                 cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span);
                 if cast_from.is_numeric() {
                     cast_possible_wrap::check(cx, expr, cast_from, cast_to);
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index f83fb1b..89e2b34 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -9,7 +9,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
-    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
+    self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
     Pat, PatKind, Path, QPath, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass};
@@ -599,7 +599,7 @@
     }
 
     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
-        if let PatKind::Binding(BindingAnnotation::REF, id, name, _) = pat.kind {
+        if let PatKind::Binding(BindingMode::REF, id, name, _) = pat.kind {
             if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
                 // This binding id has been seen before. Add this pattern to the list of changes.
                 if let Some(prev_pat) = opt_prev_pat {
@@ -1016,9 +1016,18 @@
                         },
                         _ => (0, false),
                     };
+                    let is_in_tuple = matches!(
+                        get_parent_expr(cx, data.first_expr),
+                        Some(Expr {
+                            kind: ExprKind::Tup(..),
+                            ..
+                        })
+                    );
+
                     let sugg = if !snip_is_macro
                         && (calls_field || expr.precedence().order() < precedence)
                         && !has_enclosing_paren(&snip)
+                        && !is_in_tuple
                     {
                         format!("({snip})")
                     } else {
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 5f9700b..42cd19f 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -132,7 +132,7 @@
     ///
     /// ### Why is this bad?
     /// Deriving `serde::Deserialize` will create a constructor
-    /// that may violate invariants hold by another constructor.
+    /// that may violate invariants held by another constructor.
     ///
     /// ### Example
     /// ```rust,ignore
diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
index 26f120c..f935ae2 100644
--- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
@@ -11,7 +11,7 @@
 pub fn check(
     cx: &LateContext<'_>,
     owner_id: OwnerId,
-    sig: &FnSig<'_>,
+    sig: FnSig<'_>,
     headers: DocHeaders,
     body_id: Option<BodyId>,
     panic_span: Option<Span>,
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index b135e4e..4bced10 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -3,7 +3,7 @@
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::Visitable;
-use clippy_utils::{is_entrypoint_fn, method_chain_args};
+use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
 use pulldown_cmark::Event::{
     Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
 };
@@ -11,9 +11,8 @@
 use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
 use rustc_ast::ast::Attribute;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{AnonConst, Expr};
+use rustc_hir::{AnonConst, Expr, ImplItemKind, ItemKind, Node, TraitItemKind, Unsafety};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::lint::in_external_macro;
@@ -366,7 +365,6 @@
 #[derive(Clone)]
 pub struct Documentation {
     valid_idents: FxHashSet<String>,
-    in_trait_impl: bool,
     check_private_items: bool,
 }
 
@@ -374,7 +372,6 @@
     pub fn new(valid_idents: &[String], check_private_items: bool) -> Self {
         Self {
             valid_idents: valid_idents.iter().cloned().collect(),
-            in_trait_impl: false,
             check_private_items,
         }
     }
@@ -394,36 +391,72 @@
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Documentation {
-    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
-        let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
-        check_attrs(cx, &self.valid_idents, attrs);
-    }
-
-    fn check_variant(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::Variant<'tcx>) {
-        let attrs = cx.tcx.hir().attrs(variant.hir_id);
-        check_attrs(cx, &self.valid_idents, attrs);
-    }
-
-    fn check_field_def(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::FieldDef<'tcx>) {
-        let attrs = cx.tcx.hir().attrs(variant.hir_id);
-        check_attrs(cx, &self.valid_idents, attrs);
-    }
-
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        let attrs = cx.tcx.hir().attrs(item.hir_id());
+    fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
         let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
             return;
         };
 
-        match item.kind {
-            hir::ItemKind::Fn(ref sig, _, body_id) => {
-                if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
-                    let body = cx.tcx.hir().body(body_id);
+        match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {
+            Node::Item(item) => match item.kind {
+                ItemKind::Fn(sig, _, body_id) => {
+                    if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
+                        let body = cx.tcx.hir().body(body_id);
 
-                    let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
+                        let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
+                        missing_headers::check(
+                            cx,
+                            item.owner_id,
+                            sig,
+                            headers,
+                            Some(body_id),
+                            panic_span,
+                            self.check_private_items,
+                        );
+                    }
+                },
+                ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
+                    (false, Unsafety::Unsafe) => span_lint(
+                        cx,
+                        MISSING_SAFETY_DOC,
+                        cx.tcx.def_span(item.owner_id),
+                        "docs for unsafe trait missing `# Safety` section",
+                    ),
+                    (true, Unsafety::Normal) => span_lint(
+                        cx,
+                        UNNECESSARY_SAFETY_DOC,
+                        cx.tcx.def_span(item.owner_id),
+                        "docs for safe trait have unnecessary `# Safety` section",
+                    ),
+                    _ => (),
+                },
+                _ => (),
+            },
+            Node::TraitItem(trait_item) => {
+                if let TraitItemKind::Fn(sig, ..) = trait_item.kind
+                    && !in_external_macro(cx.tcx.sess, trait_item.span)
+                {
                     missing_headers::check(
                         cx,
-                        item.owner_id,
+                        trait_item.owner_id,
+                        sig,
+                        headers,
+                        None,
+                        None,
+                        self.check_private_items,
+                    );
+                }
+            },
+            Node::ImplItem(impl_item) => {
+                if let ImplItemKind::Fn(sig, body_id) = impl_item.kind
+                    && !in_external_macro(cx.tcx.sess, impl_item.span)
+                    && !is_trait_impl_item(cx, impl_item.hir_id())
+                {
+                    let body = cx.tcx.hir().body(body_id);
+
+                    let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value);
+                    missing_headers::check(
+                        cx,
+                        impl_item.owner_id,
                         sig,
                         headers,
                         Some(body_id),
@@ -432,67 +465,7 @@
                     );
                 }
             },
-            hir::ItemKind::Impl(impl_) => {
-                self.in_trait_impl = impl_.of_trait.is_some();
-            },
-            hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
-                (false, hir::Unsafety::Unsafe) => span_lint(
-                    cx,
-                    MISSING_SAFETY_DOC,
-                    cx.tcx.def_span(item.owner_id),
-                    "docs for unsafe trait missing `# Safety` section",
-                ),
-                (true, hir::Unsafety::Normal) => span_lint(
-                    cx,
-                    UNNECESSARY_SAFETY_DOC,
-                    cx.tcx.def_span(item.owner_id),
-                    "docs for safe trait have unnecessary `# Safety` section",
-                ),
-                _ => (),
-            },
-            _ => (),
-        }
-    }
-
-    fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if let hir::ItemKind::Impl { .. } = item.kind {
-            self.in_trait_impl = false;
-        }
-    }
-
-    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
-        let attrs = cx.tcx.hir().attrs(item.hir_id());
-        let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
-            return;
-        };
-        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
-            if !in_external_macro(cx.tcx.sess, item.span) {
-                missing_headers::check(cx, item.owner_id, sig, headers, None, None, self.check_private_items);
-            }
-        }
-    }
-
-    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
-        let attrs = cx.tcx.hir().attrs(item.hir_id());
-        let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
-            return;
-        };
-        if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
-            return;
-        }
-        if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
-            let body = cx.tcx.hir().body(body_id);
-
-            let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
-            missing_headers::check(
-                cx,
-                item.owner_id,
-                sig,
-                headers,
-                Some(body_id),
-                panic_span,
-                self.check_private_items,
-            );
+            _ => {},
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 850a4f0..306a4a9 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -5,7 +5,7 @@
 use clippy_utils::usage::{local_used_after_expr, local_used_in};
 use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id};
 use rustc_errors::Applicability;
-use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, TyKind, Unsafety};
+use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, TyKind, Unsafety};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{
@@ -229,7 +229,7 @@
         && params.iter().zip(self_arg.into_iter().chain(args)).all(|(p, arg)| {
             matches!(
                 p.pat.kind,
-                PatKind::Binding(BindingAnnotation::NONE, id, _, None)
+                PatKind::Binding(BindingMode::NONE, id, _, None)
                 if path_to_local_id(arg, id)
             )
             // Only allow adjustments which change regions (i.e. re-borrowing).
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index 33bd5a5..724e184 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -4,7 +4,7 @@
 use clippy_utils::{is_expn_of, path_def_id};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
+use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::{sym, ExpnId};
@@ -114,7 +114,7 @@
         && let Node::Pat(res_pat) = cx.tcx.hir_node(expr_res)
 
         // Find id of the local we found in the block
-        && let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind
+        && let PatKind::Binding(BindingMode::NONE, local_hir_id, _ident, None) = local.pat.kind
 
         // If those two are the same hir id
         && res_pat.hir_id == local_hir_id
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
index 6ddc834..a4c3b06 100644
--- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -7,8 +7,8 @@
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::HirId;
 use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::HirId;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty;
@@ -94,7 +94,7 @@
     pat.walk_always(|pat| {
         // We'll just ignore mut and ref mut for simplicity sake right now
         if let hir::PatKind::Binding(
-            hir::BindingAnnotation(by_ref, hir::Mutability::Not),
+            hir::BindingMode(by_ref, hir::Mutability::Not),
             value_hir_id,
             ident,
             sub_pat,
diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
index 6615122..33764d3 100644
--- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
+++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
@@ -5,6 +5,7 @@
 use clippy_utils::macros::span_is_local;
 use clippy_utils::source::is_present_in_source;
 use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -147,6 +148,7 @@
     struct_threshold: u64,
     avoid_breaking_exported_api: bool,
     allow_private_module_inception: bool,
+    allowed_prefixes: FxHashSet<String>,
 }
 
 impl ItemNameRepetitions {
@@ -156,6 +158,7 @@
         struct_threshold: u64,
         avoid_breaking_exported_api: bool,
         allow_private_module_inception: bool,
+        allowed_prefixes: &[String],
     ) -> Self {
         Self {
             modules: Vec::new(),
@@ -163,8 +166,13 @@
             struct_threshold,
             avoid_breaking_exported_api,
             allow_private_module_inception,
+            allowed_prefixes: allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(),
         }
     }
+
+    fn is_allowed_prefix(&self, prefix: &str) -> bool {
+        self.allowed_prefixes.contains(prefix)
+    }
 }
 
 impl_lint_pass!(ItemNameRepetitions => [
@@ -423,7 +431,9 @@
                                 _ => (),
                             }
                         }
-                        if rmatching.char_count == nchars {
+                        if rmatching.char_count == nchars
+                            && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count])
+                        {
                             span_lint(
                                 cx,
                                 MODULE_NAME_REPETITIONS,
diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
index c5f1afe..00124dc 100644
--- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
@@ -17,7 +17,7 @@
     /// `std::<float>::EPSILON`, etc.
     ///
     /// ### Why is this bad?
-    /// All of these have been superceded by the associated constants on their respective types,
+    /// All of these have been superseded by the associated constants on their respective types,
     /// such as `i128::MAX`. These legacy items may be deprecated in a future version of rust.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
index d4ddf76..a65cb3f 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -4,7 +4,7 @@
 use clippy_utils::visitors::is_local_used;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::{BindingAnnotation, Mutability};
+use rustc_hir::{BindingMode, Mutability};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -106,7 +106,7 @@
                 };
 
                 let mutability = match mode {
-                    BindingAnnotation(_, Mutability::Mut) => "<mut> ",
+                    BindingMode(_, Mutability::Mut) => "<mut> ",
                     _ => "",
                 };
 
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index b92364a..e2aac58 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -594,6 +594,7 @@
         pub_underscore_fields_behavior,
         ref allowed_duplicate_crates,
         allow_comparison_to_zero,
+        ref allowed_prefixes,
 
         blacklisted_names: _,
         cyclomatic_complexity_threshold: _,
@@ -864,6 +865,7 @@
             struct_field_name_threshold,
             avoid_breaking_exported_api,
             allow_private_module_inception,
+            allowed_prefixes,
         ))
     });
     store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 2bb63ec..443d618 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -291,10 +291,7 @@
                     }) => {
                         // expand `&'a T` to `&'a T`
                         //          ^^         ^^^
-                        let span = cx
-                            .sess()
-                            .source_map()
-                            .span_extend_while_whitespace(usage.ident.span);
+                        let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
 
                         (span, String::new())
                     },
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
index d484ce4..b27528c 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
@@ -7,7 +7,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
 use rustc_lint::LateContext;
 use rustc_span::Span;
 
@@ -107,7 +107,7 @@
             hir_id = None;
             return;
         }
-        if let BindingAnnotation::NONE = annotation {
+        if let BindingMode::NONE = annotation {
             hir_id = Some(id);
         }
     });
diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
index 9433000..5047092 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
@@ -2,7 +2,7 @@
 use clippy_utils::diagnostics::span_lint_and_note;
 use clippy_utils::{get_enclosing_block, higher, path_to_local};
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
+use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Node, PatKind};
 use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
@@ -41,7 +41,7 @@
 fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
     if let Some(hir_id) = path_to_local(bound)
         && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
-        && let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind
+        && let PatKind::Binding(BindingMode::MUT, ..) = pat.kind
     {
         return Some(hir_id);
     }
diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
index 1d90d4a..9185cf1 100644
--- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
@@ -7,7 +7,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 use rustc_span::SyntaxContext;
@@ -61,7 +61,7 @@
                         let node = cx.tcx.hir_node(hir_id);
                         if let Node::Pat(pat) = node
                             && let PatKind::Binding(bind_ann, ..) = pat.kind
-                            && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut))
+                            && !matches!(bind_ann, BindingMode(_, Mutability::Mut))
                             && let Node::LetStmt(parent_let_expr) = cx.tcx.parent_hir_node(hir_id)
                             && let Some(init) = parent_let_expr.init
                         {
diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
index f8f33cf..daa8101 100644
--- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
@@ -4,7 +4,7 @@
 use clippy_utils::visitors::{is_local_used, local_used_once};
 use clippy_utils::{is_trait_method, path_to_local_id};
 use rustc_errors::Applicability;
-use rustc_hir::{BindingAnnotation, ExprKind, LetStmt, Node, PatKind, StmtKind};
+use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::sym;
@@ -62,7 +62,7 @@
 impl LateLintPass<'_> for ManualHashOne {
     fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) {
         // `let mut hasher = seg.build_hasher();`
-        if let PatKind::Binding(BindingAnnotation::MUT, hasher, _, None) = local.pat.kind
+        if let PatKind::Binding(BindingMode::MUT, hasher, _, None) = local.pat.kind
             && let Some(init) = local.init
             && !init.span.from_expansion()
             && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind
diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs
index c562ceb..84fb183 100644
--- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs
@@ -1,13 +1,14 @@
-use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::ty::GenericArgKind;
 use rustc_session::declare_lint_pass;
 use rustc_span::sym;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::higher::IfLetOrMatch;
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment};
 
@@ -105,19 +106,39 @@
     }
 }
 
-fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    let ExprKind::Match(match_expr, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) = expr.kind else {
-        return false;
+#[allow(clippy::needless_pass_by_value)]
+fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, expr: &'tcx Expr<'tcx>) {
+    // Get expr_name ("if let" or "match" depending on kind of expression),  the condition, the body for
+    // the some arm, the body for the none arm and the binding id of the some arm
+    let (expr_name, condition, body_some, body_none, binding_id) = match if_let_or_match {
+        IfLetOrMatch::Match(condition, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar)
+            // Make sure there are no guards to keep things simple
+            if arm1.guard.is_none()
+                && arm2.guard.is_none()
+                // Get the some and none bodies and the binding id of the some arm
+                && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) =>
+        {
+            ("match", condition, body_some, body_none, binding_id)
+        },
+        IfLetOrMatch::IfLet(condition, pat, if_expr, Some(else_expr), _)
+            if let Some(binding_id) = get_some(cx, pat) =>
+        {
+            ("if let", condition, if_expr, else_expr, binding_id)
+        },
+        _ => {
+            // All other cases (match with number of arms != 2, if let without else, etc.)
+            return;
+        },
     };
-    // We don't want conditions on the arms to simplify things.
-    if arm1.guard.is_none()
-        && arm2.guard.is_none()
-        // We check that the returned type implements the `Default` trait.
-        && let match_ty = cx.typeck_results().expr_ty(expr)
-        && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
-        && implements_trait(cx, match_ty, default_trait_id, &[])
-        // We now get the bodies for both the `Some` and `None` arms.
-        && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2)
+
+    // We check if the return type of the expression implements Default.
+    let expr_type = cx.typeck_results().expr_ty(expr);
+    if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
+        && implements_trait(cx, expr_type, default_trait_id, &[])
+        // We check if the initial condition implements Default.
+        && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1)
+        && let GenericArgKind::Type(condition_ty) = condition_ty.unpack()
+        && implements_trait(cx, condition_ty, default_trait_id, &[])
         // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`.
         && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind
         && let Res::Local(local_id) = path.res
@@ -125,8 +146,9 @@
         // We now check the `None` arm is calling a method equivalent to `Default::default`.
         && let body_none = peel_blocks(body_none)
         && is_default_equivalent(cx, body_none)
-        && let Some(receiver) = Sugg::hir_opt(cx, match_expr).map(Sugg::maybe_par)
+        && let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_par)
     {
+        // Machine applicable only if there are no comments present
         let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) {
             Applicability::MaybeIncorrect
         } else {
@@ -136,48 +158,12 @@
             cx,
             MANUAL_UNWRAP_OR_DEFAULT,
             expr.span,
-            "match can be simplified with `.unwrap_or_default()`",
+            format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
             "replace it with",
             format!("{receiver}.unwrap_or_default()"),
             applicability,
         );
     }
-    true
-}
-
-fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-    if let ExprKind::If(cond, if_block, Some(else_expr)) = expr.kind
-        && let ExprKind::Let(let_) = cond.kind
-        && let ExprKind::Block(_, _) = else_expr.kind
-        // We check that the returned type implements the `Default` trait.
-        && let match_ty = cx.typeck_results().expr_ty(expr)
-        && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
-        && implements_trait(cx, match_ty, default_trait_id, &[])
-        && let Some(binding_id) = get_some(cx, let_.pat)
-        // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`.
-        && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(if_block).kind
-        && let Res::Local(local_id) = path.res
-        && local_id == binding_id
-        // We now check the `None` arm is calling a method equivalent to `Default::default`.
-        && let body_else = peel_blocks(else_expr)
-        && is_default_equivalent(cx, body_else)
-        && let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span)
-    {
-        let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) {
-            Applicability::MaybeIncorrect
-        } else {
-            Applicability::MachineApplicable
-        };
-        span_lint_and_sugg(
-            cx,
-            MANUAL_UNWRAP_OR_DEFAULT,
-            expr.span,
-            "if let can be simplified with `.unwrap_or_default()`",
-            "replace it with",
-            format!("{if_let_expr_snippet}.unwrap_or_default()"),
-            applicability,
-        );
-    }
 }
 
 impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
@@ -185,8 +171,9 @@
         if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
             return;
         }
-        if !handle_match(cx, expr) {
-            handle_if_let(cx, expr);
+        // Call handle only if the expression is `if let` or `match`
+        if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) {
+            handle(cx, if_let_or_match, expr);
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
index 152aba9..183caab 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -11,7 +11,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
+use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
 use rustc_lint::LateContext;
 use rustc_span::{sym, SyntaxContext};
 
@@ -139,7 +139,7 @@
             }
 
             // `ref` and `ref mut` annotations were handled earlier.
-            let annotation = if matches!(annotation, BindingAnnotation::MUT) {
+            let annotation = if matches!(annotation, BindingMode::MUT) {
                 "mut "
             } else {
                 ""
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs
index f5da8ec..6c12364 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs
@@ -2,7 +2,7 @@
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks};
 use rustc_errors::Applicability;
-use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
+use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 
@@ -67,7 +67,7 @@
 fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> {
     if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind
         && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome)
-        && let PatKind::Binding(BindingAnnotation(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind
+        && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind
         && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind
         && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome)
         && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind
diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs
index fe83e78..6f7d690 100644
--- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs
@@ -8,7 +8,7 @@
 };
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath};
+use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath};
 use rustc_lint::LateContext;
 use rustc_span::sym;
 
@@ -178,7 +178,7 @@
                 },
             )),
         ) => {
-            return !matches!(annot, BindingAnnotation(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name;
+            return !matches!(annot, BindingMode(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name;
         },
         // Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
         (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index a0db8e2..37f7252 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -4,7 +4,7 @@
 use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
 use core::cmp::max;
 use rustc_errors::Applicability;
-use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
+use rustc_hir::{Arm, BindingMode, Block, Expr, ExprKind, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::{sym, Span};
@@ -166,7 +166,7 @@
             let p_ty = cx.typeck_results().pat_ty(p);
             collect_pat_paths(acc, cx, p, p_ty);
         }),
-        PatKind::TupleStruct(..) | PatKind::Binding(BindingAnnotation::NONE, .., None) | PatKind::Path(_) => {
+        PatKind::TupleStruct(..) | PatKind::Binding(BindingMode::NONE, .., None) | PatKind::Path(_) => {
             acc.push(ty);
         },
         _ => {},
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
index 4e6823e..e7a2060 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
@@ -2,7 +2,7 @@
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_copy;
 use rustc_errors::Applicability;
-use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath};
+use rustc_hir::{BindingMode, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
@@ -69,7 +69,7 @@
                 _ => false,
             },
             // local binding capturing a reference
-            Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes(_), _), ..)) => {
+            Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..)) => {
                 return;
             },
             _ => false,
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
index 7339362..e697ba6 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::implements_trait;
-use rustc_ast::{BindingAnnotation, Mutability};
+use rustc_ast::{BindingMode, Mutability};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -45,7 +45,7 @@
             span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| {
                 let (applicability, pat) = if let Some(id) = path_to_local(recv)
                     && let hir::Node::Pat(pat) = cx.tcx.hir_node(id)
-                    && let hir::PatKind::Binding(BindingAnnotation(_, Mutability::Not), _, ident, _) = pat.kind
+                    && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind
                 {
                     (Applicability::Unspecified, Some((pat.span, ident)))
                 } else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index b9fec0c..7c852a3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -6,7 +6,7 @@
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{pat_is_wild, sugg};
-use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind};
+use rustc_hir::{BindingMode, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty;
 use rustc_span::{sym, Span};
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
index 4729481..6d70989 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_copy};
-use rustc_ast::BindingAnnotation;
+use rustc_ast::BindingMode;
 use rustc_errors::Applicability;
 use rustc_hir::{Body, Expr, ExprKind, HirId, HirIdSet, PatKind};
 use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
@@ -89,7 +89,7 @@
                 }
 
                 match it.kind {
-                    PatKind::Binding(BindingAnnotation(_, Mutability::Mut), _, _, _)
+                    PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _)
                     | PatKind::Ref(_, Mutability::Mut) => {
                         to_be_discarded = true;
                         false
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs
index d121529..fedb7c2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs
@@ -3,7 +3,7 @@
 use clippy_utils::{is_trait_method, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::{BindingAnnotation, Node, PatKind};
+use rustc_hir::{BindingMode, Node, PatKind};
 use rustc_lint::LateContext;
 use rustc_span::sym;
 
@@ -22,7 +22,7 @@
                 if let Some(id) = path_to_local(recv)
                     && let Node::Pat(pat) = cx.tcx.hir_node(id)
                     && let PatKind::Binding(ann, _, _, _) = pat.kind
-                    && ann != BindingAnnotation::MUT
+                    && ann != BindingMode::MUT
                 {
                     application = Applicability::Unspecified;
                     diag.span_help(
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
index 0901268..a5ba5e5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -51,13 +51,13 @@
                 let closure_expr = peel_blocks(closure_body.value);
                 match closure_body.params[0].pat.kind {
                     hir::PatKind::Ref(inner, Mutability::Not) => {
-                        if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind {
+                        if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind {
                             if ident_eq(name, closure_expr) {
                                 lint_explicit_closure(cx, e.span, recv.span, true, msrv);
                             }
                         }
                     },
-                    hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => {
+                    hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) => {
                         match closure_expr.kind {
                             hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
                                 if ident_eq(name, inner) {
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 2fb317c..0939c02 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -3938,7 +3938,6 @@
     /// This lint cannot detect if the split is intentionally restricted to a single type of newline (`"\n"` or
     /// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is
     /// valid.
-    /// ```
     #[clippy::version = "1.77.0"]
     pub STR_SPLIT_AT_NEWLINE,
     pedantic,
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index 662e774..1c69565 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -11,7 +11,7 @@
 use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
 use rustc_hir::{
-    BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind,
+    BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind,
 };
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
@@ -86,7 +86,7 @@
             }
         },
         Node::LetStmt(l) => {
-            if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind
+            if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind
                 && let ty = cx.typeck_results().expr_ty(collect_expr)
                 && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
                     .into_iter()
diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
index ac5cc2f..f5f1e94 100644
--- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
@@ -2,7 +2,8 @@
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::deref_closure_args;
 use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::{is_trait_method, strip_pat_refs};
+use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs};
+use hir::ExprKind;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::PatKind;
@@ -35,7 +36,7 @@
             // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
             let mut applicability = Applicability::MachineApplicable;
             let any_search_snippet = if search_method == "find"
-                && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind
+                && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind
                 && let closure_body = cx.tcx.hir().body(body)
                 && let Some(closure_arg) = closure_body.params.first()
             {
@@ -72,16 +73,24 @@
                 );
             } else {
                 let iter = snippet(cx, search_recv.span, "..");
+                let sugg = if is_receiver_of_method_call(cx, expr) {
+                    format!(
+                        "(!{iter}.any({}))",
+                        any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
+                    )
+                } else {
+                    format!(
+                        "!{iter}.any({})",
+                        any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
+                    )
+                };
                 span_lint_and_sugg(
                     cx,
                     SEARCH_IS_SOME,
                     expr.span,
                     msg,
                     "consider using",
-                    format!(
-                        "!{iter}.any({})",
-                        any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
-                    ),
+                    sugg,
                     applicability,
                 );
             }
@@ -127,13 +136,18 @@
                     let string = snippet(cx, search_recv.span, "..");
                     let mut applicability = Applicability::MachineApplicable;
                     let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability);
+                    let sugg = if is_receiver_of_method_call(cx, expr) {
+                        format!("(!{string}.contains({find_arg}))")
+                    } else {
+                        format!("!{string}.contains({find_arg})")
+                    };
                     span_lint_and_sugg(
                         cx,
                         SEARCH_IS_SOME,
                         expr.span,
                         msg,
                         "consider using",
-                        format!("!{string}.contains({find_arg})"),
+                        sugg,
                         applicability,
                     );
                 },
@@ -142,3 +156,13 @@
         }
     }
 }
+
+fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+    if let Some(parent_expr) = get_parent_expr(cx, expr)
+        && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
+        && receiver.hir_id == expr.hir_id
+    {
+        return true;
+    }
+    false
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 55ae974..e8c12bb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -8,7 +8,7 @@
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::{
-    BindingAnnotation, Expr, ExprKind, HirId, LangItem, LetStmt, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
+    BindingMode, Expr, ExprKind, HirId, LangItem, LetStmt, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
 };
 use rustc_lint::LateContext;
 use rustc_middle::ty;
@@ -129,7 +129,7 @@
     let ctxt = expr.span.ctxt();
     let mut parents = cx.tcx.hir().parent_iter(expr.hir_id);
     if let (_, Node::LetStmt(local)) = parents.next()?
-        && let PatKind::Binding(BindingAnnotation::MUT, iter_binding_id, iter_ident, None) = local.pat.kind
+        && let PatKind::Binding(BindingMode::MUT, iter_binding_id, iter_ident, None) = local.pat.kind
         && let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
         && let (_, Node::Block(enclosing_block)) = parents.next()?
         && let mut stmts = enclosing_block
@@ -200,7 +200,7 @@
 ) -> Option<IndirectUsage<'tcx>> {
     if let StmtKind::Let(&LetStmt {
         pat: Pat {
-            kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None),
+            kind: PatKind::Binding(BindingMode::NONE, _, ident, None),
             ..
         },
         init: Some(init_expr),
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index 3cf054e..f3f9bf1 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -9,7 +9,7 @@
 use rustc_hir::def::Res;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind,
+    BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -129,7 +129,7 @@
             if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) {
                 return;
             }
-            if let PatKind::Binding(BindingAnnotation(ByRef::Yes(_), _), ..) = arg.pat.kind {
+            if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind {
                 span_lint(
                     cx,
                     TOPLEVEL_REF_ARG,
@@ -144,7 +144,7 @@
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
         if !in_external_macro(cx.tcx.sess, stmt.span)
             && let StmtKind::Let(local) = stmt.kind
-            && let PatKind::Binding(BindingAnnotation(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind
+            && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind
             && let Some(init) = local.init
             // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue.
             && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id)
diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
index 2ab83f7..60c4438 100644
--- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use rustc_ast::ast::{BindingAnnotation, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind};
+use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
@@ -117,13 +117,13 @@
 
         match &p.ty.kind {
             TyKind::Path(None, path) => {
-                if let PatKind::Ident(BindingAnnotation(ByRef::No, mutbl), _, _) = p.pat.kind {
+                if let PatKind::Ident(BindingMode(ByRef::No, mutbl), _, _) = p.pat.kind {
                     check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl);
                 }
             },
             TyKind::Ref(lifetime, mut_ty) => {
                 if let TyKind::Path(None, path) = &mut_ty.ty.kind
-                    && let PatKind::Ident(BindingAnnotation::NONE, _, _) = p.pat.kind
+                    && let PatKind::Ident(BindingMode::NONE, _, _) = p.pat.kind
                 {
                     check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl);
                 }
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
index d91329e..fb02f24 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
-use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
+use rustc_hir::{BindingMode, Mutability, Node, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -58,7 +58,7 @@
 
         match pat.kind {
             // Check sub_pat got a `ref` keyword (excluding `ref mut`).
-            PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
+            PatKind::Binding(BindingMode::REF, _, ident, None) => {
                 span_lint_and_then(
                     cx,
                     NEEDLESS_BORROWED_REFERENCE,
@@ -128,7 +128,7 @@
 
     for subpattern in subpatterns {
         match subpattern.kind {
-            PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
+            PatKind::Binding(BindingMode::REF, _, ident, None) => {
                 // `ref ident`
                 //  ^^^^
                 let span = subpattern.span.until(ident.span);
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
index c555fc8..a24cd4f 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -23,7 +23,7 @@
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for borrow operations (`&`) that used as a generic argument to a
+    /// Checks for borrow operations (`&`) that are used as a generic argument to a
     /// function when the borrowed value could be used.
     ///
     /// ### Why is this bad?
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
index 810799a..0c0b1a7 100644
--- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
@@ -6,7 +6,7 @@
 use core::ops::ControlFlow;
 use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::{
-    BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
+    BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
     StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
@@ -369,7 +369,7 @@
             init: None,
             pat:
                 &Pat {
-                    kind: PatKind::Binding(BindingAnnotation::NONE, binding_id, _, None),
+                    kind: PatKind::Binding(BindingMode::NONE, binding_id, _, None),
                     ..
                 },
             source: LocalSource::Normal,
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index f33e2e0..53bcde6 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -9,7 +9,7 @@
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    BindingAnnotation, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind,
+    BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind,
     QPath, TyKind,
 };
 use rustc_hir_typeck::expr_use_visitor as euv;
@@ -192,7 +192,7 @@
                 })
                 && !implements_borrow_trait
                 && !all_borrowable_trait
-                && let PatKind::Binding(BindingAnnotation(_, Mutability::Not), canonical_id, ..) = arg.pat.kind
+                && let PatKind::Binding(BindingMode(_, Mutability::Not), canonical_id, ..) = arg.pat.kind
                 && !moved_vars.contains(&canonical_id)
             {
                 // Dereference suggestion
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 5ca388d..ff10a84 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -18,7 +18,7 @@
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, DUMMY_SP, InnerSpan, Span};
+use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
 use rustc_target::abi::VariantIdx;
 
 // FIXME: this is a correctness problem but there's no suitable
@@ -297,12 +297,7 @@
     fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
         let args = cx.typeck_results().node_args(hir_id);
 
-        let result = Self::const_eval_resolve(
-            cx.tcx,
-            cx.param_env,
-            ty::UnevaluatedConst::new(def_id, args),
-            DUMMY_SP,
-        );
+        let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP);
         self.is_value_unfrozen_raw(cx, result, ty)
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
index 3cbd03a..d490632 100644
--- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -7,7 +7,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
-use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp};
+use rustc_hir::{Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::SyntaxContext;
@@ -129,7 +129,7 @@
             .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
             .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref())
     {
-        let capture_mut = if bind_annotation == BindingAnnotation::MUT {
+        let capture_mut = if bind_annotation == BindingMode::MUT {
             "mut "
         } else {
             ""
@@ -149,8 +149,8 @@
                 (mutb == Mutability::Not, mutb == Mutability::Mut)
             },
             _ => (
-                bind_annotation == BindingAnnotation::REF,
-                bind_annotation == BindingAnnotation::REF_MUT,
+                bind_annotation == BindingMode::REF,
+                bind_annotation == BindingMode::REF_MUT,
             ),
         };
 
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index bb4a1de..128bfd4 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -10,7 +10,7 @@
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
-use rustc_hir::{BindingAnnotation, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
+use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
 use rustc_middle::ty::layout::LayoutOf;
@@ -221,7 +221,7 @@
                     // if function has a body and parameter is annotated with mut, ignore
                     if let Some(param) = fn_body.and_then(|body| body.params.get(index)) {
                         match param.pat.kind {
-                            PatKind::Binding(BindingAnnotation::NONE, _, _, _) => {},
+                            PatKind::Binding(BindingMode::NONE, _, _, _) => {},
                             _ => continue,
                         }
                     }
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index d659262..cc61ef9 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -11,7 +11,7 @@
 use rustc_hir::hir_id::{HirId, HirIdMap};
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{
-    self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
+    self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
     ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
     TyKind, Unsafety,
 };
@@ -606,7 +606,7 @@
                 Some((Node::Stmt(_), _)) => (),
                 Some((Node::LetStmt(l), _)) => {
                     // Only trace simple bindings. e.g `let x = y;`
-                    if let PatKind::Binding(BindingAnnotation::NONE, id, _, None) = l.pat.kind {
+                    if let PatKind::Binding(BindingMode::NONE, id, _, None) = l.pat.kind {
                         self.bindings.insert(id, args_idx);
                     } else {
                         set_skip_flag();
@@ -687,7 +687,7 @@
             .filter_map(|(i, arg)| {
                 let param = &body.params[arg.idx];
                 match param.pat.kind {
-                    PatKind::Binding(BindingAnnotation::NONE, id, _, None)
+                    PatKind::Binding(BindingMode::NONE, id, _, None)
                         if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
                     {
                         Some((id, i))
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index 927c6f1..4ad9675 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -14,7 +14,7 @@
 use rustc_hir::def::Res;
 use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::{
-    BindingAnnotation, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath,
+    BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath,
     Stmt, StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
@@ -283,7 +283,7 @@
         && !is_else_clause(cx.tcx, expr)
         && let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind
         && ddpos.as_opt_usize().is_none()
-        && let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind
+        && let PatKind::Binding(BindingMode(by_ref, _), bind_id, ident, None) = field.kind
         && let caller_ty = cx.typeck_results().expr_ty(let_expr)
         && let if_block = IfBlockType::IfLet(
             cx.qpath_res(path1, let_pat.hir_id),
diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
index 7202266..d94ca5b 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
@@ -3,7 +3,7 @@
 use clippy_utils::ty::needs_ordered_drop;
 use rustc_ast::Mutability;
 use rustc_hir::def::Res;
-use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, LetStmt, Node, Pat, PatKind, QPath};
+use rustc_hir::{BindingMode, ByRef, ExprKind, HirId, LetStmt, Node, Pat, PatKind, QPath};
 use rustc_hir_typeck::expr_use_visitor::PlaceBase;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -50,7 +50,7 @@
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
         if !local.span.is_desugaring(DesugaringKind::Async)
             // the pattern is a single by-value binding
-            && let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind
+            && let PatKind::Binding(BindingMode(ByRef::No, mutability), _, ident, None) = local.pat.kind
             // the binding is not type-ascribed
             && local.ty.is_none()
             // the expression is a resolved path
@@ -109,7 +109,7 @@
 }
 
 /// Find the annotation of a binding introduced by a pattern, or `None` if it's not introduced.
-fn find_binding(pat: &Pat<'_>, name: Ident) -> Option<BindingAnnotation> {
+fn find_binding(pat: &Pat<'_>, name: Ident) -> Option<BindingMode> {
     let mut ret = None;
 
     pat.each_binding_or_first(&mut |annotation, _, _, ident| {
diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs
index a4be78b..607a074 100644
--- a/src/tools/clippy/clippy_lints/src/ref_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use rustc_ast::ast::{BindingAnnotation, Pat, PatKind};
+use rustc_ast::ast::{BindingMode, Pat, PatKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -28,7 +28,7 @@
 
 impl EarlyLintPass for RefPatterns {
     fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
-        if let PatKind::Ident(BindingAnnotation::REF, _, _) = pat.kind
+        if let PatKind::Ident(BindingMode::REF, _, _) = pat.kind
             && !pat.span.from_expansion()
         {
             span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
index c227b5b..caf3fb8 100644
--- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
@@ -4,7 +4,7 @@
 use clippy_utils::{is_from_proc_macro, path_to_local_id};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind};
+use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
@@ -71,7 +71,7 @@
 
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
         if let Some(init_expr) = local.init
-            && let PatKind::Binding(BindingAnnotation::MUT, id, _, None) = local.pat.kind
+            && let PatKind::Binding(BindingMode::MUT, id, _, None) = local.pat.kind
             && !in_external_macro(cx.sess(), local.span)
             && let Some(init) = get_vec_init_kind(cx, init_expr)
             && !matches!(
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index 8a9f02b..28c2545 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -7,7 +7,7 @@
 };
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind};
+use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
@@ -120,7 +120,7 @@
         // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
         // or `Vec::new()`
         if let StmtKind::Let(local) = stmt.kind
-            && let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind
+            && let PatKind::Binding(BindingMode::MUT, local_id, _, None) = local.pat.kind
             && let Some(init) = local.init
             && let Some(size_expr) = Self::as_vec_initializer(cx, init)
         {
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 3aa979c..87a3c38 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -300,11 +300,8 @@
                     e.span,
                     "calling `as_bytes()` on `include_str!(..)`",
                     "consider using `include_bytes!(..)` instead",
-                    snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability).replacen(
-                        "include_str",
-                        "include_bytes",
-                        1,
-                    ),
+                    snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability)
+                        .replacen("include_str", "include_bytes", 1),
                     applicability,
                 );
             } else if lit_content.as_str().is_ascii()
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
index 234021f..2bea3be 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
@@ -27,7 +27,7 @@
     };
 
     // FIXME: This can be simplified once `NonZero<T>` is stable.
-    let coercable_types = [
+    let coercible_types = [
         ("NonZeroU8", tcx.types.u8),
         ("NonZeroU16", tcx.types.u16),
         ("NonZeroU32", tcx.types.u32),
@@ -44,7 +44,7 @@
 
     let int_type = substs.type_at(0);
 
-    let Some(nonzero_alias) = coercable_types.iter().find_map(|(nonzero_alias, t)| {
+    let Some(nonzero_alias) = coercible_types.iter().find_map(|(nonzero_alias, t)| {
         if *t == int_type && *t == from_ty {
             Some(nonzero_alias)
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index a6a6e9a..ba8c7d6 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -1,10 +1,10 @@
-use rustc_hir_typeck::cast::check_cast;
 use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
 use rustc_ast::ExprPrecedence;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Node};
+use rustc_hir_typeck::cast::check_cast;
 use rustc_lint::LateContext;
 use rustc_middle::ty::cast::CastKind;
 use rustc_middle::ty::Ty;
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
index 333ea0c..2da7533 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
@@ -2,7 +2,7 @@
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_copy;
 use clippy_utils::{get_parent_expr, path_to_local};
-use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp};
+use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -84,7 +84,7 @@
     if let Some(hir_id) = path_to_local(expr)
         && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
     {
-        matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..))
+        matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
     } else {
         true
     }
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index 0049de9..fcc41b5 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -137,12 +137,12 @@
     struct Visitor;
     impl MutVisitor for Visitor {
         fn visit_pat(&mut self, pat: &mut P<Pat>) {
-            use ast::BindingAnnotation;
+            use ast::BindingMode;
             noop_visit_pat(pat, self);
             let target = match &mut pat.kind {
                 // `i @ a | b`, `box a | b`, and `& mut? a | b`.
                 Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
-                Ref(p, Mutability::Not) if matches!(p.kind, Ident(BindingAnnotation::MUT, ..)) => p, // `&(mut x)`
+                Ref(p, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, // `&(mut x)`
                 _ => return,
             };
             target.kind = Paren(P(take_pat(target)));
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index 7554176..2f7d54e 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -5,7 +5,7 @@
 use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
+use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::Obligation;
 use rustc_lint::{LateContext, LateLintPass};
@@ -282,7 +282,7 @@
                     if let Some(id) = path_to_local(recv)
                         && let Node::Pat(pat) = cx.tcx.hir_node(id)
                         && let PatKind::Binding(ann, ..) = pat.kind
-                        && ann != BindingAnnotation::MUT
+                        && ann != BindingMode::MUT
                     {
                         // Do not remove .into_iter() applied to a non-mutable local variable used in
                         // a larger expression context as it would differ in mutability.
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 7b43abe..7f07694 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -7,7 +7,7 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::{
-    ArrayLen, BindingAnnotation, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit,
+    ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit,
     PatKind, QPath, StmtKind, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -645,14 +645,14 @@
                 bind!(self, name);
                 opt_bind!(self, sub);
                 let ann = match ann {
-                    BindingAnnotation::NONE => "NONE",
-                    BindingAnnotation::REF => "REF",
-                    BindingAnnotation::MUT => "MUT",
-                    BindingAnnotation::REF_MUT => "REF_MUT",
-                    BindingAnnotation::MUT_REF => "MUT_REF",
-                    BindingAnnotation::MUT_REF_MUT => "MUT_REF_MUT",
+                    BindingMode::NONE => "NONE",
+                    BindingMode::REF => "REF",
+                    BindingMode::MUT => "MUT",
+                    BindingMode::REF_MUT => "REF_MUT",
+                    BindingMode::MUT_REF => "MUT_REF",
+                    BindingMode::MUT_REF_MUT => "MUT_REF_MUT",
                 };
-                kind!("Binding(BindingAnnotation::{ann}, _, {name}, {sub})");
+                kind!("Binding(BindingMode::{ann}, _, {name}, {sub})");
                 self.ident(name);
                 sub.if_some(|p| self.pat(p));
             },
diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
index b58a4fb..c46f029 100644
--- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
@@ -7,7 +7,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
-    BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp,
+    BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -159,7 +159,7 @@
 
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
         if let Some(init_expr) = local.init
-            && let PatKind::Binding(BindingAnnotation::MUT, id, name, None) = local.pat.kind
+            && let PatKind::Binding(BindingMode::MUT, id, name, None) = local.pat.kind
             && !in_external_macro(cx.sess(), local.span)
             && let Some(init) = get_vec_init_kind(cx, init_expr)
             && !matches!(init, VecInitKind::WithExprCapacity(_))
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 9f0bd4e..0395eb1 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -709,8 +709,12 @@
         (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
         (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
         (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
-        (ImplTrait(_, lg, lc), ImplTrait(_, rg, rc)) =>
-            over(lg, rg, eq_generic_bound) && both(lc, rc, |lc, rc| over(lc.0.as_slice(), rc.0.as_slice(), eq_precise_capture)),
+        (ImplTrait(_, lg, lc), ImplTrait(_, rg, rc)) => {
+            over(lg, rg, eq_generic_bound)
+                && both(lc, rc, |lc, rc| {
+                    over(lc.0.as_slice(), rc.0.as_slice(), eq_precise_capture)
+                })
+        },
         (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
         _ => false,
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 6c3d932..07c443a 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -7,7 +7,7 @@
 use rustc_hir::def::Res;
 use rustc_hir::MatchSource::TryDesugar;
 use rustc_hir::{
-    ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
+    ArrayLen, BinOpKind, BindingMode, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
     GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, PatKind, Path,
     PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
 };
@@ -947,7 +947,7 @@
     pub fn hash_pat(&mut self, pat: &Pat<'_>) {
         std::mem::discriminant(&pat.kind).hash(&mut self.s);
         match pat.kind {
-            PatKind::Binding(BindingAnnotation(by_ref, mutability), _, _, pat) => {
+            PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => {
                 std::mem::discriminant(&by_ref).hash(&mut self.s);
                 std::mem::discriminant(&mutability).hash(&mut self.s);
                 if let Some(pat) = pat {
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 37c12dd..5e242ae 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -99,7 +99,7 @@
 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::{
-    self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, ByRef, Closure, Destination, Expr,
+    self as hir, def, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, Destination, Expr,
     ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item,
     ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment,
     PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
@@ -184,7 +184,7 @@
 /// canonical binding `HirId`.
 pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
     if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
-        && matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..))
+        && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
         && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
     {
         return local.init;
@@ -3285,7 +3285,7 @@
             Right(r) => Right(r.data),
         });
 
-    // 2. for the remaning segments, construct relative path using only mod names and `super`
+    // 2. for the remaining segments, construct relative path using only mod names and `super`
     let mut go_up_by = 0;
     let mut path = Vec::new();
     for el in unique_parts {
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 1afc5ed..a06a82c 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -10,7 +10,7 @@
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
-use rustc_infer::infer::type_variable::{TypeVariableOrigin};
+use rustc_infer::infer::type_variable::TypeVariableOrigin;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::Scalar;
diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
index 762830f..2241494 100644
--- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
@@ -90,7 +90,7 @@
     if let Some(def_id) = adt_def_id(expr_ty) {
         certainty.with_def_id(def_id)
     } else {
-        certainty
+        certainty.clear_def_id()
     }
 }
 
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index 4251151..deb0609 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -26,12 +26,12 @@
 use std::fmt::{self, Write as _};
 use std::io::{self, ErrorKind};
 use std::path::{Path, PathBuf};
-use std::process::Command;
+use std::process::{Command, ExitStatus};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::time::Duration;
 use std::{env, fs, thread};
 
-use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel};
+use cargo_metadata::diagnostic::Diagnostic;
 use cargo_metadata::Message;
 use rayon::prelude::*;
 use serde::{Deserialize, Serialize};
@@ -97,16 +97,43 @@
     options: Option<Vec<String>>,
 }
 
+/// A single emitted output from clippy being executed on a crate. It may either be a
+/// `ClippyWarning`, or a `RustcIce` caused by a panic within clippy. A crate may have many
+/// `ClippyWarning`s but a maximum of one `RustcIce` (at which point clippy halts execution).
+#[derive(Debug)]
+enum ClippyCheckOutput {
+    ClippyWarning(ClippyWarning),
+    RustcIce(RustcIce),
+}
+
+#[derive(Debug)]
+struct RustcIce {
+    pub crate_name: String,
+    pub ice_content: String,
+}
+impl RustcIce {
+    pub fn from_stderr_and_status(crate_name: &str, status: ExitStatus, stderr: &str) -> Option<Self> {
+        if status.code().unwrap_or(0) == 101
+        /* ice exit status */
+        {
+            Some(Self {
+                crate_name: crate_name.to_owned(),
+                ice_content: stderr.to_owned(),
+            })
+        } else {
+            None
+        }
+    }
+}
+
 /// A single warning that clippy issued while checking a `Crate`
 #[derive(Debug)]
 struct ClippyWarning {
-    crate_name: String,
     file: String,
     line: usize,
     column: usize,
     lint_type: String,
     message: String,
-    is_ice: bool,
 }
 
 #[allow(unused)]
@@ -131,13 +158,11 @@
         };
 
         Some(Self {
-            crate_name: crate_name.to_owned(),
             file,
             line: span.line_start,
             column: span.column_start,
             lint_type,
             message: diag.message,
-            is_ice: diag.level == DiagnosticLevel::Ice,
         })
     }
 
@@ -318,7 +343,7 @@
         config: &LintcheckConfig,
         lint_filter: &[String],
         server: &Option<LintcheckServer>,
-    ) -> Vec<ClippyWarning> {
+    ) -> Vec<ClippyCheckOutput> {
         // advance the atomic index by one
         let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
         // "loop" the index within 0..thread_limit
@@ -342,9 +367,9 @@
         let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
 
         let mut cargo_clippy_args = if config.fix {
-            vec!["--fix", "--"]
+            vec!["--quiet", "--fix", "--"]
         } else {
-            vec!["--", "--message-format=json", "--"]
+            vec!["--quiet", "--message-format=json", "--"]
         };
 
         let mut clippy_args = Vec::<&str>::new();
@@ -435,14 +460,21 @@
         }
 
         // get all clippy warnings and ICEs
-        let warnings: Vec<ClippyWarning> = Message::parse_stream(stdout.as_bytes())
+        let mut entries: Vec<ClippyCheckOutput> = Message::parse_stream(stdout.as_bytes())
             .filter_map(|msg| match msg {
                 Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version),
                 _ => None,
             })
+            .map(ClippyCheckOutput::ClippyWarning)
             .collect();
 
-        warnings
+        if let Some(ice) = RustcIce::from_stderr_and_status(&self.name, *status, &stderr) {
+            entries.push(ClippyCheckOutput::RustcIce(ice));
+        } else if !status.success() {
+            println!("non-ICE bad exit status for {} {}: {}", self.name, self.version, stderr);
+        }
+
+        entries
     }
 }
 
@@ -642,7 +674,7 @@
         LintcheckServer::spawn(recursive_options)
     });
 
-    let mut clippy_warnings: Vec<ClippyWarning> = crates
+    let mut clippy_entries: Vec<ClippyCheckOutput> = crates
         .par_iter()
         .flat_map(|krate| {
             krate.run_clippy_lints(
@@ -658,7 +690,9 @@
         .collect();
 
     if let Some(server) = server {
-        clippy_warnings.extend(server.warnings());
+        let server_clippy_entries = server.warnings().map(ClippyCheckOutput::ClippyWarning);
+
+        clippy_entries.extend(server_clippy_entries);
     }
 
     // if we are in --fix mode, don't change the log files, terminate here
@@ -666,20 +700,21 @@
         return;
     }
 
+    // split up warnings and ices
+    let mut warnings: Vec<ClippyWarning> = vec![];
+    let mut raw_ices: Vec<RustcIce> = vec![];
+    for entry in clippy_entries {
+        if let ClippyCheckOutput::ClippyWarning(x) = entry {
+            warnings.push(x);
+        } else if let ClippyCheckOutput::RustcIce(x) = entry {
+            raw_ices.push(x);
+        }
+    }
+
     // generate some stats
-    let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
+    let (stats_formatted, new_stats) = gather_stats(&warnings);
 
-    // grab crashes/ICEs, save the crate name and the ice message
-    let ices: Vec<(&String, &String)> = clippy_warnings
-        .iter()
-        .filter(|warning| warning.is_ice)
-        .map(|w| (&w.crate_name, &w.message))
-        .collect();
-
-    let mut all_msgs: Vec<String> = clippy_warnings
-        .iter()
-        .map(|warn| warn.to_output(config.markdown))
-        .collect();
+    let mut all_msgs: Vec<String> = warnings.iter().map(|warn| warn.to_output(config.markdown)).collect();
     all_msgs.sort();
     all_msgs.push("\n\n### Stats:\n\n".into());
     all_msgs.push(stats_formatted);
@@ -693,11 +728,18 @@
     }
     write!(text, "{}", all_msgs.join("")).unwrap();
     text.push_str("\n\n### ICEs:\n");
-    for (cratename, msg) in &ices {
-        let _: fmt::Result = write!(text, "{cratename}: '{msg}'");
+    for ice in &raw_ices {
+        let _: fmt::Result = write!(
+            text,
+            "{}:\n{}\n========================================\n\n",
+            ice.crate_name, ice.ice_content
+        );
     }
 
     println!("Writing logs to {}", config.lintcheck_results_path.display());
+    if !raw_ices.is_empty() {
+        println!("WARNING: at least one ICE reported, check log file");
+    }
     fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
     fs::write(&config.lintcheck_results_path, text).unwrap();
 
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index b2fe5c8..521c0d1 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-04-04"
+channel = "nightly-2024-04-18"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
index 400bfd5..9b0db66 100644
--- a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
+++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
@@ -5,7 +5,6 @@
 //@normalize-stderr-test: "'rustc'" -> "'<unnamed>'"
 //@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc <version> running on <target>"
 //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> ""
-//@normalize-stderr-test: "this compiler `.*` is outdated" -> "this compiler <version> is outdated"
 
 #![deny(clippy::internal)]
 #![allow(clippy::missing_clippy_version_attribute)]
diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
index 763ce59..b99e8c0 100644
--- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
+++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
@@ -4,10 +4,9 @@
 
 error: the compiler unexpectedly panicked. this is a bug.
 
-note: it seems that this compiler <version> is outdated, a newer nightly should have been released in the meantime
-  |
-  = note: please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists
-  = note: if the problem still persists, we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml
+note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml
+
+note: please make sure that you have updated to the latest nightly
 
 note: rustc <version> running on <target>
 
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/clippy.toml b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/clippy.toml
new file mode 100644
index 0000000..3514555
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/clippy.toml
@@ -0,0 +1 @@
+allowed-prefixes = ["bar"]
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs
new file mode 100644
index 0000000..4142ced
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs
@@ -0,0 +1,15 @@
+#![warn(clippy::module_name_repetitions)]
+#![allow(dead_code)]
+
+mod foo {
+    // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name.
+    // In this test, allowed prefixes are configured to be ["bar"].
+
+    // this line should produce a warning:
+    pub fn to_foo() {}
+
+    // but this line shouldn't
+    pub fn bar_foo() {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.stderr b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.stderr
new file mode 100644
index 0000000..6cfe0ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.stderr
@@ -0,0 +1,11 @@
+error: item name ends with its containing module's name
+  --> tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs:9:12
+   |
+LL |     pub fn to_foo() {}
+   |            ^^^^^^
+   |
+   = note: `-D clippy::module-name-repetitions` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/clippy.toml b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/clippy.toml
new file mode 100644
index 0000000..31ef524
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/clippy.toml
@@ -0,0 +1 @@
+allowed-prefixes = ["..", "bar"]
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs
new file mode 100644
index 0000000..b132305
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::module_name_repetitions)]
+#![allow(dead_code)]
+
+mod foo {
+    // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name.
+    // In this test, allowed prefixes are configured to be all of the default prefixes and ["bar"].
+
+    // this line should produce a warning:
+    pub fn something_foo() {}
+
+    // but none of the following should:
+    pub fn bar_foo() {}
+    pub fn to_foo() {}
+    pub fn as_foo() {}
+    pub fn into_foo() {}
+    pub fn from_foo() {}
+    pub fn try_into_foo() {}
+    pub fn try_from_foo() {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.stderr b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.stderr
new file mode 100644
index 0000000..f495ec4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.stderr
@@ -0,0 +1,11 @@
+error: item name ends with its containing module's name
+  --> tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs:9:12
+   |
+LL |     pub fn something_foo() {}
+   |            ^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::module-name-repetitions` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 737c062..24645b6 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -14,6 +14,7 @@
            allowed-dotfiles
            allowed-duplicate-crates
            allowed-idents-below-min-chars
+           allowed-prefixes
            allowed-scripts
            allowed-wildcard-imports
            arithmetic-side-effects-allowed
@@ -93,6 +94,7 @@
            allowed-dotfiles
            allowed-duplicate-crates
            allowed-idents-below-min-chars
+           allowed-prefixes
            allowed-scripts
            allowed-wildcard-imports
            arithmetic-side-effects-allowed
@@ -172,6 +174,7 @@
            allowed-dotfiles
            allowed-duplicate-crates
            allowed-idents-below-min-chars
+           allowed-prefixes
            allowed-scripts
            allowed-wildcard-imports
            arithmetic-side-effects-allowed
diff --git a/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs b/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs
index 349e819..c287480 100644
--- a/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs
+++ b/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs
@@ -33,16 +33,9 @@
     let _ = Arc::new(42);
 
     let _ = Arc::new(RefCell::new(42));
-    //~^ ERROR: usage of an `Arc` that is not `Send` and `Sync`
-    //~| NOTE: the trait `Sync` is not implemented for `RefCell<i32>`
 
     let mutex = Mutex::new(1);
     let _ = Arc::new(mutex.lock().unwrap());
-    //~^ ERROR: usage of an `Arc` that is not `Send` and `Sync`
-    //~| NOTE: the trait `Send` is not implemented for `MutexGuard<'_, i32>`
 
     let _ = Arc::new(&42 as *const i32);
-    //~^ ERROR: usage of an `Arc` that is not `Send` and `Sync`
-    //~| NOTE: the trait `Send` is not implemented for `*const i32`
-    //~| NOTE: the trait `Sync` is not implemented for `*const i32`
 }
diff --git a/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr b/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr
index d4e6303..da363a3 100644
--- a/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr
+++ b/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr
@@ -4,38 +4,31 @@
 LL |     let _ = Arc::new(RefCell::new(42));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `Arc<RefCell<i32>>` is not `Send` and `Sync` as:
-   = note: - the trait `Sync` is not implemented for `RefCell<i32>`
-   = help: consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types
-   = note: if you intend to use `Arc` with `Send` and `Sync` traits
-   = note: wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `RefCell<i32>`
+   = note: `Arc<RefCell<i32>>` is not `Send` and `Sync` as `RefCell<i32>` is not `Sync`
+   = help: if the `Arc` will not used be across threads replace it with an `Rc`
+   = help: otherwise make `RefCell<i32>` `Send` and `Sync` or consider a wrapper type such as `Mutex`
    = note: `-D clippy::arc-with-non-send-sync` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::arc_with_non_send_sync)]`
 
 error: usage of an `Arc` that is not `Send` and `Sync`
-  --> tests/ui/arc_with_non_send_sync.rs:40:13
+  --> tests/ui/arc_with_non_send_sync.rs:38:13
    |
 LL |     let _ = Arc::new(mutex.lock().unwrap());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `Arc<MutexGuard<'_, i32>>` is not `Send` and `Sync` as:
-   = note: - the trait `Send` is not implemented for `MutexGuard<'_, i32>`
-   = help: consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types
-   = note: if you intend to use `Arc` with `Send` and `Sync` traits
-   = note: wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `MutexGuard<'_, i32>`
+   = note: `Arc<MutexGuard<'_, i32>>` is not `Send` and `Sync` as `MutexGuard<'_, i32>` is not `Send`
+   = help: if the `Arc` will not used be across threads replace it with an `Rc`
+   = help: otherwise make `MutexGuard<'_, i32>` `Send` and `Sync` or consider a wrapper type such as `Mutex`
 
 error: usage of an `Arc` that is not `Send` and `Sync`
-  --> tests/ui/arc_with_non_send_sync.rs:44:13
+  --> tests/ui/arc_with_non_send_sync.rs:40:13
    |
 LL |     let _ = Arc::new(&42 as *const i32);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `Arc<*const i32>` is not `Send` and `Sync` as:
-   = note: - the trait `Send` is not implemented for `*const i32`
-   = note: - the trait `Sync` is not implemented for `*const i32`
-   = help: consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types
-   = note: if you intend to use `Arc` with `Send` and `Sync` traits
-   = note: wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `*const i32`
+   = note: `Arc<*const i32>` is not `Send` and `Sync` as `*const i32` is neither `Send` nor `Sync`
+   = help: if the `Arc` will not used be across threads replace it with an `Rc`
+   = help: otherwise make `*const i32` `Send` and `Sync` or consider a wrapper type such as `Mutex`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout
index 27ad538..d448db0 100644
--- a/src/tools/clippy/tests/ui/author.stdout
+++ b/src/tools/clippy/tests/ui/author.stdout
@@ -5,7 +5,7 @@
     && match_qpath(qpath, &["char"])
     && let ExprKind::Lit(ref lit) = expr.kind
     && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
     && name.as_str() == "x"
 {
     // report your lint here
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
index 579f137..80b928d 100644
--- a/src/tools/clippy/tests/ui/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
@@ -4,13 +4,13 @@
     && let Some(init) = local.init
     && let ExprKind::Lit(ref lit) = init.kind
     && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
     && name.as_str() == "x"
     && let StmtKind::Local(local1) = block.stmts[1].kind
     && let Some(init1) = local1.init
     && let ExprKind::Lit(ref lit1) = init1.kind
     && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local1.pat.kind
     && name1.as_str() == "_t"
     && let StmtKind::Semi(e) = block.stmts[2].kind
     && let ExprKind::Unary(UnOp::Neg, inner) = e.kind
@@ -28,7 +28,7 @@
     && let ExprKind::Path(ref qpath) = func.kind
     && match_qpath(qpath, &["String", "new"])
     && args.is_empty()
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
     && name.as_str() == "expr"
     && let Some(trailing_expr) = block.expr
     && let ExprKind::Call(func1, args1) = trailing_expr.kind
diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout
index 94a6436..631105a 100644
--- a/src/tools/clippy/tests/ui/author/loop.stdout
+++ b/src/tools/clippy/tests/ui/author/loop.stdout
@@ -1,5 +1,5 @@
 if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr)
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = pat.kind
     && name.as_str() == "y"
     && let ExprKind::Struct(qpath, fields, None) = arg.kind
     && matches!(qpath, QPath::LangItem(LangItem::Range, _))
@@ -16,7 +16,7 @@
     && let Some(init) = local.init
     && let ExprKind::Path(ref qpath1) = init.kind
     && match_qpath(qpath1, &["y"])
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
     && name1.as_str() == "z"
     && block.expr.is_none()
 {
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
index f2e54c2..b90c830 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
@@ -34,7 +34,7 @@
     && let ExprKind::Path(ref qpath3) = inner2.kind
     && match_qpath(qpath3, &["x"])
     && block.expr.is_none()
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
     && name.as_str() == "print_text"
 {
     // report your lint here
diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
index a719e3a..3f9be29 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
@@ -1,5 +1,5 @@
 if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr)
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = pat.kind
     && name.as_str() == "i"
     && let ExprKind::Struct(qpath, fields, None) = arg.kind
     && matches!(qpath, QPath::LangItem(LangItem::Range, _))
diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout
index 88e2ca6..30e4a9b 100644
--- a/src/tools/clippy/tests/ui/author/matches.stdout
+++ b/src/tools/clippy/tests/ui/author/matches.stdout
@@ -20,7 +20,7 @@
     && let Some(init1) = local1.init
     && let ExprKind::Lit(ref lit4) = init1.kind
     && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
     && name.as_str() == "x"
     && let Some(trailing_expr) = block.expr
     && let ExprKind::Path(ref qpath) = trailing_expr.kind
@@ -29,7 +29,7 @@
     && arms[2].guard.is_none()
     && let ExprKind::Lit(ref lit5) = arms[2].body.kind
     && let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node
-    && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind
+    && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
     && name1.as_str() == "a"
 {
     // report your lint here
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
index a6f3b16..f6fdeba 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
@@ -176,3 +176,17 @@
     }
     .into()
 }
+
+#[proc_macro_attribute]
+pub fn duplicated_attr(_attr: TokenStream, input: TokenStream) -> TokenStream {
+    let item = parse_macro_input!(input as syn::Item);
+    let attrs: Vec<syn::Attribute> = vec![];
+    quote! {
+        #(#attrs)*
+        #[allow(unused)]
+        #[allow(unused)]
+        #[allow(unused)]
+        #item
+    }
+    .into()
+}
diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs
index ce76ad3..215c008 100644
--- a/src/tools/clippy/tests/ui/cast.rs
+++ b/src/tools/clippy/tests/ui/cast.rs
@@ -463,6 +463,18 @@
     }
 }
 
+fn issue11738() {
+    macro_rules! m {
+        () => {
+            let _ = i32::MIN as u32; // cast_sign_loss
+            let _ = u32::MAX as u8; // cast_possible_truncation
+            let _ = std::f64::consts::PI as f32; // cast_possible_truncation
+            let _ = 0i8 as i32; // cast_lossless
+        };
+    }
+    m!();
+}
+
 fn issue12506() -> usize {
     let bar: Result<Option<i64>, u32> = Ok(Some(10));
     bar.unwrap().unwrap() as usize
diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr
index 3736e8a..8b269c4 100644
--- a/src/tools/clippy/tests/ui/cast.stderr
+++ b/src/tools/clippy/tests/ui/cast.stderr
@@ -650,8 +650,47 @@
 LL |         (a.abs() * b.pow(2) / c.abs()) as u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: casting `i32` to `u32` may lose the sign of the value
+  --> tests/ui/cast.rs:469:21
+   |
+LL |             let _ = i32::MIN as u32; // cast_sign_loss
+   |                     ^^^^^^^^^^^^^^^
+...
+LL |     m!();
+   |     ---- in this macro invocation
+   |
+   = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: casting `u32` to `u8` may truncate the value
+  --> tests/ui/cast.rs:470:21
+   |
+LL |             let _ = u32::MAX as u8; // cast_possible_truncation
+   |                     ^^^^^^^^^^^^^^
+...
+LL |     m!();
+   |     ---- in this macro invocation
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+   = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |             let _ = u8::try_from(u32::MAX); // cast_possible_truncation
+   |                     ~~~~~~~~~~~~~~~~~~~~~~
+
+error: casting `f64` to `f32` may truncate the value
+  --> tests/ui/cast.rs:471:21
+   |
+LL |             let _ = std::f64::consts::PI as f32; // cast_possible_truncation
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     m!();
+   |     ---- in this macro invocation
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+   = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
-  --> tests/ui/cast.rs:468:5
+  --> tests/ui/cast.rs:480:5
    |
 LL |     bar.unwrap().unwrap() as usize
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -663,10 +702,10 @@
    |
 
 error: casting `i64` to `usize` may lose the sign of the value
-  --> tests/ui/cast.rs:468:5
+  --> tests/ui/cast.rs:480:5
    |
 LL |     bar.unwrap().unwrap() as usize
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 87 previous errors
+error: aborting due to 90 previous errors
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-12585.rs b/src/tools/clippy/tests/ui/crashes/ice-12585.rs
new file mode 100644
index 0000000..7928115
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-12585.rs
@@ -0,0 +1,26 @@
+#![allow(clippy::unit_arg)]
+
+struct One {
+    x: i32,
+}
+struct Two {
+    x: i32,
+}
+
+struct Product {}
+
+impl Product {
+    pub fn a_method(self, _: ()) {}
+}
+
+fn from_array(_: [i32; 2]) -> Product {
+    todo!()
+}
+
+pub fn main() {
+    let one = One { x: 1 };
+    let two = Two { x: 2 };
+
+    let product = from_array([one.x, two.x]);
+    product.a_method(<()>::default());
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-12616.stderr b/src/tools/clippy/tests/ui/crashes/ice-12616.stderr
index ef573f5..c7cf5cf 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-12616.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-12616.stderr
@@ -7,13 +7,5 @@
    = note: `-D clippy::ptr-as-ptr` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::ptr_as_ptr)]`
 
-error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/crashes/ice-12616.rs:6:5
-   |
-LL |     s() as *const ();
-   |     ^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `s().cast::<()>()`
-   |
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
index 178d4a1..7e22c84 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -235,3 +235,8 @@
 /// OSes
 /// UXes
 fn plural_acronym_test() {}
+
+extern {
+    /// `foo()`
+    fn in_extern();
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index 01edb44..3e2cb0d 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -235,3 +235,8 @@
 /// OSes
 /// UXes
 fn plural_acronym_test() {}
+
+extern {
+    /// foo()
+    fn in_extern();
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index ac87630..cd2228c 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -352,5 +352,16 @@
 LL | /// `ABes`
    |     ~~~~~~
 
-error: aborting due to 32 previous errors
+error: item in documentation is missing backticks
+  --> tests/ui/doc/doc-fixable.rs:240:9
+   |
+LL |     /// foo()
+   |         ^^^^^
+   |
+help: try
+   |
+LL |     /// `foo()`
+   |         ~~~~~~~
+
+error: aborting due to 33 previous errors
 
diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs
index d051c88..d51e7e3 100644
--- a/src/tools/clippy/tests/ui/duplicated_attributes.rs
+++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs
@@ -1,9 +1,14 @@
+//@aux-build:proc_macro_attr.rs
+
 #![warn(clippy::duplicated_attributes)]
 #![cfg(any(unix, windows))]
 #![allow(dead_code)]
 #![allow(dead_code)] //~ ERROR: duplicated attribute
 #![cfg(any(unix, windows))] // Should not warn!
 
+#[macro_use]
+extern crate proc_macro_attr;
+
 #[cfg(any(unix, windows, target_os = "linux"))]
 #[allow(dead_code)]
 #[allow(dead_code)] //~ ERROR: duplicated attribute
@@ -12,7 +17,10 @@
 
 #[cfg(unix)]
 #[cfg(windows)]
-#[cfg(unix)] //~ ERROR: duplicated attribute
+#[cfg(unix)] // cfgs are not handled
 fn bar() {}
 
+#[proc_macro_attr::duplicated_attr()] // Should not warn!
+fn babar() {}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.stderr b/src/tools/clippy/tests/ui/duplicated_attributes.stderr
index 9e26ba9..0903617 100644
--- a/src/tools/clippy/tests/ui/duplicated_attributes.stderr
+++ b/src/tools/clippy/tests/ui/duplicated_attributes.stderr
@@ -1,16 +1,16 @@
 error: duplicated attribute
-  --> tests/ui/duplicated_attributes.rs:4:10
+  --> tests/ui/duplicated_attributes.rs:6:10
    |
 LL | #![allow(dead_code)]
    |          ^^^^^^^^^
    |
 note: first defined here
-  --> tests/ui/duplicated_attributes.rs:3:10
+  --> tests/ui/duplicated_attributes.rs:5:10
    |
 LL | #![allow(dead_code)]
    |          ^^^^^^^^^
 help: remove this attribute
-  --> tests/ui/duplicated_attributes.rs:4:10
+  --> tests/ui/duplicated_attributes.rs:6:10
    |
 LL | #![allow(dead_code)]
    |          ^^^^^^^^^
@@ -18,38 +18,21 @@
    = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]`
 
 error: duplicated attribute
-  --> tests/ui/duplicated_attributes.rs:9:9
+  --> tests/ui/duplicated_attributes.rs:14:9
    |
 LL | #[allow(dead_code)]
    |         ^^^^^^^^^
    |
 note: first defined here
-  --> tests/ui/duplicated_attributes.rs:8:9
+  --> tests/ui/duplicated_attributes.rs:13:9
    |
 LL | #[allow(dead_code)]
    |         ^^^^^^^^^
 help: remove this attribute
-  --> tests/ui/duplicated_attributes.rs:9:9
+  --> tests/ui/duplicated_attributes.rs:14:9
    |
 LL | #[allow(dead_code)]
    |         ^^^^^^^^^
 
-error: duplicated attribute
-  --> tests/ui/duplicated_attributes.rs:15:7
-   |
-LL | #[cfg(unix)]
-   |       ^^^^
-   |
-note: first defined here
-  --> tests/ui/duplicated_attributes.rs:13:7
-   |
-LL | #[cfg(unix)]
-   |       ^^^^
-help: remove this attribute
-  --> tests/ui/duplicated_attributes.rs:15:7
-   |
-LL | #[cfg(unix)]
-   |       ^^^^
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed
index a0b7076..d6e736b 100644
--- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed
@@ -16,6 +16,16 @@
 
     let x: Option<Vec<String>> = None;
     x.unwrap_or_default();
+
+    // Issue #12564
+    // No error as &Vec<_> doesn't implement std::default::Default
+    let mut map = std::collections::HashMap::from([(0, vec![0; 3]), (1, vec![1; 3]), (2, vec![2])]);
+    let x: &[_] = if let Some(x) = map.get(&0) { x } else { &[] };
+    // Same code as above written using match.
+    let x: &[_] = match map.get(&0) {
+        Some(x) => x,
+        None => &[],
+    };
 }
 
 // Issue #12531
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs
index 1d4cca1..462d5d9 100644
--- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs
@@ -37,6 +37,16 @@
     } else {
         Vec::default()
     };
+
+    // Issue #12564
+    // No error as &Vec<_> doesn't implement std::default::Default
+    let mut map = std::collections::HashMap::from([(0, vec![0; 3]), (1, vec![1; 3]), (2, vec![2])]);
+    let x: &[_] = if let Some(x) = map.get(&0) { x } else { &[] };
+    // Same code as above written using match.
+    let x: &[_] = match map.get(&0) {
+        Some(x) => x,
+        None => &[],
+    };
 }
 
 // Issue #12531
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr
index d89212e..3f1da44 100644
--- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr
@@ -53,7 +53,7 @@
    | |_____^ help: replace it with: `x.unwrap_or_default()`
 
 error: match can be simplified with `.unwrap_or_default()`
-  --> tests/ui/manual_unwrap_or_default.rs:46:20
+  --> tests/ui/manual_unwrap_or_default.rs:56:20
    |
 LL |           Some(_) => match *b {
    |  ____________________^
diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs
index a6cf038..b75ef87 100644
--- a/src/tools/clippy/tests/ui/module_name_repetitions.rs
+++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs
@@ -19,6 +19,20 @@
 
     // Should not warn
     pub struct Foobar;
+
+    // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name.
+    pub fn to_foo() {}
+    pub fn into_foo() {}
+    pub fn as_foo() {}
+    pub fn from_foo() {}
+    pub fn try_into_foo() {}
+    pub fn try_from_foo() {}
+    pub trait IntoFoo {}
+    pub trait ToFoo {}
+    pub trait AsFoo {}
+    pub trait FromFoo {}
+    pub trait TryIntoFoo {}
+    pub trait TryFromFoo {}
 }
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
index 8afb4df..6b8a103 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -1,3 +1,4 @@
+//@needs-asm-support
 //@aux-build:proc_macros.rs
 #![allow(unused)]
 #![allow(deref_nullptr)]
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
index cff85ae..e732bde 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
@@ -1,5 +1,5 @@
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:36:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:37:5
    |
 LL | /     unsafe {
 LL | |         STATIC += 1;
@@ -8,12 +8,12 @@
    | |_____^
    |
 note: modification of a mutable static occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:37:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:38:9
    |
 LL |         STATIC += 1;
    |         ^^^^^^^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:38:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:39:9
    |
 LL |         not_very_safe();
    |         ^^^^^^^^^^^^^^^
@@ -21,7 +21,7 @@
    = help: to override `-D warnings` add `#[allow(clippy::multiple_unsafe_ops_per_block)]`
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:45:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:46:5
    |
 LL | /     unsafe {
 LL | |         drop(u.u);
@@ -30,18 +30,18 @@
    | |_____^
    |
 note: union field access occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:46:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:47:14
    |
 LL |         drop(u.u);
    |              ^^^
 note: raw pointer dereference occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:47:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:48:9
    |
 LL |         *raw_ptr();
    |         ^^^^^^^^^^
 
 error: this `unsafe` block contains 3 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:52:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:53:5
    |
 LL | /     unsafe {
 LL | |         asm!("nop");
@@ -51,23 +51,23 @@
    | |_____^
    |
 note: inline assembly used here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:53:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:54:9
    |
 LL |         asm!("nop");
    |         ^^^^^^^^^^^
 note: unsafe method call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:54:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9
    |
 LL |         sample.not_very_safe();
    |         ^^^^^^^^^^^^^^^^^^^^^^
 note: modification of a mutable static occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:56:9
    |
 LL |         STATIC = 0;
    |         ^^^^^^^^^^
 
 error: this `unsafe` block contains 6 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:61:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:62:5
    |
 LL | /     unsafe {
 LL | |         drop(u.u);
@@ -79,55 +79,55 @@
    | |_____^
    |
 note: union field access occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:62:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:63:14
    |
 LL |         drop(u.u);
    |              ^^^
 note: access of a mutable static occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:63:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:64:14
    |
 LL |         drop(STATIC);
    |              ^^^^^^
 note: unsafe method call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:64:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:65:9
    |
 LL |         sample.not_very_safe();
    |         ^^^^^^^^^^^^^^^^^^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:65:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9
    |
 LL |         not_very_safe();
    |         ^^^^^^^^^^^^^^^
 note: raw pointer dereference occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9
    |
 LL |         *raw_ptr();
    |         ^^^^^^^^^^
 note: inline assembly used here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:68:9
    |
 LL |         asm!("nop");
    |         ^^^^^^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:105:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:106:5
    |
 LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:105:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:106:14
    |
 LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: raw pointer dereference occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:105:39
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:106:39
    |
 LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |                                       ^^^^^^^^^^^^^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:123:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:124:5
    |
 LL | /     unsafe {
 LL | |         x();
@@ -136,18 +136,18 @@
    | |_____^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:124:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:125:9
    |
 LL |         x();
    |         ^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:125:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:126:9
    |
 LL |         x();
    |         ^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:134:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:135:9
    |
 LL | /         unsafe {
 LL | |             T::X();
@@ -156,18 +156,18 @@
    | |_________^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:135:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:136:13
    |
 LL |             T::X();
    |             ^^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:136:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:137:13
    |
 LL |             T::X();
    |             ^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:144:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:145:5
    |
 LL | /     unsafe {
 LL | |         x.0();
@@ -176,12 +176,12 @@
    | |_____^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:145:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:146:9
    |
 LL |         x.0();
    |         ^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:146:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:147:9
    |
 LL |         x.0();
    |         ^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed
index 998f543..5121077 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrow.fixed
@@ -251,3 +251,11 @@
         (&S).f::<()>();
     }
 }
+
+fn issue_12268() {
+    let option = Some((&1,));
+    let x = (&1,);
+    option.unwrap_or((x.0,));
+    //~^ ERROR: this expression creates a reference which is immediately dereferenced by the
+    // compiler
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs
index acb2c74..e3a5cb2 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.rs
+++ b/src/tools/clippy/tests/ui/needless_borrow.rs
@@ -251,3 +251,11 @@
         (&S).f::<()>();
     }
 }
+
+fn issue_12268() {
+    let option = Some((&1,));
+    let x = (&1,);
+    option.unwrap_or((&x.0,));
+    //~^ ERROR: this expression creates a reference which is immediately dereferenced by the
+    // compiler
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr
index 5f02833..4b2b17e 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrow.stderr
@@ -163,5 +163,11 @@
 LL |         let _ = &mut (&mut { x.u }).x;
    |                      ^^^^^^^^^^^^^^ help: change this to: `{ x.u }`
 
-error: aborting due to 27 previous errors
+error: this expression creates a reference which is immediately dereferenced by the compiler
+  --> tests/ui/needless_borrow.rs:258:23
+   |
+LL |     option.unwrap_or((&x.0,));
+   |                       ^^^^ help: change this to: `x.0`
+
+error: aborting due to 28 previous errors
 
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
index bd4be3e..aba5996 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
@@ -109,4 +109,12 @@
     let _ = !(a >= b);
 }
 
+fn issue_12625() {
+    let a = 0;
+    let b = 0;
+    if (a as u64) < b {} //~ ERROR: this boolean expression can be simplified
+    if (a as u64) < b {} //~ ERROR: this boolean expression can be simplified
+    if a as u64 > b {} //~ ERROR: this boolean expression can be simplified
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
index 4523c73..35f22db 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
@@ -109,4 +109,12 @@
     let _ = !(a >= b);
 }
 
+fn issue_12625() {
+    let a = 0;
+    let b = 0;
+    if !(a as u64 >= b) {} //~ ERROR: this boolean expression can be simplified
+    if !((a as u64) >= b) {} //~ ERROR: this boolean expression can be simplified
+    if !(a as u64 <= b) {} //~ ERROR: this boolean expression can be simplified
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
index e32c8da..18da4e0 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
@@ -79,5 +79,23 @@
 LL |     if !res.is_none() {}
    |        ^^^^^^^^^^^^^^ help: try: `res.is_some()`
 
-error: aborting due to 13 previous errors
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:115:8
+   |
+LL |     if !(a as u64 >= b) {}
+   |        ^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:116:8
+   |
+LL |     if !((a as u64) >= b) {}
+   |        ^^^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:117:8
+   |
+LL |     if !(a as u64 <= b) {}
+   |        ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b`
+
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
index 61d37b8..fa15c32 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
@@ -1,5 +1,4 @@
 //@aux-build:proc_macros.rs
-//@compile-flags: -Zdeduplicate-diagnostics=yes
 
 #![warn(clippy::ptr_as_ptr)]
 
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
index 8f2068c..7ab52e6 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
@@ -1,5 +1,4 @@
 //@aux-build:proc_macros.rs
-//@compile-flags: -Zdeduplicate-diagnostics=yes
 
 #![warn(clippy::ptr_as_ptr)]
 
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
index e6cd697..e162f35 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
@@ -1,5 +1,5 @@
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:19:33
+  --> tests/ui/ptr_as_ptr.rs:18:33
    |
 LL |         *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::issue_11278_a::T<String>) }
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `Box::into_raw(Box::new(o)).cast::<super::issue_11278_a::T<String>>()`
@@ -8,37 +8,37 @@
    = help: to override `-D warnings` add `#[allow(clippy::ptr_as_ptr)]`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:28:13
+  --> tests/ui/ptr_as_ptr.rs:27:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:29:13
+  --> tests/ui/ptr_as_ptr.rs:28:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:34:17
+  --> tests/ui/ptr_as_ptr.rs:33:17
    |
 LL |         let _ = *ptr_ptr as *const i32;
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:47:25
+  --> tests/ui/ptr_as_ptr.rs:46:25
    |
 LL |     let _: *const i32 = ptr as *const _;
    |                         ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:48:23
+  --> tests/ui/ptr_as_ptr.rs:47:23
    |
 LL |     let _: *mut i32 = mut_ptr as _;
    |                       ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:51:21
+  --> tests/ui/ptr_as_ptr.rs:50:21
    |
 LL |     let _ = inline!($ptr as *const i32);
    |                     ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
@@ -46,157 +46,157 @@
    = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:72:13
+  --> tests/ui/ptr_as_ptr.rs:71:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:73:13
+  --> tests/ui/ptr_as_ptr.rs:72:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:80:9
+  --> tests/ui/ptr_as_ptr.rs:79:9
    |
 LL |         ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:84:9
+  --> tests/ui/ptr_as_ptr.rs:83:9
    |
 LL |         std::ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut::<u32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:89:9
+  --> tests/ui/ptr_as_ptr.rs:88:9
    |
 LL |         ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:93:9
+  --> tests/ui/ptr_as_ptr.rs:92:9
    |
 LL |         core::ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut::<u32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:98:9
+  --> tests/ui/ptr_as_ptr.rs:97:9
    |
 LL |         ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:102:9
+  --> tests/ui/ptr_as_ptr.rs:101:9
    |
 LL |         std::ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::<u32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:107:9
+  --> tests/ui/ptr_as_ptr.rs:106:9
    |
 LL |         ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:111:9
+  --> tests/ui/ptr_as_ptr.rs:110:9
    |
 LL |         core::ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null::<u32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:118:9
+  --> tests/ui/ptr_as_ptr.rs:117:9
    |
 LL |         ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:122:9
+  --> tests/ui/ptr_as_ptr.rs:121:9
    |
 LL |         std::ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:127:9
+  --> tests/ui/ptr_as_ptr.rs:126:9
    |
 LL |         ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:131:9
+  --> tests/ui/ptr_as_ptr.rs:130:9
    |
 LL |         core::ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:136:9
+  --> tests/ui/ptr_as_ptr.rs:135:9
    |
 LL |         ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:140:9
+  --> tests/ui/ptr_as_ptr.rs:139:9
    |
 LL |         std::ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:145:9
+  --> tests/ui/ptr_as_ptr.rs:144:9
    |
 LL |         ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:149:9
+  --> tests/ui/ptr_as_ptr.rs:148:9
    |
 LL |         core::ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:156:9
+  --> tests/ui/ptr_as_ptr.rs:155:9
    |
 LL |         ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:160:9
+  --> tests/ui/ptr_as_ptr.rs:159:9
    |
 LL |         std::ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:165:9
+  --> tests/ui/ptr_as_ptr.rs:164:9
    |
 LL |         ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:169:9
+  --> tests/ui/ptr_as_ptr.rs:168:9
    |
 LL |         core::ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:174:9
+  --> tests/ui/ptr_as_ptr.rs:173:9
    |
 LL |         ptr::null() as _
    |         ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:178:9
+  --> tests/ui/ptr_as_ptr.rs:177:9
    |
 LL |         std::ptr::null() as _
    |         ^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:183:9
+  --> tests/ui/ptr_as_ptr.rs:182:9
    |
 LL |         ptr::null() as _
    |         ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> tests/ui/ptr_as_ptr.rs:187:9
+  --> tests/ui/ptr_as_ptr.rs:186:9
    |
 LL |         core::ptr::null() as _
    |         ^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()`
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
index 5163639..86a937b 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
@@ -213,3 +213,53 @@
         let _ = !v.iter().any(|fp| test_u32_2(*fp.field));
     }
 }
+
+mod issue_11910 {
+    fn computations() -> u32 {
+        0
+    }
+
+    struct Foo;
+    impl Foo {
+        fn bar(&self, _: bool) {}
+    }
+
+    fn test_normal_for_iter() {
+        let v = vec![3, 2, 1, 0, -1, -2, -3];
+        let _ = !v.iter().any(|x| *x == 42);
+        Foo.bar(!v.iter().any(|x| *x == 42));
+    }
+
+    fn test_then_for_iter() {
+        let v = vec![3, 2, 1, 0, -1, -2, -3];
+        (!v.iter().any(|x| *x == 42)).then(computations);
+    }
+
+    fn test_then_some_for_iter() {
+        let v = vec![3, 2, 1, 0, -1, -2, -3];
+        (!v.iter().any(|x| *x == 42)).then_some(0);
+    }
+
+    fn test_normal_for_str() {
+        let s = "hello";
+        let _ = !s.contains("world");
+        Foo.bar(!s.contains("world"));
+        let s = String::from("hello");
+        let _ = !s.contains("world");
+        Foo.bar(!s.contains("world"));
+    }
+
+    fn test_then_for_str() {
+        let s = "hello";
+        let _ = (!s.contains("world")).then(computations);
+        let s = String::from("hello");
+        let _ = (!s.contains("world")).then(computations);
+    }
+
+    fn test_then_some_for_str() {
+        let s = "hello";
+        let _ = (!s.contains("world")).then_some(0);
+        let s = String::from("hello");
+        let _ = (!s.contains("world")).then_some(0);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
index c7d773e..c0103a0 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
@@ -219,3 +219,53 @@
         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
     }
 }
+
+mod issue_11910 {
+    fn computations() -> u32 {
+        0
+    }
+
+    struct Foo;
+    impl Foo {
+        fn bar(&self, _: bool) {}
+    }
+
+    fn test_normal_for_iter() {
+        let v = vec![3, 2, 1, 0, -1, -2, -3];
+        let _ = v.iter().find(|x| **x == 42).is_none();
+        Foo.bar(v.iter().find(|x| **x == 42).is_none());
+    }
+
+    fn test_then_for_iter() {
+        let v = vec![3, 2, 1, 0, -1, -2, -3];
+        v.iter().find(|x| **x == 42).is_none().then(computations);
+    }
+
+    fn test_then_some_for_iter() {
+        let v = vec![3, 2, 1, 0, -1, -2, -3];
+        v.iter().find(|x| **x == 42).is_none().then_some(0);
+    }
+
+    fn test_normal_for_str() {
+        let s = "hello";
+        let _ = s.find("world").is_none();
+        Foo.bar(s.find("world").is_none());
+        let s = String::from("hello");
+        let _ = s.find("world").is_none();
+        Foo.bar(s.find("world").is_none());
+    }
+
+    fn test_then_for_str() {
+        let s = "hello";
+        let _ = s.find("world").is_none().then(computations);
+        let s = String::from("hello");
+        let _ = s.find("world").is_none().then(computations);
+    }
+
+    fn test_then_some_for_str() {
+        let s = "hello";
+        let _ = s.find("world").is_none().then_some(0);
+        let s = String::from("hello");
+        let _ = s.find("world").is_none().then_some(0);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr
index 4ad1e25..2c858b9 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr
@@ -282,5 +282,77 @@
 LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_2(*fp.field))`
 
-error: aborting due to 43 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> tests/ui/search_is_some_fixable_none.rs:235:17
+   |
+LL |         let _ = v.iter().find(|x| **x == 42).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> tests/ui/search_is_some_fixable_none.rs:236:17
+   |
+LL |         Foo.bar(v.iter().find(|x| **x == 42).is_none());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> tests/ui/search_is_some_fixable_none.rs:241:9
+   |
+LL |         v.iter().find(|x| **x == 42).is_none().then(computations);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> tests/ui/search_is_some_fixable_none.rs:246:9
+   |
+LL |         v.iter().find(|x| **x == 42).is_none().then_some(0);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))`
+
+error: called `is_none()` after calling `find()` on a string
+  --> tests/ui/search_is_some_fixable_none.rs:251:17
+   |
+LL |         let _ = s.find("world").is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> tests/ui/search_is_some_fixable_none.rs:252:17
+   |
+LL |         Foo.bar(s.find("world").is_none());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> tests/ui/search_is_some_fixable_none.rs:254:17
+   |
+LL |         let _ = s.find("world").is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> tests/ui/search_is_some_fixable_none.rs:255:17
+   |
+LL |         Foo.bar(s.find("world").is_none());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> tests/ui/search_is_some_fixable_none.rs:260:17
+   |
+LL |         let _ = s.find("world").is_none().then(computations);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))`
+
+error: called `is_none()` after calling `find()` on a string
+  --> tests/ui/search_is_some_fixable_none.rs:262:17
+   |
+LL |         let _ = s.find("world").is_none().then(computations);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))`
+
+error: called `is_none()` after calling `find()` on a string
+  --> tests/ui/search_is_some_fixable_none.rs:267:17
+   |
+LL |         let _ = s.find("world").is_none().then_some(0);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))`
+
+error: called `is_none()` after calling `find()` on a string
+  --> tests/ui/search_is_some_fixable_none.rs:269:17
+   |
+LL |         let _ = s.find("world").is_none().then_some(0);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))`
+
+error: aborting due to 55 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.rs b/src/tools/clippy/tests/ui/unconditional_recursion.rs
index 70b390b..a51fc56 100644
--- a/src/tools/clippy/tests/ui/unconditional_recursion.rs
+++ b/src/tools/clippy/tests/ui/unconditional_recursion.rs
@@ -266,7 +266,7 @@
 
 impl S13 {
     fn new() -> Self {
-        // Shoud not warn!
+        // Should not warn!
         Self::default()
     }
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr
index fbc0574..3d58c9e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr
@@ -57,5 +57,41 @@
 LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg)]`
 
-error: aborting due to 8 previous errors
+error: duplicated attribute
+  --> tests/ui/unnecessary_clippy_cfg.rs:8:26
+   |
+LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))]
+   |                          ^^^^^^^^^
+   |
+note: first defined here
+  --> tests/ui/unnecessary_clippy_cfg.rs:6:26
+   |
+LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
+   |                          ^^^^^^^^^
+help: remove this attribute
+  --> tests/ui/unnecessary_clippy_cfg.rs:8:26
+   |
+LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))]
+   |                          ^^^^^^^^^
+   = note: `-D clippy::duplicated-attributes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]`
+
+error: duplicated attribute
+  --> tests/ui/unnecessary_clippy_cfg.rs:17:25
+   |
+LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))]
+   |                         ^^^^^^^^^
+   |
+note: first defined here
+  --> tests/ui/unnecessary_clippy_cfg.rs:15:25
+   |
+LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
+   |                         ^^^^^^^^^
+help: remove this attribute
+  --> tests/ui/unnecessary_clippy_cfg.rs:17:25
+   |
+LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))]
+   |                         ^^^^^^^^^
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed
index d1cdf73..8175908 100644
--- a/src/tools/clippy/tests/ui/useless_attribute.fixed
+++ b/src/tools/clippy/tests/ui/useless_attribute.fixed
@@ -1,6 +1,6 @@
 //@aux-build:proc_macro_derive.rs
 
-#![allow(unused)]
+#![allow(unused, clippy::duplicated_attributes)]
 #![warn(clippy::useless_attribute)]
 #![warn(unreachable_pub)]
 #![feature(rustc_private)]
diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs
index d6aa7fa..59a9dcf 100644
--- a/src/tools/clippy/tests/ui/useless_attribute.rs
+++ b/src/tools/clippy/tests/ui/useless_attribute.rs
@@ -1,6 +1,6 @@
 //@aux-build:proc_macro_derive.rs
 
-#![allow(unused)]
+#![allow(unused, clippy::duplicated_attributes)]
 #![warn(clippy::useless_attribute)]
 #![warn(unreachable_pub)]
 #![feature(rustc_private)]
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index d813104..901977d 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -19,7 +19,12 @@
 
 [assign]
 contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
-users_on_vacation = ["y21"]
+users_on_vacation = [
+    "y21",
+    "matthiaskrgr",
+    "giraffate",
+    "Centri3",
+]
 
 [assign.owners]
 "/.github" = ["@flip1995"]
diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs
index 140c3aa..3231f9f 100644
--- a/src/tools/compiletest/src/errors.rs
+++ b/src/tools/compiletest/src/errors.rs
@@ -118,7 +118,7 @@
     //     //[rev1]~
     //     //[rev1,rev2]~^^
     static RE: Lazy<Regex> =
-        Lazy::new(|| Regex::new(r"//(?:\[(?P<revs>[\w,]+)])?~(?P<adjust>\||\^*)").unwrap());
+        Lazy::new(|| Regex::new(r"//(?:\[(?P<revs>[\w\-,]+)])?~(?P<adjust>\||\^*)").unwrap());
 
     let captures = RE.captures(line)?;
 
@@ -178,3 +178,6 @@
     );
     Some((which, Error { line_num, kind, msg }))
 }
+
+#[cfg(test)]
+mod tests;
diff --git a/src/tools/compiletest/src/errors/tests.rs b/src/tools/compiletest/src/errors/tests.rs
new file mode 100644
index 0000000..dfb001d
--- /dev/null
+++ b/src/tools/compiletest/src/errors/tests.rs
@@ -0,0 +1,12 @@
+use super::*;
+
+#[test]
+fn test_parse_expected_matching() {
+    // Ensure that we correctly extract expected revisions
+    let d1 = "//[rev1,rev2]~^ ERROR foo";
+    let d2 = "//[rev1,rev2-foo]~^ ERROR foo";
+    assert!(parse_expected(None, 1, d1, Some("rev1")).is_some());
+    assert!(parse_expected(None, 1, d1, Some("rev2")).is_some());
+    assert!(parse_expected(None, 1, d2, Some("rev1")).is_some());
+    assert!(parse_expected(None, 1, d2, Some("rev2-foo")).is_some());
+}
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index f5d7ce1..8aafbb3 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -244,7 +244,7 @@
     pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth";
     pub const INCREMENTAL: &'static str = "incremental";
     pub const KNOWN_BUG: &'static str = "known-bug";
-    pub const MIR_UNIT_TEST: &'static str = "unit-test";
+    pub const TEST_MIR_PASS: &'static str = "test-mir-pass";
     pub const REMAP_SRC_BASE: &'static str = "remap-src-base";
     pub const COMPARE_OUTPUT_LINES_BY_SUBSET: &'static str = "compare-output-lines-by-subset";
     pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags";
@@ -549,7 +549,7 @@
 
                     config.set_name_value_directive(
                         ln,
-                        MIR_UNIT_TEST,
+                        TEST_MIR_PASS,
                         &mut self.mir_unit_test,
                         |s| s.trim().to_string(),
                     );
@@ -922,7 +922,7 @@
     "should-fail",
     "should-ice",
     "stderr-per-bitwidth",
-    "unit-test",
+    "test-mir-pass",
     "unset-exec-env",
     "unset-rustc-env",
     // tidy-alphabetical-end
diff --git a/src/tools/generate-windows-sys/Cargo.toml b/src/tools/generate-windows-sys/Cargo.toml
index 9ea26de..8b971d6 100644
--- a/src/tools/generate-windows-sys/Cargo.toml
+++ b/src/tools/generate-windows-sys/Cargo.toml
@@ -4,4 +4,4 @@
 edition = "2021"
 
 [dependencies.windows-bindgen]
-version = "0.55.0"
+version = "0.56.0"
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index 2566124..aca780e 100644
--- a/src/tools/lint-docs/src/lib.rs
+++ b/src/tools/lint-docs/src/lib.rs
@@ -7,6 +7,43 @@
 
 mod groups;
 
+/// List of lints which have been renamed.
+///
+/// These will get redirects in the output to the new name. The
+/// format is `(level, [(old_name, new_name), ...])`.
+///
+/// Note: This hard-coded list is a temporary hack. The intent is in the
+/// future to have `rustc` expose this information in some way (like a `-Z`
+/// flag spitting out JSON). Also, this does not yet support changing the
+/// level of the lint, which will be more difficult to support, since rustc
+/// currently does not track that historical information.
+static RENAMES: &[(Level, &[(&str, &str)])] = &[
+    (
+        Level::Allow,
+        &[
+            ("single-use-lifetime", "single-use-lifetimes"),
+            ("elided-lifetime-in-path", "elided-lifetimes-in-paths"),
+            ("async-idents", "keyword-idents"),
+            ("disjoint-capture-migration", "rust-2021-incompatible-closure-captures"),
+            ("or-patterns-back-compat", "rust-2021-incompatible-or-patterns"),
+        ],
+    ),
+    (
+        Level::Warn,
+        &[
+            ("bare-trait-object", "bare-trait-objects"),
+            ("unstable-name-collision", "unstable-name-collisions"),
+            ("unused-doc-comment", "unused-doc-comments"),
+            ("redundant-semicolon", "redundant-semicolons"),
+            ("overlapping-patterns", "overlapping-range-endpoints"),
+            ("non-fmt-panic", "non-fmt-panics"),
+            ("unused-tuple-struct-fields", "dead-code"),
+            ("static-mut-ref", "static-mut-refs"),
+        ],
+    ),
+    (Level::Deny, &[("exceeding-bitshifts", "arithmetic-overflow")]),
+];
+
 pub struct LintExtractor<'a> {
     /// Path to the `src` directory, where it will scan for `.rs` files to
     /// find lint declarations.
@@ -126,6 +163,7 @@
                 )
             })?;
         }
+        add_renamed_lints(&mut lints);
         self.save_lints_markdown(&lints)?;
         self.generate_group_docs(&lints)?;
         Ok(())
@@ -482,6 +520,7 @@
             }
             result.push('\n');
         }
+        add_rename_redirect(level, &mut result);
         let out_path = self.out_path.join("listing").join(level.doc_filename());
         // Delete the output because rustbuild uses hard links in its copies.
         let _ = fs::remove_file(&out_path);
@@ -491,6 +530,56 @@
     }
 }
 
+/// Adds `Lint`s that have been renamed.
+fn add_renamed_lints(lints: &mut Vec<Lint>) {
+    for (level, names) in RENAMES {
+        for (from, to) in *names {
+            lints.push(Lint {
+                name: from.to_string(),
+                doc: vec![format!("The lint `{from}` has been renamed to [`{to}`](#{to}).")],
+                level: *level,
+                path: PathBuf::new(),
+                lineno: 0,
+            });
+        }
+    }
+}
+
+// This uses DOMContentLoaded instead of running immediately because for some
+// reason on Firefox (124 of this writing) doesn't update the `target` CSS
+// selector if only the hash changes.
+static RENAME_START: &str = "
+<script>
+document.addEventListener(\"DOMContentLoaded\", (event) => {
+    var fragments = {
+";
+
+static RENAME_END: &str = "\
+    };
+    var target = fragments[window.location.hash];
+    if (target) {
+        var url = window.location.toString();
+        var base = url.substring(0, url.lastIndexOf('/'));
+        window.location.replace(base + \"/\" + target);
+    }
+});
+</script>
+";
+
+/// Adds the javascript redirection code to the given markdown output.
+fn add_rename_redirect(level: Level, output: &mut String) {
+    for (rename_level, names) in RENAMES {
+        if *rename_level == level {
+            let filename = level.doc_filename().replace(".md", ".html");
+            output.push_str(RENAME_START);
+            for (from, to) in *names {
+                write!(output, "        \"#{from}\": \"{filename}#{to}\",\n").unwrap();
+            }
+            output.push_str(RENAME_END);
+        }
+    }
+}
+
 /// Extracts the lint name (removing the visibility modifier, and checking validity).
 fn lint_name(line: &str) -> Result<String, &'static str> {
     // Skip over any potential `pub` visibility.
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 948f1ee..4254b9b 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -295,6 +295,16 @@
 Miri adds its own set of `-Z` flags, which are usually set via the `MIRIFLAGS`
 environment variable. We first document the most relevant and most commonly used flags:
 
+* `-Zmiri-address-reuse-rate=<rate>` changes the probability that a freed *non-stack* allocation
+  will be added to the pool for address reuse, and the probability that a new *non-stack* allocation
+  will be taken from the pool. Stack allocations never get added to or taken from the pool. The
+  default is `0.5`.
+* `-Zmiri-address-reuse-cross-thread-rate=<rate>` changes the probability that an allocation which
+  attempts to reuse a previously freed block of memory will also consider blocks freed by *other
+  threads*. The default is `0.1`, which means by default, in 90% of the cases where an address reuse
+  attempt is made, only addresses from the same thread will be considered. Reusing an address from
+  another thread induces synchronization between those threads, which can mask data races and weak
+  memory bugs.
 * `-Zmiri-compare-exchange-weak-failure-rate=<rate>` changes the failure rate of
   `compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail).
   You can change it to any value between `0.0` and `1.0`, where `1.0` means it
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 6ad8fba..a60acf4 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-23d47dba319331d4418827cfbb8c1af283497d3c
+c8d19a92aa9022eb690899cf6d54fd23cb6877e5
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index b498365..2bbb34c 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -13,8 +13,9 @@
 use rustc_span::Span;
 use rustc_target::abi::{Align, HasDataLayout, Size};
 
-use crate::*;
-use reuse_pool::ReusePool;
+use crate::{concurrency::VClock, *};
+
+use self::reuse_pool::ReusePool;
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum ProvenanceMode {
@@ -77,7 +78,7 @@
         GlobalStateInner {
             int_to_ptr_map: Vec::default(),
             base_addr: FxHashMap::default(),
-            reuse: ReusePool::new(),
+            reuse: ReusePool::new(config),
             exposed: FxHashSet::default(),
             next_base_addr: stack_addr,
             provenance_mode: config.provenance_mode,
@@ -144,7 +145,7 @@
     fn addr_from_alloc_id(
         &self,
         alloc_id: AllocId,
-        _kind: MemoryKind,
+        memory_kind: MemoryKind,
     ) -> InterpResult<'tcx, u64> {
         let ecx = self.eval_context_ref();
         let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
@@ -163,9 +164,18 @@
                 assert!(!matches!(kind, AllocKind::Dead));
 
                 // This allocation does not have a base address yet, pick or reuse one.
-                let base_addr = if let Some(reuse_addr) =
-                    global_state.reuse.take_addr(&mut *rng, size, align)
-                {
+                let base_addr = if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
+                    &mut *rng,
+                    size,
+                    align,
+                    memory_kind,
+                    ecx.get_active_thread(),
+                ) {
+                    if let Some(clock) = clock
+                        && let Some(data_race) = &ecx.machine.data_race
+                    {
+                        data_race.acquire_clock(&clock, ecx.get_active_thread());
+                    }
                     reuse_addr
                 } else {
                     // We have to pick a fresh address.
@@ -333,14 +343,11 @@
     }
 }
 
-impl GlobalStateInner {
-    pub fn free_alloc_id(
-        &mut self,
-        rng: &mut impl Rng,
-        dead_id: AllocId,
-        size: Size,
-        align: Align,
-    ) {
+impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
+    pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) {
+        let global_state = self.alloc_addresses.get_mut();
+        let rng = self.rng.get_mut();
+
         // We can *not* remove this from `base_addr`, since the interpreter design requires that we
         // be able to retrieve an AllocId + offset for any memory access *before* we check if the
         // access is valid. Specifically, `ptr_get_alloc` is called on each attempt at a memory
@@ -353,15 +360,25 @@
         // returns a dead allocation.
         // To avoid a linear scan we first look up the address in `base_addr`, and then find it in
         // `int_to_ptr_map`.
-        let addr = *self.base_addr.get(&dead_id).unwrap();
-        let pos = self.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap();
-        let removed = self.int_to_ptr_map.remove(pos);
+        let addr = *global_state.base_addr.get(&dead_id).unwrap();
+        let pos =
+            global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap();
+        let removed = global_state.int_to_ptr_map.remove(pos);
         assert_eq!(removed, (addr, dead_id)); // double-check that we removed the right thing
         // We can also remove it from `exposed`, since this allocation can anyway not be returned by
         // `alloc_id_from_addr` any more.
-        self.exposed.remove(&dead_id);
+        global_state.exposed.remove(&dead_id);
         // Also remember this address for future reuse.
-        self.reuse.add_addr(rng, addr, size, align)
+        let thread = self.threads.get_active_thread_id();
+        global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || {
+            if let Some(data_race) = &self.data_race {
+                data_race
+                    .release_clock(thread, self.threads.active_thread_ref().current_span())
+                    .clone()
+            } else {
+                VClock::default()
+            }
+        })
     }
 }
 
diff --git a/src/tools/miri/src/alloc_addresses/reuse_pool.rs b/src/tools/miri/src/alloc_addresses/reuse_pool.rs
index 8374d0e..77fc9f5 100644
--- a/src/tools/miri/src/alloc_addresses/reuse_pool.rs
+++ b/src/tools/miri/src/alloc_addresses/reuse_pool.rs
@@ -4,11 +4,9 @@
 
 use rustc_target::abi::{Align, Size};
 
-const MAX_POOL_SIZE: usize = 64;
+use crate::{concurrency::VClock, MemoryKind, MiriConfig, ThreadId};
 
-// Just use fair coins, until we have evidence that other numbers are better.
-const ADDR_REMEMBER_CHANCE: f64 = 0.5;
-const ADDR_TAKE_CHANCE: f64 = 0.5;
+const MAX_POOL_SIZE: usize = 64;
 
 /// The pool strikes a balance between exploring more possible executions and making it more likely
 /// to find bugs. The hypothesis is that bugs are more likely to occur when reuse happens for
@@ -16,20 +14,29 @@
 /// structure. Therefore we only reuse allocations when size and alignment match exactly.
 #[derive(Debug)]
 pub struct ReusePool {
+    address_reuse_rate: f64,
+    address_reuse_cross_thread_rate: f64,
     /// The i-th element in `pool` stores allocations of alignment `2^i`. We store these reusable
-    /// allocations as address-size pairs, the list must be sorted by the size.
+    /// allocations as address-size pairs, the list must be sorted by the size and then the thread ID.
     ///
     /// Each of these maps has at most MAX_POOL_SIZE elements, and since alignment is limited to
     /// less than 64 different possible value, that bounds the overall size of the pool.
-    pool: Vec<Vec<(u64, Size)>>,
+    ///
+    /// We also store the ID and the data-race clock of the thread that donated this pool element,
+    /// to ensure synchronization with the thread that picks up this address.
+    pool: Vec<Vec<(u64, Size, ThreadId, VClock)>>,
 }
 
 impl ReusePool {
-    pub fn new() -> Self {
-        ReusePool { pool: vec![] }
+    pub fn new(config: &MiriConfig) -> Self {
+        ReusePool {
+            address_reuse_rate: config.address_reuse_rate,
+            address_reuse_cross_thread_rate: config.address_reuse_cross_thread_rate,
+            pool: vec![],
+        }
     }
 
-    fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size)> {
+    fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size, ThreadId, VClock)> {
         let pool_idx: usize = align.bytes().trailing_zeros().try_into().unwrap();
         if self.pool.len() <= pool_idx {
             self.pool.resize(pool_idx + 1, Vec::new());
@@ -37,40 +44,73 @@
         &mut self.pool[pool_idx]
     }
 
-    pub fn add_addr(&mut self, rng: &mut impl Rng, addr: u64, size: Size, align: Align) {
+    pub fn add_addr(
+        &mut self,
+        rng: &mut impl Rng,
+        addr: u64,
+        size: Size,
+        align: Align,
+        kind: MemoryKind,
+        thread: ThreadId,
+        clock: impl FnOnce() -> VClock,
+    ) {
         // Let's see if we even want to remember this address.
-        if !rng.gen_bool(ADDR_REMEMBER_CHANCE) {
+        // We don't remember stack addresses: there's a lot of them (so the perf impact is big),
+        // and we only want to reuse stack slots within the same thread or else we'll add a lot of
+        // undesired synchronization.
+        if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) {
             return;
         }
+        let clock = clock();
         // Determine the pool to add this to, and where in the pool to put it.
         let subpool = self.subpool(align);
-        let pos = subpool.partition_point(|(_addr, other_size)| *other_size < size);
+        let pos = subpool.partition_point(|(_addr, other_size, other_thread, _)| {
+            (*other_size, *other_thread) < (size, thread)
+        });
         // Make sure the pool does not grow too big.
         if subpool.len() >= MAX_POOL_SIZE {
             // Pool full. Replace existing element, or last one if this would be even bigger.
             let clamped_pos = pos.min(subpool.len() - 1);
-            subpool[clamped_pos] = (addr, size);
+            subpool[clamped_pos] = (addr, size, thread, clock);
             return;
         }
         // Add address to pool, at the right position.
-        subpool.insert(pos, (addr, size));
+        subpool.insert(pos, (addr, size, thread, clock));
     }
 
-    pub fn take_addr(&mut self, rng: &mut impl Rng, size: Size, align: Align) -> Option<u64> {
-        // Determine whether we'll even attempt a reuse.
-        if !rng.gen_bool(ADDR_TAKE_CHANCE) {
+    /// Returns the address to use and optionally a clock we have to synchronize with.
+    pub fn take_addr(
+        &mut self,
+        rng: &mut impl Rng,
+        size: Size,
+        align: Align,
+        kind: MemoryKind,
+        thread: ThreadId,
+    ) -> Option<(u64, Option<VClock>)> {
+        // Determine whether we'll even attempt a reuse. As above, we don't do reuse for stack addresses.
+        if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) {
             return None;
         }
+        let cross_thread_reuse = rng.gen_bool(self.address_reuse_cross_thread_rate);
         // Determine the pool to take this from.
         let subpool = self.subpool(align);
         // Let's see if we can find something of the right size. We want to find the full range of
-        // such items, beginning with the first, so we can't use `binary_search_by_key`.
-        let begin = subpool.partition_point(|(_addr, other_size)| *other_size < size);
+        // such items, beginning with the first, so we can't use `binary_search_by_key`. If we do
+        // *not* want to consider other thread's allocations, we effectively use the lexicographic
+        // order on `(size, thread)`.
+        let begin = subpool.partition_point(|(_addr, other_size, other_thread, _)| {
+            *other_size < size
+                || (*other_size == size && !cross_thread_reuse && *other_thread < thread)
+        });
         let mut end = begin;
-        while let Some((_addr, other_size)) = subpool.get(end) {
+        while let Some((_addr, other_size, other_thread, _)) = subpool.get(end) {
             if *other_size != size {
                 break;
             }
+            if !cross_thread_reuse && *other_thread != thread {
+                // We entered the allocations of another thread.
+                break;
+            }
             end += 1;
         }
         if end == begin {
@@ -80,8 +120,10 @@
         // Pick a random element with the desired size.
         let idx = rng.gen_range(begin..end);
         // Remove it from the pool and return.
-        let (chosen_addr, chosen_size) = subpool.remove(idx);
+        let (chosen_addr, chosen_size, chosen_thread, clock) = subpool.remove(idx);
         debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0);
-        Some(chosen_addr)
+        debug_assert!(cross_thread_reuse || chosen_thread == thread);
+        // No synchronization needed if we reused from the current thread.
+        Some((chosen_addr, if chosen_thread == thread { None } else { Some(clock) }))
     }
 }
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 3f7a965..0070d1f 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -1,4 +1,3 @@
-#![feature(generic_nonzero)]
 #![feature(rustc_private, stmt_expr_attributes)]
 #![allow(
     clippy::manual_range_contains,
@@ -307,6 +306,15 @@
     input.split(',').map(str::parse::<T>).collect()
 }
 
+/// Parses the input as a float in the range from 0.0 to 1.0 (inclusive).
+fn parse_rate(input: &str) -> Result<f64, &'static str> {
+    match input.parse::<f64>() {
+        Ok(rate) if rate >= 0.0 && rate <= 1.0 => Ok(rate),
+        Ok(_) => Err("must be between `0.0` and `1.0`"),
+        Err(_) => Err("requires a `f64` between `0.0` and `1.0`"),
+    }
+}
+
 #[cfg(any(target_os = "linux", target_os = "macos"))]
 fn jemalloc_magic() {
     // These magic runes are copied from
@@ -499,14 +507,9 @@
         } else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") {
             miri_config.forwarded_env_vars.push(param.to_owned());
         } else if let Some(param) = arg.strip_prefix("-Zmiri-track-pointer-tag=") {
-            let ids: Vec<u64> = match parse_comma_list(param) {
-                Ok(ids) => ids,
-                Err(err) =>
-                    show_error!(
-                        "-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {}",
-                        err
-                    ),
-            };
+            let ids: Vec<u64> = parse_comma_list(param).unwrap_or_else(|err| {
+                show_error!("-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {err}")
+            });
             for id in ids.into_iter().map(miri::BorTag::new) {
                 if let Some(id) = id {
                     miri_config.tracked_pointer_tags.insert(id);
@@ -515,14 +518,9 @@
                 }
             }
         } else if let Some(param) = arg.strip_prefix("-Zmiri-track-call-id=") {
-            let ids: Vec<u64> = match parse_comma_list(param) {
-                Ok(ids) => ids,
-                Err(err) =>
-                    show_error!(
-                        "-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {}",
-                        err
-                    ),
-            };
+            let ids: Vec<u64> = parse_comma_list(param).unwrap_or_else(|err| {
+                show_error!("-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {err}")
+            });
             for id in ids.into_iter().map(miri::CallId::new) {
                 if let Some(id) = id {
                     miri_config.tracked_call_ids.insert(id);
@@ -531,56 +529,37 @@
                 }
             }
         } else if let Some(param) = arg.strip_prefix("-Zmiri-track-alloc-id=") {
-            let ids: Vec<miri::AllocId> = match parse_comma_list::<NonZero<u64>>(param) {
-                Ok(ids) => ids.into_iter().map(miri::AllocId).collect(),
-                Err(err) =>
-                    show_error!(
-                        "-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {}",
-                        err
-                    ),
-            };
-            miri_config.tracked_alloc_ids.extend(ids);
+            let ids = parse_comma_list::<NonZero<u64>>(param).unwrap_or_else(|err| {
+                show_error!("-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {err}")
+            });
+            miri_config.tracked_alloc_ids.extend(ids.into_iter().map(miri::AllocId));
         } else if arg == "-Zmiri-track-alloc-accesses" {
             miri_config.track_alloc_accesses = true;
+        } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-rate=") {
+            miri_config.address_reuse_rate = parse_rate(param)
+                .unwrap_or_else(|err| show_error!("-Zmiri-address-reuse-rate {err}"));
+        } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-cross-thread-rate=") {
+            miri_config.address_reuse_cross_thread_rate = parse_rate(param)
+                .unwrap_or_else(|err| show_error!("-Zmiri-address-reuse-cross-thread-rate {err}"));
         } else if let Some(param) = arg.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=") {
-            let rate = match param.parse::<f64>() {
-                Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
-                Ok(_) =>
-                    show_error!(
-                        "-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`"
-                    ),
-                Err(err) =>
-                    show_error!(
-                        "-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}",
-                        err
-                    ),
-            };
-            miri_config.cmpxchg_weak_failure_rate = rate;
+            miri_config.cmpxchg_weak_failure_rate = parse_rate(param).unwrap_or_else(|err| {
+                show_error!("-Zmiri-compare-exchange-weak-failure-rate {err}")
+            });
         } else if let Some(param) = arg.strip_prefix("-Zmiri-preemption-rate=") {
-            let rate = match param.parse::<f64>() {
-                Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
-                Ok(_) => show_error!("-Zmiri-preemption-rate must be between `0.0` and `1.0`"),
-                Err(err) =>
-                    show_error!(
-                        "-Zmiri-preemption-rate requires a `f64` between `0.0` and `1.0`: {}",
-                        err
-                    ),
-            };
-            miri_config.preemption_rate = rate;
+            miri_config.preemption_rate =
+                parse_rate(param).unwrap_or_else(|err| show_error!("-Zmiri-preemption-rate {err}"));
         } else if arg == "-Zmiri-report-progress" {
             // This makes it take a few seconds between progress reports on my laptop.
             miri_config.report_progress = Some(1_000_000);
         } else if let Some(param) = arg.strip_prefix("-Zmiri-report-progress=") {
-            let interval = match param.parse::<u32>() {
-                Ok(i) => i,
-                Err(err) => show_error!("-Zmiri-report-progress requires a `u32`: {}", err),
-            };
+            let interval = param.parse::<u32>().unwrap_or_else(|err| {
+                show_error!("-Zmiri-report-progress requires a `u32`: {}", err)
+            });
             miri_config.report_progress = Some(interval);
         } else if let Some(param) = arg.strip_prefix("-Zmiri-provenance-gc=") {
-            let interval = match param.parse::<u32>() {
-                Ok(i) => i,
-                Err(err) => show_error!("-Zmiri-provenance-gc requires a `u32`: {}", err),
-            };
+            let interval = param.parse::<u32>().unwrap_or_else(|err| {
+                show_error!("-Zmiri-provenance-gc requires a `u32`: {}", err)
+            });
             miri_config.gc_interval = interval;
         } else if let Some(param) = arg.strip_prefix("-Zmiri-measureme=") {
             miri_config.measureme_out = Some(param.to_string());
@@ -605,23 +584,20 @@
                 show_error!("-Zmiri-extern-so-file `{}` does not exist", filename);
             }
         } else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") {
-            let num_cpus = match param.parse::<u32>() {
-                Ok(i) => i,
-                Err(err) => show_error!("-Zmiri-num-cpus requires a `u32`: {}", err),
-            };
-
+            let num_cpus = param
+                .parse::<u32>()
+                .unwrap_or_else(|err| show_error!("-Zmiri-num-cpus requires a `u32`: {}", err));
             miri_config.num_cpus = num_cpus;
         } else if let Some(param) = arg.strip_prefix("-Zmiri-force-page-size=") {
-            let page_size = match param.parse::<u64>() {
-                Ok(i) =>
-                    if i.is_power_of_two() {
-                        i * 1024
-                    } else {
-                        show_error!("-Zmiri-force-page-size requires a power of 2: {}", i)
-                    },
-                Err(err) => show_error!("-Zmiri-force-page-size requires a `u64`: {}", err),
+            let page_size = param.parse::<u64>().unwrap_or_else(|err| {
+                show_error!("-Zmiri-force-page-size requires a `u64`: {}", err)
+            });
+            // Convert from kilobytes to bytes.
+            let page_size = if page_size.is_power_of_two() {
+                page_size * 1024
+            } else {
+                show_error!("-Zmiri-force-page-size requires a power of 2: {page_size}");
             };
-
             miri_config.page_size = Some(page_size);
         } else {
             // Forward to rustc.
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
index 6d4a5bd..cb677b8 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
@@ -438,7 +438,7 @@
             .machine
             .threads
             .all_stacks()
-            .flatten()
+            .flat_map(|(_id, stack)| stack)
             .map(|frame| {
                 frame.extra.borrow_tracker.as_ref().expect("we should have borrow tracking data")
             })
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/exhaustive.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/exhaustive.rs
index daf3590..d50a22a 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/exhaustive.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/exhaustive.rs
@@ -2,7 +2,6 @@
 //! (These are used in Tree Borrows `#[test]`s for thorough verification
 //! of the behavior of the state machine of permissions,
 //! but the contents of this file are extremely generic)
-#![cfg(test)]
 
 pub trait Exhaustive: Sized {
     fn exhaustive() -> Box<dyn Iterator<Item = Self>>;
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index 95049b9..2281609 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -547,9 +547,9 @@
     ) -> Result<(), DataRace> {
         trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks);
         if !current_span.is_dummy() {
-            thread_clocks.clock[index].span = current_span;
+            thread_clocks.clock.index_mut(index).span = current_span;
         }
-        thread_clocks.clock[index].set_read_type(read_type);
+        thread_clocks.clock.index_mut(index).set_read_type(read_type);
         if self.write_was_before(&thread_clocks.clock) {
             let race_free = if let Some(atomic) = self.atomic() {
                 // We must be ordered-after all atomic accesses, reads and writes.
@@ -577,7 +577,7 @@
     ) -> Result<(), DataRace> {
         trace!("Unsynchronized write with vectors: {:#?} :: {:#?}", self, thread_clocks);
         if !current_span.is_dummy() {
-            thread_clocks.clock[index].span = current_span;
+            thread_clocks.clock.index_mut(index).span = current_span;
         }
         if self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock {
             let race_free = if let Some(atomic) = self.atomic() {
@@ -1701,49 +1701,34 @@
         format!("thread `{thread_name}`")
     }
 
-    /// Acquire a lock, express that the previous call of
-    /// `validate_lock_release` must happen before this.
+    /// Acquire the given clock into the given thread, establishing synchronization with
+    /// the moment when that clock snapshot was taken via `release_clock`.
     /// As this is an acquire operation, the thread timestamp is not
     /// incremented.
-    pub fn validate_lock_acquire(&self, lock: &VClock, thread: ThreadId) {
-        let (_, mut clocks) = self.load_thread_state_mut(thread);
+    pub fn acquire_clock(&self, lock: &VClock, thread: ThreadId) {
+        let (_, mut clocks) = self.thread_state_mut(thread);
         clocks.clock.join(lock);
     }
 
-    /// Release a lock handle, express that this happens-before
-    /// any subsequent calls to `validate_lock_acquire`.
-    /// For normal locks this should be equivalent to `validate_lock_release_shared`
-    /// since an acquire operation should have occurred before, however
-    /// for futex & condvar operations this is not the case and this
-    /// operation must be used.
-    pub fn validate_lock_release(&self, lock: &mut VClock, thread: ThreadId, current_span: Span) {
-        let (index, mut clocks) = self.load_thread_state_mut(thread);
-        lock.clone_from(&clocks.clock);
+    /// Returns the `release` clock of the given thread.
+    /// Other threads can acquire this clock in the future to establish synchronization
+    /// with this program point.
+    pub fn release_clock(&self, thread: ThreadId, current_span: Span) -> Ref<'_, VClock> {
+        // We increment the clock each time this happens, to ensure no two releases
+        // can be confused with each other.
+        let (index, mut clocks) = self.thread_state_mut(thread);
         clocks.increment_clock(index, current_span);
-    }
-
-    /// Release a lock handle, express that this happens-before
-    /// any subsequent calls to `validate_lock_acquire` as well
-    /// as any previous calls to this function after any
-    /// `validate_lock_release` calls.
-    /// For normal locks this should be equivalent to `validate_lock_release`.
-    /// This function only exists for joining over the set of concurrent readers
-    /// in a read-write lock and should not be used for anything else.
-    pub fn validate_lock_release_shared(
-        &self,
-        lock: &mut VClock,
-        thread: ThreadId,
-        current_span: Span,
-    ) {
-        let (index, mut clocks) = self.load_thread_state_mut(thread);
-        lock.join(&clocks.clock);
-        clocks.increment_clock(index, current_span);
+        drop(clocks);
+        // To return a read-only view, we need to release the RefCell
+        // and borrow it again.
+        let (_index, clocks) = self.thread_state(thread);
+        Ref::map(clocks, |c| &c.clock)
     }
 
     /// Load the vector index used by the given thread as well as the set of vector clocks
     /// used by the thread.
     #[inline]
-    fn load_thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
+    fn thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
         let index = self.thread_info.borrow()[thread]
             .vector_index
             .expect("Loading thread state for thread with no assigned vector");
@@ -1752,6 +1737,18 @@
         (index, clocks)
     }
 
+    /// Load the vector index used by the given thread as well as the set of vector clocks
+    /// used by the thread.
+    #[inline]
+    fn thread_state(&self, thread: ThreadId) -> (VectorIdx, Ref<'_, ThreadClockSet>) {
+        let index = self.thread_info.borrow()[thread]
+            .vector_index
+            .expect("Loading thread state for thread with no assigned vector");
+        let ref_vector = self.vector_clocks.borrow();
+        let clocks = Ref::map(ref_vector, |vec| &vec[index]);
+        (index, clocks)
+    }
+
     /// Load the current vector clock in use and the current set of thread clocks
     /// in use for the vector.
     #[inline]
@@ -1759,10 +1756,7 @@
         &self,
         thread_mgr: &ThreadManager<'_, '_>,
     ) -> (VectorIdx, Ref<'_, ThreadClockSet>) {
-        let index = self.current_index(thread_mgr);
-        let ref_vector = self.vector_clocks.borrow();
-        let clocks = Ref::map(ref_vector, |vec| &vec[index]);
-        (index, clocks)
+        self.thread_state(thread_mgr.get_active_thread_id())
     }
 
     /// Load the current vector clock in use and the current set of thread clocks
@@ -1772,10 +1766,7 @@
         &self,
         thread_mgr: &ThreadManager<'_, '_>,
     ) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
-        let index = self.current_index(thread_mgr);
-        let ref_vector = self.vector_clocks.borrow_mut();
-        let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]);
-        (index, clocks)
+        self.thread_state_mut(thread_mgr.get_active_thread_id())
     }
 
     /// Return the current thread, should be the same
diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs
index 35dcfec..a01b59c 100644
--- a/src/tools/miri/src/concurrency/init_once.rs
+++ b/src/tools/miri/src/concurrency/init_once.rs
@@ -41,7 +41,7 @@
 pub(super) struct InitOnce<'mir, 'tcx> {
     status: InitOnceStatus,
     waiters: VecDeque<InitOnceWaiter<'mir, 'tcx>>,
-    data_race: VClock,
+    clock: VClock,
 }
 
 impl<'mir, 'tcx> VisitProvenance for InitOnce<'mir, 'tcx> {
@@ -61,10 +61,8 @@
         let current_thread = this.get_active_thread();
 
         if let Some(data_race) = &this.machine.data_race {
-            data_race.validate_lock_acquire(
-                &this.machine.threads.sync.init_onces[id].data_race,
-                current_thread,
-            );
+            data_race
+                .acquire_clock(&this.machine.threads.sync.init_onces[id].clock, current_thread);
         }
     }
 
@@ -77,7 +75,7 @@
         let this = self.eval_context_mut();
         let current_thread = this.get_active_thread();
 
-        this.unblock_thread(waiter.thread);
+        this.unblock_thread(waiter.thread, BlockReason::InitOnce(id));
 
         // Call callback, with the woken-up thread as `current`.
         this.set_active_thread(waiter.thread);
@@ -142,7 +140,7 @@
         let init_once = &mut this.machine.threads.sync.init_onces[id];
         assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once");
         init_once.waiters.push_back(InitOnceWaiter { thread, callback });
-        this.block_thread(thread);
+        this.block_thread(thread, BlockReason::InitOnce(id));
     }
 
     /// Begin initializing this InitOnce. Must only be called after checking that it is currently
@@ -176,7 +174,7 @@
 
         // Each complete happens-before the end of the wait
         if let Some(data_race) = &this.machine.data_race {
-            data_race.validate_lock_release(&mut init_once.data_race, current_thread, current_span);
+            init_once.clock.clone_from(&data_race.release_clock(current_thread, current_span));
         }
 
         // Wake up everyone.
@@ -202,7 +200,7 @@
 
         // Each complete happens-before the end of the wait
         if let Some(data_race) = &this.machine.data_race {
-            data_race.validate_lock_release(&mut init_once.data_race, current_thread, current_span);
+            init_once.clock.clone_from(&data_race.release_clock(current_thread, current_span));
         }
 
         // Wake up one waiting thread, so they can go ahead and try to init this.
diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs
index 4590310..15e1a94 100644
--- a/src/tools/miri/src/concurrency/mod.rs
+++ b/src/tools/miri/src/concurrency/mod.rs
@@ -6,3 +6,5 @@
 pub mod thread;
 mod vector_clock;
 pub mod weak_memory;
+
+pub use vector_clock::VClock;
diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs
index 956a02d..d3cef8b 100644
--- a/src/tools/miri/src/concurrency/sync.rs
+++ b/src/tools/miri/src/concurrency/sync.rs
@@ -69,12 +69,8 @@
     lock_count: usize,
     /// The queue of threads waiting for this mutex.
     queue: VecDeque<ThreadId>,
-    /// Data race handle. This tracks the happens-before
-    /// relationship between each mutex access. It is
-    /// released to during unlock and acquired from during
-    /// locking, and therefore stores the clock of the last
-    /// thread to release this mutex.
-    data_race: VClock,
+    /// Mutex clock. This tracks the moment of the last unlock.
+    clock: VClock,
 }
 
 declare_id!(RwLockId);
@@ -91,7 +87,7 @@
     writer_queue: VecDeque<ThreadId>,
     /// The queue of reader threads waiting for this lock.
     reader_queue: VecDeque<ThreadId>,
-    /// Data race handle for writers. Tracks the happens-before
+    /// Data race clock for writers. Tracks the happens-before
     /// ordering between each write access to a rwlock and is updated
     /// after a sequence of concurrent readers to track the happens-
     /// before ordering between the set of previous readers and
@@ -99,8 +95,8 @@
     /// Contains the clock of the last thread to release a writer
     /// lock or the joined clock of the set of last threads to release
     /// shared reader locks.
-    data_race: VClock,
-    /// Data race handle for readers. This is temporary storage
+    clock_unlocked: VClock,
+    /// Data race clock for readers. This is temporary storage
     /// for the combined happens-before ordering for between all
     /// concurrent readers and the next writer, and the value
     /// is stored to the main data_race variable once all
@@ -110,30 +106,18 @@
     /// add happens-before orderings between shared reader
     /// locks.
     /// This is only relevant when there is an active reader.
-    data_race_reader: VClock,
+    clock_current_readers: VClock,
 }
 
 declare_id!(CondvarId);
 
-#[derive(Debug, Copy, Clone)]
-pub enum RwLockMode {
-    Read,
-    Write,
-}
-
-#[derive(Debug)]
-pub enum CondvarLock {
-    Mutex(MutexId),
-    RwLock { id: RwLockId, mode: RwLockMode },
-}
-
 /// A thread waiting on a conditional variable.
 #[derive(Debug)]
 struct CondvarWaiter {
     /// The thread that is waiting on this variable.
     thread: ThreadId,
-    /// The mutex or rwlock on which the thread is waiting.
-    lock: CondvarLock,
+    /// The mutex on which the thread is waiting.
+    lock: MutexId,
 }
 
 /// The conditional variable state.
@@ -144,8 +128,8 @@
     /// between a cond-var signal and a cond-var
     /// wait during a non-spurious signal event.
     /// Contains the clock of the last thread to
-    /// perform a futex-signal.
-    data_race: VClock,
+    /// perform a condvar-signal.
+    clock: VClock,
 }
 
 /// The futex state.
@@ -157,7 +141,7 @@
     /// during a non-spurious wake event.
     /// Contains the clock of the last thread to
     /// perform a futex-wake.
-    data_race: VClock,
+    clock: VClock,
 }
 
 /// A thread waiting on a futex.
@@ -232,7 +216,7 @@
     fn rwlock_dequeue_and_lock_reader(&mut self, id: RwLockId) -> bool {
         let this = self.eval_context_mut();
         if let Some(reader) = this.machine.threads.sync.rwlocks[id].reader_queue.pop_front() {
-            this.unblock_thread(reader);
+            this.unblock_thread(reader, BlockReason::RwLock(id));
             this.rwlock_reader_lock(id, reader);
             true
         } else {
@@ -246,7 +230,7 @@
     fn rwlock_dequeue_and_lock_writer(&mut self, id: RwLockId) -> bool {
         let this = self.eval_context_mut();
         if let Some(writer) = this.machine.threads.sync.rwlocks[id].writer_queue.pop_front() {
-            this.unblock_thread(writer);
+            this.unblock_thread(writer, BlockReason::RwLock(id));
             this.rwlock_writer_lock(id, writer);
             true
         } else {
@@ -260,7 +244,7 @@
     fn mutex_dequeue_and_lock(&mut self, id: MutexId) -> bool {
         let this = self.eval_context_mut();
         if let Some(thread) = this.machine.threads.sync.mutexes[id].queue.pop_front() {
-            this.unblock_thread(thread);
+            this.unblock_thread(thread, BlockReason::Mutex(id));
             this.mutex_lock(id, thread);
             true
         } else {
@@ -358,7 +342,7 @@
         }
         mutex.lock_count = mutex.lock_count.checked_add(1).unwrap();
         if let Some(data_race) = &this.machine.data_race {
-            data_race.validate_lock_acquire(&mutex.data_race, thread);
+            data_race.acquire_clock(&mutex.clock, thread);
         }
     }
 
@@ -385,11 +369,7 @@
                 // The mutex is completely unlocked. Try transferring ownership
                 // to another thread.
                 if let Some(data_race) = &this.machine.data_race {
-                    data_race.validate_lock_release(
-                        &mut mutex.data_race,
-                        current_owner,
-                        current_span,
-                    );
+                    mutex.clock.clone_from(&data_race.release_clock(current_owner, current_span));
                 }
                 this.mutex_dequeue_and_lock(id);
             }
@@ -406,7 +386,7 @@
         let this = self.eval_context_mut();
         assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
         this.machine.threads.sync.mutexes[id].queue.push_back(thread);
-        this.block_thread(thread);
+        this.block_thread(thread, BlockReason::Mutex(id));
     }
 
     /// Provides the closure with the next RwLockId. Creates that RwLock if the closure returns None,
@@ -460,7 +440,7 @@
         let count = rwlock.readers.entry(reader).or_insert(0);
         *count = count.checked_add(1).expect("the reader counter overflowed");
         if let Some(data_race) = &this.machine.data_race {
-            data_race.validate_lock_acquire(&rwlock.data_race, reader);
+            data_race.acquire_clock(&rwlock.clock_unlocked, reader);
         }
     }
 
@@ -486,20 +466,16 @@
         }
         if let Some(data_race) = &this.machine.data_race {
             // Add this to the shared-release clock of all concurrent readers.
-            data_race.validate_lock_release_shared(
-                &mut rwlock.data_race_reader,
-                reader,
-                current_span,
-            );
+            rwlock.clock_current_readers.join(&data_race.release_clock(reader, current_span));
         }
 
         // The thread was a reader. If the lock is not held any more, give it to a writer.
         if this.rwlock_is_locked(id).not() {
             // All the readers are finished, so set the writer data-race handle to the value
-            //  of the union of all reader data race handles, since the set of readers
-            //  happen-before the writers
+            // of the union of all reader data race handles, since the set of readers
+            // happen-before the writers
             let rwlock = &mut this.machine.threads.sync.rwlocks[id];
-            rwlock.data_race.clone_from(&rwlock.data_race_reader);
+            rwlock.clock_unlocked.clone_from(&rwlock.clock_current_readers);
             this.rwlock_dequeue_and_lock_writer(id);
         }
         true
@@ -511,7 +487,7 @@
         let this = self.eval_context_mut();
         assert!(this.rwlock_is_write_locked(id), "read-queueing on not write locked rwlock");
         this.machine.threads.sync.rwlocks[id].reader_queue.push_back(reader);
-        this.block_thread(reader);
+        this.block_thread(reader, BlockReason::RwLock(id));
     }
 
     /// Lock by setting the writer that owns the lock.
@@ -523,7 +499,7 @@
         let rwlock = &mut this.machine.threads.sync.rwlocks[id];
         rwlock.writer = Some(writer);
         if let Some(data_race) = &this.machine.data_race {
-            data_race.validate_lock_acquire(&rwlock.data_race, writer);
+            data_race.acquire_clock(&rwlock.clock_unlocked, writer);
         }
     }
 
@@ -542,11 +518,9 @@
             trace!("rwlock_writer_unlock: {:?} unlocked by {:?}", id, expected_writer);
             // Release memory to next lock holder.
             if let Some(data_race) = &this.machine.data_race {
-                data_race.validate_lock_release(
-                    &mut rwlock.data_race,
-                    current_writer,
-                    current_span,
-                );
+                rwlock
+                    .clock_unlocked
+                    .clone_from(&*data_race.release_clock(current_writer, current_span));
             }
             // The thread was a writer.
             //
@@ -573,7 +547,7 @@
         let this = self.eval_context_mut();
         assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock");
         this.machine.threads.sync.rwlocks[id].writer_queue.push_back(writer);
-        this.block_thread(writer);
+        this.block_thread(writer, BlockReason::RwLock(id));
     }
 
     /// Provides the closure with the next CondvarId. Creates that Condvar if the closure returns None,
@@ -605,7 +579,7 @@
     }
 
     /// Mark that the thread is waiting on the conditional variable.
-    fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: CondvarLock) {
+    fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: MutexId) {
         let this = self.eval_context_mut();
         let waiters = &mut this.machine.threads.sync.condvars[id].waiters;
         assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
@@ -614,7 +588,7 @@
 
     /// Wake up some thread (if there is any) sleeping on the conditional
     /// variable.
-    fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, CondvarLock)> {
+    fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, MutexId)> {
         let this = self.eval_context_mut();
         let current_thread = this.get_active_thread();
         let current_span = this.machine.current_span();
@@ -623,11 +597,11 @@
 
         // Each condvar signal happens-before the end of the condvar wake
         if let Some(data_race) = data_race {
-            data_race.validate_lock_release(&mut condvar.data_race, current_thread, current_span);
+            condvar.clock.clone_from(&*data_race.release_clock(current_thread, current_span));
         }
         condvar.waiters.pop_front().map(|waiter| {
             if let Some(data_race) = data_race {
-                data_race.validate_lock_acquire(&condvar.data_race, waiter.thread);
+                data_race.acquire_clock(&condvar.clock, waiter.thread);
             }
             (waiter.thread, waiter.lock)
         })
@@ -657,14 +631,14 @@
 
         // Each futex-wake happens-before the end of the futex wait
         if let Some(data_race) = data_race {
-            data_race.validate_lock_release(&mut futex.data_race, current_thread, current_span);
+            futex.clock.clone_from(&*data_race.release_clock(current_thread, current_span));
         }
 
         // Wake up the first thread in the queue that matches any of the bits in the bitset.
         futex.waiters.iter().position(|w| w.bitset & bitset != 0).map(|i| {
             let waiter = futex.waiters.remove(i).unwrap();
             if let Some(data_race) = data_race {
-                data_race.validate_lock_acquire(&futex.data_race, waiter.thread);
+                data_race.acquire_clock(&futex.clock, waiter.thread);
             }
             waiter.thread
         })
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index d113627..2fabd39 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -88,18 +88,33 @@
     }
 }
 
+/// Keeps track of what the thread is blocked on.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum BlockReason {
+    /// The thread tried to join the specified thread and is blocked until that
+    /// thread terminates.
+    Join(ThreadId),
+    /// Waiting for time to pass.
+    Sleep,
+    /// Blocked on a mutex.
+    Mutex(MutexId),
+    /// Blocked on a condition variable.
+    Condvar(CondvarId),
+    /// Blocked on a reader-writer lock.
+    RwLock(RwLockId),
+    /// Blocled on a Futex variable.
+    Futex { addr: u64 },
+    /// Blocked on an InitOnce.
+    InitOnce(InitOnceId),
+}
+
 /// The state of a thread.
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum ThreadState {
     /// The thread is enabled and can be executed.
     Enabled,
-    /// The thread tried to join the specified thread and is blocked until that
-    /// thread terminates.
-    BlockedOnJoin(ThreadId),
-    /// The thread is blocked on some synchronization primitive. It is the
-    /// responsibility of the synchronization primitives to track threads that
-    /// are blocked by them.
-    BlockedOnSync,
+    /// The thread is blocked on something.
+    Blocked(BlockReason),
     /// The thread has terminated its execution. We do not delete terminated
     /// threads (FIXME: why?).
     Terminated,
@@ -208,6 +223,12 @@
         // empty stacks.
         self.top_user_relevant_frame.or_else(|| self.stack.len().checked_sub(1))
     }
+
+    pub fn current_span(&self) -> Span {
+        self.top_user_relevant_frame()
+            .map(|frame_idx| self.stack[frame_idx].current_span())
+            .unwrap_or(rustc_span::DUMMY_SP)
+    }
 }
 
 impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> {
@@ -296,17 +317,17 @@
 
 /// A specific moment in time.
 #[derive(Debug)]
-pub enum Time {
+pub enum CallbackTime {
     Monotonic(Instant),
     RealTime(SystemTime),
 }
 
-impl Time {
+impl CallbackTime {
     /// How long do we have to wait from now until the specified time?
     fn get_wait_time(&self, clock: &Clock) -> Duration {
         match self {
-            Time::Monotonic(instant) => instant.duration_since(clock.now()),
-            Time::RealTime(time) =>
+            CallbackTime::Monotonic(instant) => instant.duration_since(clock.now()),
+            CallbackTime::RealTime(time) =>
                 time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
         }
     }
@@ -318,7 +339,7 @@
 /// conditional variable, the signal handler deletes the callback.
 struct TimeoutCallbackInfo<'mir, 'tcx> {
     /// The callback should be called no earlier than this time.
-    call_time: Time,
+    call_time: CallbackTime,
     /// The called function.
     callback: TimeoutCallback<'mir, 'tcx>,
 }
@@ -430,11 +451,10 @@
     ) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
         &mut self.threads[self.active_thread].stack
     }
-
     pub fn all_stacks(
         &self,
-    ) -> impl Iterator<Item = &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>]> {
-        self.threads.iter().map(|t| &t.stack[..])
+    ) -> impl Iterator<Item = (ThreadId, &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>])> {
+        self.threads.iter_enumerated().map(|(id, t)| (id, &t.stack[..]))
     }
 
     /// Create a new thread and returns its id.
@@ -539,7 +559,8 @@
         self.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined;
         if self.threads[joined_thread_id].state != ThreadState::Terminated {
             // The joined thread is still running, we need to wait for it.
-            self.active_thread_mut().state = ThreadState::BlockedOnJoin(joined_thread_id);
+            self.active_thread_mut().state =
+                ThreadState::Blocked(BlockReason::Join(joined_thread_id));
             trace!(
                 "{:?} blocked on {:?} when trying to join",
                 self.active_thread,
@@ -569,10 +590,11 @@
             throw_ub_format!("trying to join itself");
         }
 
+        // Sanity check `join_status`.
         assert!(
-            self.threads
-                .iter()
-                .all(|thread| thread.state != ThreadState::BlockedOnJoin(joined_thread_id)),
+            self.threads.iter().all(|thread| {
+                thread.state != ThreadState::Blocked(BlockReason::Join(joined_thread_id))
+            }),
             "this thread already has threads waiting for its termination"
         );
 
@@ -594,16 +616,17 @@
     }
 
     /// Put the thread into the blocked state.
-    fn block_thread(&mut self, thread: ThreadId) {
+    fn block_thread(&mut self, thread: ThreadId, reason: BlockReason) {
         let state = &mut self.threads[thread].state;
         assert_eq!(*state, ThreadState::Enabled);
-        *state = ThreadState::BlockedOnSync;
+        *state = ThreadState::Blocked(reason);
     }
 
     /// Put the blocked thread into the enabled state.
-    fn unblock_thread(&mut self, thread: ThreadId) {
+    /// Sanity-checks that the thread previously was blocked for the right reason.
+    fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) {
         let state = &mut self.threads[thread].state;
-        assert_eq!(*state, ThreadState::BlockedOnSync);
+        assert_eq!(*state, ThreadState::Blocked(reason));
         *state = ThreadState::Enabled;
     }
 
@@ -622,7 +645,7 @@
     fn register_timeout_callback(
         &mut self,
         thread: ThreadId,
-        call_time: Time,
+        call_time: CallbackTime,
         callback: TimeoutCallback<'mir, 'tcx>,
     ) {
         self.timeout_callbacks
@@ -683,7 +706,7 @@
         // Check if we need to unblock any threads.
         let mut joined_threads = vec![]; // store which threads joined, we'll need it
         for (i, thread) in self.threads.iter_enumerated_mut() {
-            if thread.state == ThreadState::BlockedOnJoin(self.active_thread) {
+            if thread.state == ThreadState::Blocked(BlockReason::Join(self.active_thread)) {
                 // The thread has terminated, mark happens-before edge to joining thread
                 if data_race.is_some() {
                     joined_threads.push(i);
@@ -999,13 +1022,13 @@
     }
 
     #[inline]
-    fn block_thread(&mut self, thread: ThreadId) {
-        self.eval_context_mut().machine.threads.block_thread(thread);
+    fn block_thread(&mut self, thread: ThreadId, reason: BlockReason) {
+        self.eval_context_mut().machine.threads.block_thread(thread, reason);
     }
 
     #[inline]
-    fn unblock_thread(&mut self, thread: ThreadId) {
-        self.eval_context_mut().machine.threads.unblock_thread(thread);
+    fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) {
+        self.eval_context_mut().machine.threads.unblock_thread(thread, reason);
     }
 
     #[inline]
@@ -1027,11 +1050,11 @@
     fn register_timeout_callback(
         &mut self,
         thread: ThreadId,
-        call_time: Time,
+        call_time: CallbackTime,
         callback: TimeoutCallback<'mir, 'tcx>,
     ) {
         let this = self.eval_context_mut();
-        if !this.machine.communicate() && matches!(call_time, Time::RealTime(..)) {
+        if !this.machine.communicate() && matches!(call_time, CallbackTime::RealTime(..)) {
             panic!("cannot have `RealTime` callback with isolation enabled!")
         }
         this.machine.threads.register_timeout_callback(thread, call_time, callback);
diff --git a/src/tools/miri/src/concurrency/vector_clock.rs b/src/tools/miri/src/concurrency/vector_clock.rs
index fe71994..2cd3d03 100644
--- a/src/tools/miri/src/concurrency/vector_clock.rs
+++ b/src/tools/miri/src/concurrency/vector_clock.rs
@@ -4,7 +4,7 @@
 use std::{
     cmp::Ordering,
     fmt::Debug,
-    ops::{Index, IndexMut, Shr},
+    ops::{Index, Shr},
 };
 
 use super::data_race::NaReadType;
@@ -92,7 +92,7 @@
     }
 
     #[inline]
-    pub fn set_read_type(&mut self, read_type: NaReadType) {
+    pub(super) fn set_read_type(&mut self, read_type: NaReadType) {
         self.time_and_read_type = Self::encode_time_and_read_type(self.time(), read_type);
     }
 
@@ -138,7 +138,7 @@
 impl VClock {
     /// Create a new vector-clock containing all zeros except
     /// for a value at the given index
-    pub fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock {
+    pub(super) fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock {
         let len = index.index() + 1;
         let mut vec = smallvec::smallvec![VTimestamp::ZERO; len];
         vec[index.index()] = timestamp;
@@ -147,12 +147,18 @@
 
     /// Load the internal timestamp slice in the vector clock
     #[inline]
-    pub fn as_slice(&self) -> &[VTimestamp] {
+    pub(super) fn as_slice(&self) -> &[VTimestamp] {
+        debug_assert!(!self.0.last().is_some_and(|t| t.time() == 0));
         self.0.as_slice()
     }
 
+    #[inline]
+    pub(super) fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp {
+        self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap()
+    }
+
     /// Get a mutable slice to the internal vector with minimum `min_len`
-    /// elements, to preserve invariants this vector must modify
+    /// elements. To preserve invariants, the caller must modify
     /// the `min_len`-1 nth element to a non-zero value
     #[inline]
     fn get_mut_with_min_len(&mut self, min_len: usize) -> &mut [VTimestamp] {
@@ -166,7 +172,7 @@
     /// Increment the vector clock at a known index
     /// this will panic if the vector index overflows
     #[inline]
-    pub fn increment_index(&mut self, idx: VectorIdx, current_span: Span) {
+    pub(super) fn increment_index(&mut self, idx: VectorIdx, current_span: Span) {
         let idx = idx.index();
         let mut_slice = self.get_mut_with_min_len(idx + 1);
         let idx_ref = &mut mut_slice[idx];
@@ -190,28 +196,36 @@
         }
     }
 
-    /// Set the element at the current index of the vector
-    pub fn set_at_index(&mut self, other: &Self, idx: VectorIdx) {
+    /// Set the element at the current index of the vector. May only increase elements.
+    pub(super) fn set_at_index(&mut self, other: &Self, idx: VectorIdx) {
+        let new_timestamp = other[idx];
+        // Setting to 0 is different, since the last element cannot be 0.
+        if new_timestamp.time() == 0 {
+            if idx.index() >= self.0.len() {
+                // This index does not even exist yet in our clock. Just do nothing.
+                return;
+            }
+            // This changes an existing element. Since it can only increase, that
+            // can never make the last element 0.
+        }
+
         let mut_slice = self.get_mut_with_min_len(idx.index() + 1);
+        let mut_timestamp = &mut mut_slice[idx.index()];
 
-        let prev_span = mut_slice[idx.index()].span;
+        let prev_span = mut_timestamp.span;
 
-        mut_slice[idx.index()] = other[idx];
+        assert!(*mut_timestamp <= new_timestamp, "set_at_index: may only increase the timestamp");
+        *mut_timestamp = new_timestamp;
 
-        let span = &mut mut_slice[idx.index()].span;
+        let span = &mut mut_timestamp.span;
         *span = span.substitute_dummy(prev_span);
     }
 
     /// Set the vector to the all-zero vector
     #[inline]
-    pub fn set_zero_vector(&mut self) {
+    pub(super) fn set_zero_vector(&mut self) {
         self.0.clear();
     }
-
-    /// Return if this vector is the all-zero vector
-    pub fn is_zero_vector(&self) -> bool {
-        self.0.is_empty()
-    }
 }
 
 impl Clone for VClock {
@@ -407,13 +421,6 @@
     }
 }
 
-impl IndexMut<VectorIdx> for VClock {
-    #[inline]
-    fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp {
-        self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap()
-    }
-}
-
 /// Test vector clock ordering operations
 ///  data-race detection is tested in the external
 ///  test suite
@@ -553,4 +560,15 @@
             "Invalid alt (>=):\n l: {l:?}\n r: {r:?}"
         );
     }
+
+    #[test]
+    fn set_index_to_0() {
+        let mut clock1 = from_slice(&[0, 1, 2, 3]);
+        let clock2 = from_slice(&[0, 2, 3, 4, 0, 5]);
+        // Naively, this would extend clock1 with a new index and set it to 0, making
+        // the last index 0. Make sure that does not happen.
+        clock1.set_at_index(&clock2, VectorIdx(4));
+        // This must not have made the last element 0.
+        assert!(clock1.0.last().unwrap().time() != 0);
+    }
 }
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index a2b817e..0c0ac4c 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -291,7 +291,7 @@
                     ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer
                 ) =>
             {
-                ecx.handle_ice(); // print interpreter backtrace
+                ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`)
                 bug!(
                     "This validation error should be impossible in Miri: {}",
                     format_interp_error(ecx.tcx.dcx(), e)
@@ -308,7 +308,7 @@
                 InvalidProgramInfo::AlreadyReported(_) | InvalidProgramInfo::Layout(..),
             ) => "post-monomorphization error",
             _ => {
-                ecx.handle_ice(); // print interpreter backtrace
+                ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`)
                 bug!(
                     "This error should be impossible in Miri: {}",
                     format_interp_error(ecx.tcx.dcx(), e)
@@ -361,9 +361,12 @@
     };
 
     let stacktrace = ecx.generate_stacktrace();
-    let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
+    let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
 
-    // We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early.
+    let mut show_all_threads = false;
+
+    // We want to dump the allocation if this is `InvalidUninitBytes`.
+    // Since `format_interp_error` consumes `e`, we compute the outut early.
     let mut extra = String::new();
     match e.kind() {
         UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
@@ -375,6 +378,15 @@
             .unwrap();
             writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap();
         }
+        MachineStop(info) => {
+            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
+            match info {
+                TerminationInfo::Deadlock => {
+                    show_all_threads = true;
+                }
+                _ => {}
+            }
+        }
         _ => {}
     }
 
@@ -387,18 +399,39 @@
         vec![],
         helps,
         &stacktrace,
+        Some(ecx.get_active_thread()),
         &ecx.machine,
     );
 
+    eprint!("{extra}"); // newlines are already in the string
+
+    if show_all_threads {
+        for (thread, stack) in ecx.machine.threads.all_stacks() {
+            if thread != ecx.get_active_thread() {
+                let stacktrace = Frame::generate_stacktrace_from_stack(stack);
+                let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
+                any_pruned |= was_pruned;
+                report_msg(
+                    DiagLevel::Error,
+                    format!("deadlock: the evaluated program deadlocked"),
+                    vec![format!("the evaluated program deadlocked")],
+                    vec![],
+                    vec![],
+                    &stacktrace,
+                    Some(thread),
+                    &ecx.machine,
+                )
+            }
+        }
+    }
+
     // Include a note like `std` does when we omit frames from a backtrace
-    if was_pruned {
+    if any_pruned {
         ecx.tcx.dcx().note(
             "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
         );
     }
 
-    eprint!("{extra}"); // newlines are already in the string
-
     // Debug-dump all locals.
     for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
         trace!("-------------------");
@@ -435,6 +468,7 @@
             vec![],
             vec![],
             &backtrace,
+            None, // we don't know the thread this is from
             &ecx.machine,
         );
     }
@@ -457,6 +491,7 @@
     notes: Vec<(Option<SpanData>, String)>,
     helps: Vec<(Option<SpanData>, String)>,
     stacktrace: &[FrameInfo<'tcx>],
+    thread: Option<ThreadId>,
     machine: &MiriMachine<'_, 'tcx>,
 ) {
     let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
@@ -506,12 +541,13 @@
     if extra_span {
         write!(backtrace_title, " (of the first span)").unwrap();
     }
-    let thread_name =
-        machine.threads.get_thread_display_name(machine.threads.get_active_thread_id());
-    if thread_name != "main" {
-        // Only print thread name if it is not `main`.
-        write!(backtrace_title, " on thread `{thread_name}`").unwrap();
-    };
+    if let Some(thread) = thread {
+        let thread_name = machine.threads.get_thread_display_name(thread);
+        if thread_name != "main" {
+            // Only print thread name if it is not `main`.
+            write!(backtrace_title, " on thread `{thread_name}`").unwrap();
+        };
+    }
     write!(backtrace_title, ":").unwrap();
     err.note(backtrace_title);
     for (idx, frame_info) in stacktrace.iter().enumerate() {
@@ -628,7 +664,16 @@
             _ => vec![],
         };
 
-        report_msg(diag_level, title, vec![msg], notes, helps, &stacktrace, self);
+        report_msg(
+            diag_level,
+            title,
+            vec![msg],
+            notes,
+            helps,
+            &stacktrace,
+            Some(self.threads.get_active_thread_id()),
+            self,
+        );
     }
 }
 
@@ -654,6 +699,7 @@
             vec![],
             vec![],
             &stacktrace,
+            Some(this.get_active_thread()),
             &this.machine,
         );
     }
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index df0ede1..45dadb5 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -150,6 +150,10 @@
     pub page_size: Option<u64>,
     /// Whether to collect a backtrace when each allocation is created, just in case it leaks.
     pub collect_leak_backtraces: bool,
+    /// Probability for address reuse.
+    pub address_reuse_rate: f64,
+    /// Probability for address reuse across threads.
+    pub address_reuse_cross_thread_rate: f64,
 }
 
 impl Default for MiriConfig {
@@ -186,6 +190,8 @@
             num_cpus: 1,
             page_size: None,
             collect_leak_backtraces: true,
+            address_reuse_rate: 0.5,
+            address_reuse_cross_thread_rate: 0.1,
         }
     }
 }
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index e2c6769..92bdaf3 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -912,10 +912,25 @@
         })
     }
 
+    /// Read bytes from a byte slice.
+    fn read_byte_slice<'a>(
+        &'a self,
+        slice: &ImmTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, &'a [u8]>
+    where
+        'mir: 'a,
+    {
+        let this = self.eval_context_ref();
+        let (ptr, len) = slice.to_scalar_pair();
+        let ptr = ptr.to_pointer(this)?;
+        let len = len.to_target_usize(this)?;
+        let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
+        Ok(bytes)
+    }
+
     /// Read a sequence of bytes until the first null terminator.
     fn read_c_str<'a>(&'a self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, &'a [u8]>
     where
-        'tcx: 'a,
         'mir: 'a,
     {
         let this = self.eval_context_ref();
@@ -1265,9 +1280,7 @@
     /// This function is backed by a cache, and can be assumed to be very fast.
     /// It will work even when the stack is empty.
     pub fn current_span(&self) -> Span {
-        self.top_user_relevant_frame()
-            .map(|frame_idx| self.stack()[frame_idx].current_span())
-            .unwrap_or(rustc_span::DUMMY_SP)
+        self.threads.active_thread_ref().current_span()
     }
 
     /// Returns the span of the *caller* of the current operation, again
@@ -1279,7 +1292,7 @@
         // We need to go down at least to the caller (len - 2), or however
         // far we have to go to find a frame in a local crate which is also not #[track_caller].
         let frame_idx = self.top_user_relevant_frame().unwrap();
-        let frame_idx = cmp::min(frame_idx, self.stack().len().checked_sub(2).unwrap());
+        let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
         self.stack()[frame_idx].current_span()
     }
 
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 390340d..e1c0da9 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -2,7 +2,6 @@
 #![feature(cell_update)]
 #![feature(const_option)]
 #![feature(float_gamma)]
-#![feature(generic_nonzero)]
 #![feature(map_try_insert)]
 #![feature(never_type)]
 #![feature(try_blocks)]
@@ -116,7 +115,9 @@
     data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
     init_once::{EvalContextExt as _, InitOnceId},
     sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SyncId},
-    thread::{EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, Time},
+    thread::{
+        BlockReason, CallbackTime, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager,
+    },
 };
 pub use crate::diagnostics::{
     report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo,
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 0bfc59e..cbe70cb 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -241,12 +241,12 @@
     Wildcard,
 }
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(Pointer<Provenance>, 24);
 // FIXME: this would with in 24bytes but layout optimizations are not smart enough
-// #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+// #[cfg(target_pointer_width = "64")]
 //static_assert_size!(Pointer<Option<Provenance>>, 24);
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(target_pointer_width = "64")]
 static_assert_size!(Scalar<Provenance>, 32);
 
 impl fmt::Debug for Provenance {
@@ -1282,7 +1282,7 @@
         (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
         size: Size,
         align: Align,
-        _kind: MemoryKind,
+        kind: MemoryKind,
     ) -> InterpResult<'tcx> {
         if machine.tracked_alloc_ids.contains(&alloc_id) {
             machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
@@ -1303,12 +1303,7 @@
         {
             *deallocated_at = Some(machine.current_span());
         }
-        machine.alloc_addresses.get_mut().free_alloc_id(
-            machine.rng.get_mut(),
-            alloc_id,
-            size,
-            align,
-        );
+        machine.free_alloc_id(alloc_id, size, align, kind);
         Ok(())
     }
 
diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs
new file mode 100644
index 0000000..b5ae06c
--- /dev/null
+++ b/src/tools/miri/src/shims/alloc.rs
@@ -0,0 +1,152 @@
+use std::iter;
+
+use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_target::abi::{Align, Size};
+
+use crate::*;
+use shims::foreign_items::EmulateForeignItemResult;
+
+/// Check some basic requirements for this allocation request:
+/// non-zero size, power-of-two alignment.
+pub(super) fn check_alloc_request<'tcx>(size: u64, align: u64) -> InterpResult<'tcx> {
+    if size == 0 {
+        throw_ub_format!("creating allocation with size 0");
+    }
+    if !align.is_power_of_two() {
+        throw_ub_format!("creating allocation with non-power-of-two alignment {}", align);
+    }
+    Ok(())
+}
+
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+    /// Returns the minimum alignment for the target architecture for allocations of the given size.
+    fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {
+        let this = self.eval_context_ref();
+        // List taken from `library/std/src/sys/pal/common/alloc.rs`.
+        // This list should be kept in sync with the one from libstd.
+        let min_align = match this.tcx.sess.target.arch.as_ref() {
+            "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8,
+            "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" =>
+                16,
+            arch => bug!("unsupported target architecture for malloc: `{}`", arch),
+        };
+        // Windows always aligns, even small allocations.
+        // Source: <https://support.microsoft.com/en-us/help/286470/how-to-use-pageheap-exe-in-windows-xp-windows-2000-and-windows-server>
+        // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big.
+        if kind == MiriMemoryKind::WinHeap || size >= min_align {
+            return Align::from_bytes(min_align).unwrap();
+        }
+        // We have `size < min_align`. Round `size` *down* to the next power of two and use that.
+        fn prev_power_of_two(x: u64) -> u64 {
+            let next_pow2 = x.next_power_of_two();
+            if next_pow2 == x {
+                // x *is* a power of two, just use that.
+                x
+            } else {
+                // x is between two powers, so next = 2*prev.
+                next_pow2 / 2
+            }
+        }
+        Align::from_bytes(prev_power_of_two(size)).unwrap()
+    }
+
+    /// Emulates calling the internal __rust_* allocator functions
+    fn emulate_allocator(
+        &mut self,
+        default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>,
+    ) -> InterpResult<'tcx, EmulateForeignItemResult> {
+        let this = self.eval_context_mut();
+
+        let Some(allocator_kind) = this.tcx.allocator_kind(()) else {
+            // in real code, this symbol does not exist without an allocator
+            return Ok(EmulateForeignItemResult::NotSupported);
+        };
+
+        match allocator_kind {
+            AllocatorKind::Global => {
+                // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion
+                // of this attribute. As such we have to call an exported Rust function,
+                // and not execute any Miri shim. Somewhat unintuitively doing so is done
+                // by returning `NotSupported`, which triggers the `lookup_exported_symbol`
+                // fallback case in `emulate_foreign_item`.
+                return Ok(EmulateForeignItemResult::NotSupported);
+            }
+            AllocatorKind::Default => {
+                default(this)?;
+                Ok(EmulateForeignItemResult::NeedsJumping)
+            }
+        }
+    }
+
+    fn malloc(
+        &mut self,
+        size: u64,
+        zero_init: bool,
+        kind: MiriMemoryKind,
+    ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
+        let this = self.eval_context_mut();
+        if size == 0 {
+            Ok(Pointer::null())
+        } else {
+            let align = this.min_align(size, kind);
+            let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?;
+            if zero_init {
+                // We just allocated this, the access is definitely in-bounds and fits into our address space.
+                this.write_bytes_ptr(
+                    ptr.into(),
+                    iter::repeat(0u8).take(usize::try_from(size).unwrap()),
+                )
+                .unwrap();
+            }
+            Ok(ptr.into())
+        }
+    }
+
+    fn free(
+        &mut self,
+        ptr: Pointer<Option<Provenance>>,
+        kind: MiriMemoryKind,
+    ) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+        if !this.ptr_is_null(ptr)? {
+            this.deallocate_ptr(ptr, None, kind.into())?;
+        }
+        Ok(())
+    }
+
+    fn realloc(
+        &mut self,
+        old_ptr: Pointer<Option<Provenance>>,
+        new_size: u64,
+        kind: MiriMemoryKind,
+    ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
+        let this = self.eval_context_mut();
+        let new_align = this.min_align(new_size, kind);
+        if this.ptr_is_null(old_ptr)? {
+            // Here we must behave like `malloc`.
+            if new_size == 0 {
+                Ok(Pointer::null())
+            } else {
+                let new_ptr =
+                    this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?;
+                Ok(new_ptr.into())
+            }
+        } else {
+            if new_size == 0 {
+                // C, in their infinite wisdom, made this UB.
+                // <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
+                throw_ub_format!("`realloc` with a size of zero");
+            } else {
+                let new_ptr = this.reallocate_ptr(
+                    old_ptr,
+                    None,
+                    Size::from_bytes(new_size),
+                    new_align,
+                    kind.into(),
+                )?;
+                Ok(new_ptr.into())
+            }
+        }
+    }
+}
diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs
index 0284e5b..7c4a54f 100644
--- a/src/tools/miri/src/shims/extern_static.rs
+++ b/src/tools/miri/src/shims/extern_static.rs
@@ -32,9 +32,14 @@
     /// Sets up the "extern statics" for this machine.
     pub fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
         // "__rust_no_alloc_shim_is_unstable"
-        let val = ImmTy::from_int(0, this.machine.layouts.u8);
+        let val = ImmTy::from_int(0, this.machine.layouts.u8); // always 0, value does not matter
         Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?;
 
+        // "__rust_alloc_error_handler_should_panic"
+        let val = this.tcx.sess.opts.unstable_opts.oom.should_panic();
+        let val = ImmTy::from_int(val, this.machine.layouts.u8);
+        Self::alloc_extern_static(this, "__rust_alloc_error_handler_should_panic", val)?;
+
         match this.tcx.sess.target.os.as_ref() {
             "linux" => {
                 Self::null_ptr_extern_statics(
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index 6b0797f..6363611 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -1,7 +1,7 @@
 use std::{collections::hash_map::Entry, io::Write, iter, path::Path};
 
 use rustc_apfloat::Float;
-use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_ast::expand::allocator::alloc_error_handler_name;
 use rustc_hir::{def::DefKind, def_id::CrateNum};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir;
@@ -12,6 +12,7 @@
     spec::abi::Abi,
 };
 
+use super::alloc::{check_alloc_request, EvalContextExt as _};
 use super::backtrace::EvalContextExt as _;
 use crate::*;
 use helpers::{ToHost, ToSoft};
@@ -80,6 +81,20 @@
                             panic_impl_instance,
                         )));
                     }
+                    "__rust_alloc_error_handler" => {
+                        // Forward to the right symbol that implements this function.
+                        let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else {
+                            // in real code, this symbol does not exist without an allocator
+                            throw_unsup_format!(
+                                "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set"
+                            );
+                        };
+                        let name = alloc_error_handler_name(handler_kind);
+                        let handler = this
+                            .lookup_exported_symbol(Symbol::intern(name))?
+                            .expect("missing alloc error handler symbol");
+                        return Ok(Some(handler));
+                    }
                     #[rustfmt::skip]
                     | "exit"
                     | "ExitProcess"
@@ -218,151 +233,10 @@
             Some(instance) => Ok(Some((this.load_mir(instance.def, None)?, instance))),
         }
     }
-
-    fn malloc(
-        &mut self,
-        size: u64,
-        zero_init: bool,
-        kind: MiriMemoryKind,
-    ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
-        let this = self.eval_context_mut();
-        if size == 0 {
-            Ok(Pointer::null())
-        } else {
-            let align = this.min_align(size, kind);
-            let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?;
-            if zero_init {
-                // We just allocated this, the access is definitely in-bounds and fits into our address space.
-                this.write_bytes_ptr(
-                    ptr.into(),
-                    iter::repeat(0u8).take(usize::try_from(size).unwrap()),
-                )
-                .unwrap();
-            }
-            Ok(ptr.into())
-        }
-    }
-
-    fn free(
-        &mut self,
-        ptr: Pointer<Option<Provenance>>,
-        kind: MiriMemoryKind,
-    ) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
-        if !this.ptr_is_null(ptr)? {
-            this.deallocate_ptr(ptr, None, kind.into())?;
-        }
-        Ok(())
-    }
-
-    fn realloc(
-        &mut self,
-        old_ptr: Pointer<Option<Provenance>>,
-        new_size: u64,
-        kind: MiriMemoryKind,
-    ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
-        let this = self.eval_context_mut();
-        let new_align = this.min_align(new_size, kind);
-        if this.ptr_is_null(old_ptr)? {
-            if new_size == 0 {
-                Ok(Pointer::null())
-            } else {
-                let new_ptr =
-                    this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?;
-                Ok(new_ptr.into())
-            }
-        } else {
-            if new_size == 0 {
-                this.deallocate_ptr(old_ptr, None, kind.into())?;
-                Ok(Pointer::null())
-            } else {
-                let new_ptr = this.reallocate_ptr(
-                    old_ptr,
-                    None,
-                    Size::from_bytes(new_size),
-                    new_align,
-                    kind.into(),
-                )?;
-                Ok(new_ptr.into())
-            }
-        }
-    }
 }
 
 impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
 trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
-    /// Read bytes from a `(ptr, len)` argument
-    fn read_byte_slice<'i>(&'i self, bytes: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, &'i [u8]>
-    where
-        'mir: 'i,
-    {
-        let this = self.eval_context_ref();
-        let (ptr, len) = this.read_immediate(bytes)?.to_scalar_pair();
-        let ptr = ptr.to_pointer(this)?;
-        let len = len.to_target_usize(this)?;
-        let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
-        Ok(bytes)
-    }
-
-    /// Returns the minimum alignment for the target architecture for allocations of the given size.
-    fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {
-        let this = self.eval_context_ref();
-        // List taken from `library/std/src/sys/pal/common/alloc.rs`.
-        // This list should be kept in sync with the one from libstd.
-        let min_align = match this.tcx.sess.target.arch.as_ref() {
-            "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8,
-            "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" =>
-                16,
-            arch => bug!("unsupported target architecture for malloc: `{}`", arch),
-        };
-        // Windows always aligns, even small allocations.
-        // Source: <https://support.microsoft.com/en-us/help/286470/how-to-use-pageheap-exe-in-windows-xp-windows-2000-and-windows-server>
-        // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big.
-        if kind == MiriMemoryKind::WinHeap || size >= min_align {
-            return Align::from_bytes(min_align).unwrap();
-        }
-        // We have `size < min_align`. Round `size` *down* to the next power of two and use that.
-        fn prev_power_of_two(x: u64) -> u64 {
-            let next_pow2 = x.next_power_of_two();
-            if next_pow2 == x {
-                // x *is* a power of two, just use that.
-                x
-            } else {
-                // x is between two powers, so next = 2*prev.
-                next_pow2 / 2
-            }
-        }
-        Align::from_bytes(prev_power_of_two(size)).unwrap()
-    }
-
-    /// Emulates calling the internal __rust_* allocator functions
-    fn emulate_allocator(
-        &mut self,
-        default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>,
-    ) -> InterpResult<'tcx, EmulateForeignItemResult> {
-        let this = self.eval_context_mut();
-
-        let Some(allocator_kind) = this.tcx.allocator_kind(()) else {
-            // in real code, this symbol does not exist without an allocator
-            return Ok(EmulateForeignItemResult::NotSupported);
-        };
-
-        match allocator_kind {
-            AllocatorKind::Global => {
-                // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion
-                // of this attribute. As such we have to call an exported Rust function,
-                // and not execute any Miri shim. Somewhat unintuitively doing so is done
-                // by returning `NotSupported`, which triggers the `lookup_exported_symbol`
-                // fallback case in `emulate_foreign_item`.
-                return Ok(EmulateForeignItemResult::NotSupported);
-            }
-            AllocatorKind::Default => {
-                default(this)?;
-                Ok(EmulateForeignItemResult::NeedsJumping)
-            }
-        }
-    }
-
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
@@ -452,7 +326,9 @@
                 let [ptr, nth_parent, name] = this.check_shim(abi, Abi::Rust, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
-                let name = this.read_byte_slice(name)?;
+                let name = this.read_immediate(name)?;
+
+                let name = this.read_byte_slice(&name)?;
                 // We must make `name` owned because we need to
                 // end the shared borrow from `read_byte_slice` before we can
                 // start the mutable borrow for `give_pointer_debug_name`.
@@ -513,7 +389,8 @@
             // README for details.
             "miri_write_to_stdout" | "miri_write_to_stderr" => {
                 let [msg] = this.check_shim(abi, Abi::Rust, link_name, args)?;
-                let msg = this.read_byte_slice(msg)?;
+                let msg = this.read_immediate(msg)?;
+                let msg = this.read_byte_slice(&msg)?;
                 // Note: we're ignoring errors writing to host stdout/stderr.
                 let _ignore = match link_name.as_str() {
                     "miri_write_to_stdout" => std::io::stdout().write_all(msg),
@@ -606,7 +483,7 @@
                     let size = this.read_target_usize(size)?;
                     let align = this.read_target_usize(align)?;
 
-                    Self::check_alloc_request(size, align)?;
+                    check_alloc_request(size, align)?;
 
                     let memory_kind = match link_name.as_str() {
                         "__rust_alloc" => MiriMemoryKind::Rust,
@@ -640,7 +517,7 @@
                     let size = this.read_target_usize(size)?;
                     let align = this.read_target_usize(align)?;
 
-                    Self::check_alloc_request(size, align)?;
+                    check_alloc_request(size, align)?;
 
                     let ptr = this.allocate_ptr(
                         Size::from_bytes(size),
@@ -704,7 +581,7 @@
                     let new_size = this.read_target_usize(new_size)?;
                     // No need to check old_size; we anyway check that they match the allocation.
 
-                    Self::check_alloc_request(new_size, align)?;
+                    check_alloc_request(new_size, align)?;
 
                     let align = Align::from_bytes(align).unwrap();
                     let new_ptr = this.reallocate_ptr(
@@ -1096,16 +973,4 @@
         // i.e., if we actually emulated the function with one of the shims.
         Ok(EmulateForeignItemResult::NeedsJumping)
     }
-
-    /// Check some basic requirements for this allocation request:
-    /// non-zero size, power-of-two alignment.
-    fn check_alloc_request(size: u64, align: u64) -> InterpResult<'tcx> {
-        if size == 0 {
-            throw_ub_format!("creating allocation with size 0");
-        }
-        if !align.is_power_of_two() {
-            throw_ub_format!("creating allocation with non-power-of-two alignment {}", align);
-        }
-        Ok(())
-    }
 }
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index ea6120f..85c9a20 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -1,5 +1,6 @@
 #![warn(clippy::arithmetic_side_effects)]
 
+mod alloc;
 mod backtrace;
 #[cfg(target_os = "linux")]
 pub mod ffi_support;
diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs
index 0409e31..3e8c35d 100644
--- a/src/tools/miri/src/shims/os_str.rs
+++ b/src/tools/miri/src/shims/os_str.rs
@@ -251,7 +251,6 @@
         this.alloc_os_str_as_wide_str(&os_str, memkind)
     }
 
-    #[allow(clippy::get_first)]
     fn convert_path<'a>(
         &self,
         os_str: Cow<'a, OsStr>,
@@ -260,100 +259,97 @@
         let this = self.eval_context_ref();
         let target_os = &this.tcx.sess.target.os;
 
+        /// Adjust a Windows path to Unix conventions such that it un-does everything that
+        /// `unix_to_windows` did, and such that if the Windows input path was absolute, then the
+        /// Unix output path is absolute.
+        fn windows_to_unix<T>(path: &mut Vec<T>)
+        where
+            T: From<u8> + Copy + Eq,
+        {
+            let sep = T::from(b'/');
+            // Make sure all path separators are `/`.
+            for c in path.iter_mut() {
+                if *c == b'\\'.into() {
+                    *c = sep;
+                }
+            }
+            // If this starts with `//?/`, it was probably produced by `unix_to_windows`` and we
+            // remove the `//?` that got added to get the Unix path back out.
+            if path.get(0..4) == Some(&[sep, sep, b'?'.into(), sep]) {
+                // Remove first 3 characters. It still starts with `/` so it is absolute on Unix.
+                path.splice(0..3, std::iter::empty());
+            }
+            // If it starts with a drive letter (`X:/`), convert it to an absolute Unix path.
+            else if path.get(1..3) == Some(&[b':'.into(), sep]) {
+                // We add a `/` at the beginning, to store the absolute Windows
+                // path in something that looks like an absolute Unix path.
+                path.insert(0, sep);
+            }
+        }
+
+        /// Adjust a Unix path to Windows conventions such that it un-does everything that
+        /// `windows_to_unix` did, and such that if the Unix input path was absolute, then the
+        /// Windows output path is absolute.
+        fn unix_to_windows<T>(path: &mut Vec<T>)
+        where
+            T: From<u8> + Copy + Eq,
+        {
+            let sep = T::from(b'\\');
+            // Make sure all path separators are `\`.
+            for c in path.iter_mut() {
+                if *c == b'/'.into() {
+                    *c = sep;
+                }
+            }
+            // If the path is `\X:\`, the leading separator was probably added by `windows_to_unix`
+            // and we should get rid of it again.
+            if path.get(2..4) == Some(&[b':'.into(), sep]) && path[0] == sep {
+                // The new path is still absolute on Windows.
+                path.remove(0);
+            }
+            // If this starts withs a `\` but not a `\\`, then this was absolute on Unix but is
+            // relative on Windows (relative to "the root of the current directory", e.g. the
+            // drive letter).
+            else if path.first() == Some(&sep) && path.get(1) != Some(&sep) {
+                // We add `\\?` so it starts with `\\?\` which is some magic path on Windows
+                // that *is* considered absolute. This way we store the absolute Unix path
+                // in something that looks like an absolute Windows path.
+                path.splice(0..0, [sep, sep, b'?'.into()]);
+            }
+        }
+
+        // Below we assume that everything non-Windows works like Unix, at least
+        // when it comes to file system path conventions.
         #[cfg(windows)]
         return if target_os == "windows" {
             // Windows-on-Windows, all fine.
             os_str
         } else {
             // Unix target, Windows host.
-            let (from, to) = match direction {
-                PathConversion::HostToTarget => ('\\', '/'),
-                PathConversion::TargetToHost => ('/', '\\'),
-            };
-            let mut converted = os_str
-                .encode_wide()
-                .map(|wchar| if wchar == from as u16 { to as u16 } else { wchar })
-                .collect::<Vec<_>>();
-            // We also have to ensure that absolute paths remain absolute.
+            let mut path: Vec<u16> = os_str.encode_wide().collect();
             match direction {
                 PathConversion::HostToTarget => {
-                    // If this is an absolute Windows path that starts with a drive letter (`C:/...`
-                    // after separator conversion), it would not be considered absolute by Unix
-                    // target code.
-                    if converted.get(1).copied() == Some(b':' as u16)
-                        && converted.get(2).copied() == Some(b'/' as u16)
-                    {
-                        // We add a `/` at the beginning, to store the absolute Windows
-                        // path in something that looks like an absolute Unix path.
-                        converted.insert(0, b'/' as u16);
-                    }
+                    windows_to_unix(&mut path);
                 }
                 PathConversion::TargetToHost => {
-                    // If the path is `\C:\`, the leading backslash was probably added by the above code
-                    // and we should get rid of it again.
-                    if converted.get(0).copied() == Some(b'\\' as u16)
-                        && converted.get(2).copied() == Some(b':' as u16)
-                        && converted.get(3).copied() == Some(b'\\' as u16)
-                    {
-                        converted.remove(0);
-                    }
+                    unix_to_windows(&mut path);
                 }
             }
-            Cow::Owned(OsString::from_wide(&converted))
+            Cow::Owned(OsString::from_wide(&path))
         };
         #[cfg(unix)]
         return if target_os == "windows" {
             // Windows target, Unix host.
-            let (from, to) = match direction {
-                PathConversion::HostToTarget => (b'/', b'\\'),
-                PathConversion::TargetToHost => (b'\\', b'/'),
-            };
-            let mut converted = os_str
-                .as_bytes()
-                .iter()
-                .map(|&wchar| if wchar == from { to } else { wchar })
-                .collect::<Vec<_>>();
-            // We also have to ensure that absolute paths remain absolute.
+            let mut path: Vec<u8> = os_str.into_owned().into_encoded_bytes();
             match direction {
                 PathConversion::HostToTarget => {
-                    // If the path is `/C:/`, the leading backslash was probably added by the below
-                    // driver letter handling and we should get rid of it again.
-                    if converted.get(0).copied() == Some(b'\\')
-                        && converted.get(2).copied() == Some(b':')
-                        && converted.get(3).copied() == Some(b'\\')
-                    {
-                        converted.remove(0);
-                    }
-                    // If this start withs a `\` but not a `\\`, then for Windows this is a relative
-                    // path. But the host path is absolute as it started with `/`. We add `\\?` so
-                    // it starts with `\\?\` which is some magic path on Windows that *is*
-                    // considered absolute.
-                    else if converted.get(0).copied() == Some(b'\\')
-                        && converted.get(1).copied() != Some(b'\\')
-                    {
-                        converted.splice(0..0, b"\\\\?".iter().copied());
-                    }
+                    unix_to_windows(&mut path);
                 }
                 PathConversion::TargetToHost => {
-                    // If this starts with `//?/`, it was probably produced by the above code and we
-                    // remove the `//?` that got added to get the Unix path back out.
-                    if converted.get(0).copied() == Some(b'/')
-                        && converted.get(1).copied() == Some(b'/')
-                        && converted.get(2).copied() == Some(b'?')
-                        && converted.get(3).copied() == Some(b'/')
-                    {
-                        // Remove first 3 characters
-                        converted.splice(0..3, std::iter::empty());
-                    }
-                    // If it starts with a drive letter, convert it to an absolute Unix path.
-                    else if converted.get(1).copied() == Some(b':')
-                        && converted.get(2).copied() == Some(b'/')
-                    {
-                        converted.insert(0, b'/');
-                    }
+                    windows_to_unix(&mut path);
                 }
             }
-            Cow::Owned(OsString::from_vec(converted))
+            Cow::Owned(OsString::from_vec(path))
         } else {
             // Unix-on-Unix, all is fine.
             os_str
diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs
index 4535bcf..1126c90 100644
--- a/src/tools/miri/src/shims/time.rs
+++ b/src/tools/miri/src/shims/time.rs
@@ -236,11 +236,11 @@
             .unwrap_or_else(|| now.checked_add(Duration::from_secs(3600)).unwrap());
 
         let active_thread = this.get_active_thread();
-        this.block_thread(active_thread);
+        this.block_thread(active_thread, BlockReason::Sleep);
 
         this.register_timeout_callback(
             active_thread,
-            Time::Monotonic(timeout_time),
+            CallbackTime::Monotonic(timeout_time),
             Box::new(UnblockCallback { thread_to_unblock: active_thread }),
         );
 
@@ -259,11 +259,11 @@
         let timeout_time = this.machine.clock.now().checked_add(duration).unwrap();
 
         let active_thread = this.get_active_thread();
-        this.block_thread(active_thread);
+        this.block_thread(active_thread, BlockReason::Sleep);
 
         this.register_timeout_callback(
             active_thread,
-            Time::Monotonic(timeout_time),
+            CallbackTime::Monotonic(timeout_time),
             Box::new(UnblockCallback { thread_to_unblock: active_thread }),
         );
 
@@ -281,7 +281,7 @@
 
 impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback {
     fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
-        ecx.unblock_thread(self.thread_to_unblock);
+        ecx.unblock_thread(self.thread_to_unblock, BlockReason::Sleep);
         Ok(())
     }
 }
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 3a56aa9..c72d3bb 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -6,6 +6,7 @@
 use rustc_target::abi::{Align, Size};
 use rustc_target::spec::abi::Abi;
 
+use crate::shims::alloc::EvalContextExt as _;
 use crate::shims::unix::*;
 use crate::*;
 use shims::foreign_items::EmulateForeignItemResult;
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 31076fd..ebf9f43 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -196,13 +196,12 @@
     read_dir: ReadDir,
     /// The most recent entry returned by readdir().
     /// Will be freed by the next call.
-    entry: Pointer<Option<Provenance>>,
+    entry: Option<Pointer<Option<Provenance>>>,
 }
 
 impl OpenDir {
     fn new(read_dir: ReadDir) -> Self {
-        // We rely on `free` being a NOP on null pointers.
-        Self { read_dir, entry: Pointer::null() }
+        Self { read_dir, entry: None }
     }
 }
 
@@ -924,8 +923,12 @@
                 let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes();
                 let size = d_name_offset.checked_add(name_len).unwrap();
 
-                let entry =
-                    this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::Runtime)?;
+                let entry = this.allocate_ptr(
+                    Size::from_bytes(size),
+                    dirent64_layout.align.abi,
+                    MiriMemoryKind::Runtime.into(),
+                )?;
+                let entry: Pointer<Option<Provenance>> = entry.into();
 
                 // If the host is a Unix system, fill in the inode number with its real value.
                 // If not, use 0 as a fallback value.
@@ -949,23 +952,25 @@
                 let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?;
                 this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;
 
-                entry
+                Some(entry)
             }
             None => {
                 // end of stream: return NULL
-                Pointer::null()
+                None
             }
             Some(Err(e)) => {
                 this.set_last_error_from_io_error(e.kind())?;
-                Pointer::null()
+                None
             }
         };
 
         let open_dir = this.machine.dirs.streams.get_mut(&dirp).unwrap();
         let old_entry = std::mem::replace(&mut open_dir.entry, entry);
-        this.free(old_entry, MiriMemoryKind::Runtime)?;
+        if let Some(old_entry) = old_entry {
+            this.deallocate_ptr(old_entry, None, MiriMemoryKind::Runtime.into())?;
+        }
 
-        Ok(Scalar::from_maybe_pointer(entry, this))
+        Ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this))
     }
 
     fn macos_fbsd_readdir_r(
@@ -1106,7 +1111,9 @@
         }
 
         if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) {
-            this.free(open_dir.entry, MiriMemoryKind::Runtime)?;
+            if let Some(entry) = open_dir.entry {
+                this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?;
+            }
             drop(open_dir);
             Ok(0)
         } else {
diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs
index ed27066..d4a6cd9 100644
--- a/src/tools/miri/src/shims/unix/linux/sync.rs
+++ b/src/tools/miri/src/shims/unix/linux/sync.rs
@@ -107,16 +107,22 @@
                 Some(if wait_bitset {
                     // FUTEX_WAIT_BITSET uses an absolute timestamp.
                     if realtime {
-                        Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
+                        CallbackTime::RealTime(
+                            SystemTime::UNIX_EPOCH.checked_add(duration).unwrap(),
+                        )
                     } else {
-                        Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
+                        CallbackTime::Monotonic(
+                            this.machine.clock.anchor().checked_add(duration).unwrap(),
+                        )
                     }
                 } else {
                     // FUTEX_WAIT uses a relative timestamp.
                     if realtime {
-                        Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
+                        CallbackTime::RealTime(SystemTime::now().checked_add(duration).unwrap())
                     } else {
-                        Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())
+                        CallbackTime::Monotonic(
+                            this.machine.clock.now().checked_add(duration).unwrap(),
+                        )
                     }
                 })
             };
@@ -169,7 +175,7 @@
             let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Relaxed)?.to_i32()?;
             if val == futex_val {
                 // The value still matches, so we block the thread make it wait for FUTEX_WAKE.
-                this.block_thread(thread);
+                this.block_thread(thread, BlockReason::Futex { addr: addr_usize });
                 this.futex_wait(addr_usize, thread, bitset);
                 // Succesfully waking up from FUTEX_WAIT always returns zero.
                 this.write_scalar(Scalar::from_target_isize(0, this), dest)?;
@@ -191,7 +197,10 @@
 
                     impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> {
                         fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
-                            this.unblock_thread(self.thread);
+                            this.unblock_thread(
+                                self.thread,
+                                BlockReason::Futex { addr: self.addr_usize },
+                            );
                             this.futex_remove_waiter(self.addr_usize, self.thread);
                             let etimedout = this.eval_libc("ETIMEDOUT");
                             this.set_last_error(etimedout)?;
@@ -249,7 +258,7 @@
             #[allow(clippy::arithmetic_side_effects)]
             for _ in 0..val {
                 if let Some(thread) = this.futex_wake(addr_usize, bitset) {
-                    this.unblock_thread(thread);
+                    this.unblock_thread(thread, BlockReason::Futex { addr: addr_usize });
                     this.unregister_timeout_callback_if_exists(thread);
                     n += 1;
                 } else {
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index dd301f9..e50a893 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -1,6 +1,5 @@
 use std::time::SystemTime;
 
-use crate::concurrency::sync::CondvarLock;
 use crate::concurrency::thread::MachineCallback;
 use crate::*;
 
@@ -225,9 +224,10 @@
 fn reacquire_cond_mutex<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     thread: ThreadId,
+    condvar: CondvarId,
     mutex: MutexId,
 ) -> InterpResult<'tcx> {
-    ecx.unblock_thread(thread);
+    ecx.unblock_thread(thread, BlockReason::Condvar(condvar));
     if ecx.mutex_is_locked(mutex) {
         ecx.mutex_enqueue_and_block(mutex, thread);
     } else {
@@ -242,9 +242,10 @@
 fn post_cond_signal<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     thread: ThreadId,
+    condvar: CondvarId,
     mutex: MutexId,
 ) -> InterpResult<'tcx> {
-    reacquire_cond_mutex(ecx, thread, mutex)?;
+    reacquire_cond_mutex(ecx, thread, condvar, mutex)?;
     // Waiting for the mutex is not included in the waiting time because we need
     // to acquire the mutex always even if we get a timeout.
     ecx.unregister_timeout_callback_if_exists(thread);
@@ -256,6 +257,7 @@
 fn release_cond_mutex_and_block<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     active_thread: ThreadId,
+    condvar: CondvarId,
     mutex: MutexId,
 ) -> InterpResult<'tcx> {
     if let Some(old_locked_count) = ecx.mutex_unlock(mutex, active_thread) {
@@ -265,7 +267,7 @@
     } else {
         throw_ub_format!("awaiting on unlocked or owned by a different thread mutex");
     }
-    ecx.block_thread(active_thread);
+    ecx.block_thread(active_thread, BlockReason::Condvar(condvar));
     Ok(())
 }
 
@@ -792,12 +794,8 @@
     fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
         let id = cond_get_id(this, cond_op)?;
-        if let Some((thread, lock)) = this.condvar_signal(id) {
-            if let CondvarLock::Mutex(mutex) = lock {
-                post_cond_signal(this, thread, mutex)?;
-            } else {
-                panic!("condvar should not have an rwlock on unix");
-            }
+        if let Some((thread, mutex)) = this.condvar_signal(id) {
+            post_cond_signal(this, thread, id, mutex)?;
         }
 
         Ok(0)
@@ -810,12 +808,8 @@
         let this = self.eval_context_mut();
         let id = cond_get_id(this, cond_op)?;
 
-        while let Some((thread, lock)) = this.condvar_signal(id) {
-            if let CondvarLock::Mutex(mutex) = lock {
-                post_cond_signal(this, thread, mutex)?;
-            } else {
-                panic!("condvar should not have an rwlock on unix");
-            }
+        while let Some((thread, mutex)) = this.condvar_signal(id) {
+            post_cond_signal(this, thread, id, mutex)?;
         }
 
         Ok(0)
@@ -832,8 +826,8 @@
         let mutex_id = mutex_get_id(this, mutex_op)?;
         let active_thread = this.get_active_thread();
 
-        release_cond_mutex_and_block(this, active_thread, mutex_id)?;
-        this.condvar_wait(id, active_thread, CondvarLock::Mutex(mutex_id));
+        release_cond_mutex_and_block(this, active_thread, id, mutex_id)?;
+        this.condvar_wait(id, active_thread, mutex_id);
 
         Ok(0)
     }
@@ -866,15 +860,15 @@
 
         let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME") {
             this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
-            Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
+            CallbackTime::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
         } else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") {
-            Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
+            CallbackTime::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
         } else {
             throw_unsup_format!("unsupported clock id: {}", clock_id);
         };
 
-        release_cond_mutex_and_block(this, active_thread, mutex_id)?;
-        this.condvar_wait(id, active_thread, CondvarLock::Mutex(mutex_id));
+        release_cond_mutex_and_block(this, active_thread, id, mutex_id)?;
+        this.condvar_wait(id, active_thread, mutex_id);
 
         // We return success for now and override it in the timeout callback.
         this.write_scalar(Scalar::from_i32(0), dest)?;
@@ -897,7 +891,7 @@
             fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
                 // We are not waiting for the condvar any more, wait for the
                 // mutex instead.
-                reacquire_cond_mutex(ecx, self.active_thread, self.mutex_id)?;
+                reacquire_cond_mutex(ecx, self.active_thread, self.id, self.mutex_id)?;
 
                 // Remove the thread from the conditional variable.
                 ecx.condvar_remove_waiter(self.id, self.active_thread);
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index de80df3..ec4c610 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -8,6 +8,7 @@
 use rustc_target::abi::Size;
 use rustc_target::spec::abi::Abi;
 
+use crate::shims::alloc::EvalContextExt as _;
 use crate::shims::os_str::bytes_to_os_str;
 use crate::*;
 use shims::foreign_items::EmulateForeignItemResult;
diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs
index f02939f..836b9e9 100644
--- a/src/tools/miri/src/shims/windows/sync.rs
+++ b/src/tools/miri/src/shims/windows/sync.rs
@@ -170,7 +170,7 @@
             None
         } else {
             let duration = Duration::from_millis(timeout_ms.into());
-            Some(Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap()))
+            Some(CallbackTime::Monotonic(this.machine.clock.now().checked_add(duration).unwrap()))
         };
 
         // See the Linux futex implementation for why this fence exists.
@@ -183,7 +183,7 @@
 
         if futex_val == compare_val {
             // If the values are the same, we have to block.
-            this.block_thread(thread);
+            this.block_thread(thread, BlockReason::Futex { addr });
             this.futex_wait(addr, thread, u32::MAX);
 
             if let Some(timeout_time) = timeout_time {
@@ -202,7 +202,7 @@
 
                 impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> {
                     fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
-                        this.unblock_thread(self.thread);
+                        this.unblock_thread(self.thread, BlockReason::Futex { addr: self.addr });
                         this.futex_remove_waiter(self.addr, self.thread);
                         let error_timeout = this.eval_windows("c", "ERROR_TIMEOUT");
                         this.set_last_error(error_timeout)?;
@@ -233,8 +233,9 @@
         // See the Linux futex implementation for why this fence exists.
         this.atomic_fence(AtomicFenceOrd::SeqCst)?;
 
-        if let Some(thread) = this.futex_wake(ptr.addr().bytes(), u32::MAX) {
-            this.unblock_thread(thread);
+        let addr = ptr.addr().bytes();
+        if let Some(thread) = this.futex_wake(addr, u32::MAX) {
+            this.unblock_thread(thread, BlockReason::Futex { addr });
             this.unregister_timeout_callback_if_exists(thread);
         }
 
@@ -248,8 +249,9 @@
         // See the Linux futex implementation for why this fence exists.
         this.atomic_fence(AtomicFenceOrd::SeqCst)?;
 
-        while let Some(thread) = this.futex_wake(ptr.addr().bytes(), u32::MAX) {
-            this.unblock_thread(thread);
+        let addr = ptr.addr().bytes();
+        while let Some(thread) = this.futex_wake(addr, u32::MAX) {
+            this.unblock_thread(thread, BlockReason::Futex { addr });
             this.unregister_timeout_callback_if_exists(thread);
         }
 
diff --git a/src/tools/miri/test-cargo-miri/test.bin-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.bin-target.stdout.ref
index 5264530..6f48025 100644
--- a/src/tools/miri/test-cargo-miri/test.bin-target.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.bin-target.stdout.ref
@@ -3,5 +3,5 @@
 test test::dev_dependency ... ok
 test test::exported_symbol ... ok
 
-test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
 
diff --git a/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref
index 8c543e4..2ef124e 100644
--- a/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref
@@ -1,11 +1,11 @@
 
 running 2 tests
 ..
-test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
 
 imported main
 
 running 6 tests
 ...i..
-test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
+test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME
 
diff --git a/src/tools/miri/test-cargo-miri/test.default.stdout.ref b/src/tools/miri/test-cargo-miri/test.default.stdout.ref
index 922d212..2d74d82 100644
--- a/src/tools/miri/test-cargo-miri/test.default.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.default.stdout.ref
@@ -1,13 +1,13 @@
 
 running 2 tests
 ..
-test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
 
 imported main
 
 running 6 tests
 ...i..
-test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
+test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME
 
 
 running 5 tests
diff --git a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref
index bb0282d..59b4deb 100644
--- a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref
@@ -1,12 +1,12 @@
 
 running 0 tests
 
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in $TIME
 
 imported main
 
 running 1 test
 test simple ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME
 
diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref
index 5c819dd..b68bc98 100644
--- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref
@@ -1,14 +1,14 @@
 
 running 0 tests
 
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in $TIME
 
 imported main
 
 running 1 test
 test simple ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME
 
 
 running 0 tests
diff --git a/src/tools/miri/test-cargo-miri/test.subcrate.stdout.ref b/src/tools/miri/test-cargo-miri/test.subcrate.stdout.ref
index 67e5c7f..e50838e 100644
--- a/src/tools/miri/test-cargo-miri/test.subcrate.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.subcrate.stdout.ref
@@ -1,6 +1,6 @@
 
 running 0 tests
 
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
 
 subcrate testing
diff --git a/src/tools/miri/test-cargo-miri/test.test-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.test-target.stdout.ref
index dd59b32..38b3f5c 100644
--- a/src/tools/miri/test-cargo-miri/test.test-target.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.test-target.stdout.ref
@@ -7,5 +7,5 @@
 test fail_index_check - should panic ... ok
 test simple ... ok
 
-test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
+test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs
index 910e062..532bda2 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs
@@ -1,6 +1,7 @@
 //@only-target-windows: Uses win32 api functions
 // We are making scheduler assumptions here.
 //@compile-flags: -Zmiri-preemption-rate=0
+//@error-in-other-file: deadlock
 
 // On windows, joining main is not UB, but it will block a thread forever.
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
index d9137ee..12f35fd 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
@@ -8,7 +8,28 @@
    = note: inside closure at RUSTLIB/core/src/macros/mod.rs:LL:CC
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |         let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
+   |                                                                                          ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> $DIR/windows_join_main.rs:LL:CC
+   |
+LL | /     thread::spawn(|| {
+LL | |         unsafe {
+LL | |             assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0);
+LL | |         }
+LL | |     })
+LL | |     .join()
+   | |___________^
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs
index a7c8faf..a64265c 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs
@@ -1,6 +1,7 @@
 //@only-target-windows: Uses win32 api functions
 // We are making scheduler assumptions here.
 //@compile-flags: -Zmiri-preemption-rate=0
+//@error-in-other-file: deadlock
 
 // On windows, a thread joining itself is not UB, but it will deadlock.
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
index 74699a0..8d26c35 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
@@ -7,7 +7,29 @@
    = note: BACKTRACE on thread `unnamed-ID`:
    = note: inside closure at $DIR/windows_join_self.rs:LL:CC
 
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |         let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
+   |                                                                                          ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> $DIR/windows_join_self.rs:LL:CC
+   |
+LL | /     thread::spawn(|| {
+LL | |         unsafe {
+LL | |             let native = GetCurrentThread();
+LL | |             assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0);
+LL | |         }
+LL | |     })
+LL | |     .join()
+   | |___________^
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/miri/tests/fail-dep/realloc-zero.rs b/src/tools/miri/tests/fail-dep/realloc-zero.rs
new file mode 100644
index 0000000..1482798
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/realloc-zero.rs
@@ -0,0 +1,10 @@
+//@ignore-target-windows: No libc on Windows
+
+fn main() {
+    unsafe {
+        let p1 = libc::malloc(20);
+        // C made this UB...
+        let p2 = libc::realloc(p1, 0); //~ERROR: `realloc` with a size of zero
+        assert!(p2.is_null());
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/realloc-zero.stderr b/src/tools/miri/tests/fail-dep/realloc-zero.stderr
new file mode 100644
index 0000000..749a61f
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/realloc-zero.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: `realloc` with a size of zero
+  --> $DIR/realloc-zero.rs:LL:CC
+   |
+LL |         let p2 = libc::realloc(p1, 0);
+   |                  ^^^^^^^^^^^^^^^^^^^^ `realloc` with a size of zero
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/realloc-zero.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs
index 6c3cb73..60d56d4 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs
@@ -1,4 +1,5 @@
 //@ignore-target-windows: No libc on Windows
+//@error-in-other-file: deadlock
 
 use std::cell::UnsafeCell;
 use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr
index 76b1d26..987d0fc 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr
@@ -7,7 +7,26 @@
    = note: BACKTRACE on thread `unnamed-ID`:
    = note: inside closure at $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
 
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |             let ret = libc::pthread_join(self.id, ptr::null_mut());
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
+   |
+LL | /         thread::spawn(move || {
+LL | |             assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0);
+LL | |         })
+LL | |         .join()
+   | |_______________^
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs
index 2018446..0f02c32 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs
@@ -1,4 +1,5 @@
 //@ignore-target-windows: No libc on Windows
+//@error-in-other-file: deadlock
 
 use std::cell::UnsafeCell;
 use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr
index 5501dab..bc9b15f 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr
@@ -7,7 +7,26 @@
    = note: BACKTRACE on thread `unnamed-ID`:
    = note: inside closure at $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
 
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |             let ret = libc::pthread_join(self.id, ptr::null_mut());
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
+   |
+LL | /         thread::spawn(move || {
+LL | |             assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);
+LL | |         })
+LL | |         .join()
+   | |_______________^
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs
index b1d7e04..10be5b3 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs
@@ -1,4 +1,5 @@
 //@ignore-target-windows: No libc on Windows
+//@error-in-other-file: deadlock
 
 use std::cell::UnsafeCell;
 use std::sync::Arc;
diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr
index 815d85a..66c142b 100644
--- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr
@@ -7,7 +7,26 @@
    = note: BACKTRACE on thread `unnamed-ID`:
    = note: inside closure at $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
 
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |             let ret = libc::pthread_join(self.id, ptr::null_mut());
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
+   |
+LL | /         thread::spawn(move || {
+LL | |             assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);
+LL | |         })
+LL | |         .join()
+   | |_______________^
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs
new file mode 100644
index 0000000..2097126
--- /dev/null
+++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs
@@ -0,0 +1,10 @@
+//@error-in-other-file: aborted
+//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();"
+//@normalize-stderr-test: "\| +\^+" -> "| ^"
+#![feature(allocator_api)]
+
+use std::alloc::*;
+
+fn main() {
+    handle_alloc_error(Layout::for_value(&0));
+}
diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr
new file mode 100644
index 0000000..d1731a0
--- /dev/null
+++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr
@@ -0,0 +1,24 @@
+memory allocation of 4 bytes failed
+error: abnormal termination: the program aborted execution
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+   |
+LL |     ABORT();
+   | ^ the program aborted execution
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+   = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC
+   = note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC
+   = note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC
+   = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
+   = note: inside `std::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
+note: inside `main`
+  --> $DIR/alloc_error_handler.rs:LL:CC
+   |
+LL |     handle_alloc_error(Layout::for_value(&0));
+   | ^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.rs
new file mode 100644
index 0000000..babdb73
--- /dev/null
+++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.rs
@@ -0,0 +1,49 @@
+//@compile-flags: -Cpanic=abort
+#![feature(start, core_intrinsics)]
+#![feature(alloc_error_handler)]
+#![feature(allocator_api)]
+#![no_std]
+
+extern crate alloc;
+
+use alloc::alloc::*;
+use core::fmt::Write;
+
+#[path = "../../utils/mod.no_std.rs"]
+mod utils;
+
+#[alloc_error_handler]
+fn alloc_error_handler(layout: Layout) -> ! {
+    let _ = writeln!(utils::MiriStderr, "custom alloc error handler: {layout:?}");
+    core::intrinsics::abort(); //~ERROR: aborted
+}
+
+// rustc requires us to provide some more things that aren't actually used by this test
+mod plumbing {
+    use super::*;
+
+    #[panic_handler]
+    fn panic_handler(_: &core::panic::PanicInfo) -> ! {
+        loop {}
+    }
+
+    struct NoAlloc;
+
+    unsafe impl GlobalAlloc for NoAlloc {
+        unsafe fn alloc(&self, _: Layout) -> *mut u8 {
+            unreachable!();
+        }
+
+        unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
+            unreachable!();
+        }
+    }
+
+    #[global_allocator]
+    static GLOBAL: NoAlloc = NoAlloc;
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+    handle_alloc_error(Layout::for_value(&0));
+}
diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr
new file mode 100644
index 0000000..5d9c2e2
--- /dev/null
+++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_custom.stderr
@@ -0,0 +1,27 @@
+custom alloc error handler: Layout { size: 4, align: 4 (1 << 2) }
+error: abnormal termination: the program aborted execution
+  --> $DIR/alloc_error_handler_custom.rs:LL:CC
+   |
+LL |     core::intrinsics::abort();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution
+   |
+   = note: BACKTRACE:
+   = note: inside `alloc_error_handler` at $DIR/alloc_error_handler_custom.rs:LL:CC
+note: inside `_::__rg_oom`
+  --> $DIR/alloc_error_handler_custom.rs:LL:CC
+   |
+LL | #[alloc_error_handler]
+   | ---------------------- in this procedural macro expansion
+LL | fn alloc_error_handler(layout: Layout) -> ! {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
+   = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
+note: inside `start`
+  --> $DIR/alloc_error_handler_custom.rs:LL:CC
+   |
+LL |     handle_alloc_error(Layout::for_value(&0));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.rs
new file mode 100644
index 0000000..18a8a61
--- /dev/null
+++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.rs
@@ -0,0 +1,47 @@
+//@compile-flags: -Cpanic=abort
+#![feature(start, core_intrinsics)]
+#![feature(alloc_error_handler)]
+#![feature(allocator_api)]
+#![no_std]
+
+extern crate alloc;
+
+use alloc::alloc::*;
+use core::fmt::Write;
+
+#[path = "../../utils/mod.no_std.rs"]
+mod utils;
+
+// The default no_std alloc_error_handler is a panic.
+
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+    let _ = writeln!(utils::MiriStderr, "custom panic handler called!");
+    let _ = writeln!(utils::MiriStderr, "{panic_info}");
+    core::intrinsics::abort(); //~ERROR: aborted
+}
+
+// rustc requires us to provide some more things that aren't actually used by this test
+mod plumbing {
+    use super::*;
+
+    struct NoAlloc;
+
+    unsafe impl GlobalAlloc for NoAlloc {
+        unsafe fn alloc(&self, _: Layout) -> *mut u8 {
+            unreachable!();
+        }
+
+        unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
+            unreachable!();
+        }
+    }
+
+    #[global_allocator]
+    static GLOBAL: NoAlloc = NoAlloc;
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+    handle_alloc_error(Layout::for_value(&0));
+}
diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr
new file mode 100644
index 0000000..6b98f6f
--- /dev/null
+++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler_no_std.stderr
@@ -0,0 +1,24 @@
+custom panic handler called!
+panicked at RUSTLIB/alloc/src/alloc.rs:LL:CC:
+memory allocation of 4 bytes failed
+error: abnormal termination: the program aborted execution
+  --> $DIR/alloc_error_handler_no_std.rs:LL:CC
+   |
+LL |     core::intrinsics::abort();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution
+   |
+   = note: BACKTRACE:
+   = note: inside `panic_handler` at $DIR/alloc_error_handler_no_std.rs:LL:CC
+   = note: inside `alloc::alloc::__alloc_error_handler::__rdl_oom` at RUSTLIB/alloc/src/alloc.rs:LL:CC
+   = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
+   = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
+note: inside `start`
+  --> $DIR/alloc_error_handler_no_std.rs:LL:CC
+   |
+LL |     handle_alloc_error(Layout::for_value(&0));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs
index 3edaf10..0061679 100644
--- a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs
+++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs
@@ -1,6 +1,8 @@
 //! Make sure that a retag acts like a write for the data race model.
 //@revisions: stack tree
 //@compile-flags: -Zmiri-preemption-rate=0
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 //@[tree]compile-flags: -Zmiri-tree-borrows
 #[derive(Copy, Clone)]
 struct SendPtr(*mut u8);
diff --git a/src/tools/miri/tests/fail/data_race/alloc_read_race.rs b/src/tools/miri/tests/fail/data_race/alloc_read_race.rs
index 2cf3660..c85c0eb 100644
--- a/src/tools/miri/tests/fail/data_race/alloc_read_race.rs
+++ b/src/tools/miri/tests/fail/data_race/alloc_read_race.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 #![feature(new_uninit)]
 
 use std::mem::MaybeUninit;
diff --git a/src/tools/miri/tests/fail/data_race/alloc_write_race.rs b/src/tools/miri/tests/fail/data_race/alloc_write_race.rs
index e95e0e1..9e2a430 100644
--- a/src/tools/miri/tests/fail/data_race/alloc_write_race.rs
+++ b/src/tools/miri/tests/fail/data_race/alloc_write_race.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 #![feature(new_uninit)]
 
 use std::ptr::null_mut;
diff --git a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs
index a256267..4003892 100644
--- a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs
+++ b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::thread::spawn;
diff --git a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs
index cc6a074..8bceba9 100644
--- a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs
+++ b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::AtomicUsize;
 use std::sync::atomic::Ordering;
diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs
index 7392781..1a2746a 100644
--- a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs
+++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::AtomicUsize;
 use std::sync::atomic::Ordering;
diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs
index f681ce0..e0876a9 100644
--- a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs
+++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::thread::spawn;
diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs
index 47a3ef5..1010216 100644
--- a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs
+++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::thread::spawn;
diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs
index 8bba4a8..b494bd3 100644
--- a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs
+++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::AtomicUsize;
 use std::sync::atomic::Ordering;
diff --git a/src/tools/miri/tests/fail/data_race/dangling_thread_async_race.rs b/src/tools/miri/tests/fail/data_race/dangling_thread_async_race.rs
index 5b90056..dffafe3 100644
--- a/src/tools/miri/tests/fail/data_race/dangling_thread_async_race.rs
+++ b/src/tools/miri/tests/fail/data_race/dangling_thread_async_race.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::mem;
 use std::thread::{sleep, spawn};
diff --git a/src/tools/miri/tests/fail/data_race/dangling_thread_race.rs b/src/tools/miri/tests/fail/data_race/dangling_thread_race.rs
index 91c1191..8dc35c7 100644
--- a/src/tools/miri/tests/fail/data_race/dangling_thread_race.rs
+++ b/src/tools/miri/tests/fail/data_race/dangling_thread_race.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::mem;
 use std::thread::{sleep, spawn};
diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs
index 5928e47..f174909 100644
--- a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs
+++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::thread::spawn;
 
diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs
index c5f82cc..1edfbf5 100644
--- a/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs
+++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::thread::spawn;
 
diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs
index 1095f1e..c67e03d 100644
--- a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs
+++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::ptr::null_mut;
 use std::sync::atomic::{AtomicPtr, Ordering};
diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs
index b5911e5..7605f19 100644
--- a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs
+++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::thread::spawn;
 
diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs
index 7a2c882..4f3819b 100644
--- a/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs
+++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::thread::spawn;
 
diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs
index 5ee4cc0..8e63bc1 100644
--- a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs
+++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::ptr::null_mut;
 use std::sync::atomic::{AtomicPtr, Ordering};
diff --git a/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs b/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs
index f2da45d..5305060 100644
--- a/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs
+++ b/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::thread::spawn;
 
diff --git a/src/tools/miri/tests/fail/data_race/fence_after_load.rs b/src/tools/miri/tests/fail/data_race/fence_after_load.rs
index 683e3b9..92cb4cc 100644
--- a/src/tools/miri/tests/fail/data_race/fence_after_load.rs
+++ b/src/tools/miri/tests/fail/data_race/fence_after_load.rs
@@ -1,5 +1,8 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
+
 use std::sync::atomic::{fence, AtomicUsize, Ordering};
 use std::sync::Arc;
 use std::thread;
diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read.rs b/src/tools/miri/tests/fail/data_race/mixed_size_read.rs
index 091a470..61af972 100644
--- a/src/tools/miri/tests/fail/data_race/mixed_size_read.rs
+++ b/src/tools/miri/tests/fail/data_race/mixed_size_read.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
+
 use std::sync::atomic::{AtomicU16, AtomicU8, Ordering};
 use std::thread;
 
diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_write.rs b/src/tools/miri/tests/fail/data_race/mixed_size_write.rs
index 49fb6c1..12e51bb 100644
--- a/src/tools/miri/tests/fail/data_race/mixed_size_write.rs
+++ b/src/tools/miri/tests/fail/data_race/mixed_size_write.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
+
 use std::sync::atomic::{AtomicU16, AtomicU8, Ordering};
 use std::thread;
 
diff --git a/src/tools/miri/tests/fail/data_race/read_read_race1.rs b/src/tools/miri/tests/fail/data_race/read_read_race1.rs
index f66b5ca..02aa4e4 100644
--- a/src/tools/miri/tests/fail/data_race/read_read_race1.rs
+++ b/src/tools/miri/tests/fail/data_race/read_read_race1.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-preemption-rate=0.0
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
+
 use std::sync::atomic::{AtomicU16, Ordering};
 use std::thread;
 
diff --git a/src/tools/miri/tests/fail/data_race/read_read_race2.rs b/src/tools/miri/tests/fail/data_race/read_read_race2.rs
index d87b667..3b94c91 100644
--- a/src/tools/miri/tests/fail/data_race/read_read_race2.rs
+++ b/src/tools/miri/tests/fail/data_race/read_read_race2.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-preemption-rate=0.0
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
+
 use std::sync::atomic::{AtomicU16, Ordering};
 use std::thread;
 
diff --git a/src/tools/miri/tests/fail/data_race/read_write_race.rs b/src/tools/miri/tests/fail/data_race/read_write_race.rs
index 70971b5..adf19dd 100644
--- a/src/tools/miri/tests/fail/data_race/read_write_race.rs
+++ b/src/tools/miri/tests/fail/data_race/read_write_race.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here. Stacked borrows interferes by having its own accesses.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::thread::spawn;
 
diff --git a/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs
index 9fec3cee..f411767 100644
--- a/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs
+++ b/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::ptr::null_mut;
 use std::sync::atomic::{AtomicPtr, Ordering};
diff --git a/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs b/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs
index be44507..c4f9438 100644
--- a/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs
+++ b/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::thread::spawn;
diff --git a/src/tools/miri/tests/fail/data_race/release_seq_race.rs b/src/tools/miri/tests/fail/data_race/release_seq_race.rs
index 9810832..f03ab3e 100644
--- a/src/tools/miri/tests/fail/data_race/release_seq_race.rs
+++ b/src/tools/miri/tests/fail/data_race/release_seq_race.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::thread::{sleep, spawn};
diff --git a/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs b/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs
index 93cbc2a..88ae01b 100644
--- a/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs
+++ b/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::thread::spawn;
diff --git a/src/tools/miri/tests/fail/data_race/rmw_race.rs b/src/tools/miri/tests/fail/data_race/rmw_race.rs
index 982e9c1..d738caa 100644
--- a/src/tools/miri/tests/fail/data_race/rmw_race.rs
+++ b/src/tools/miri/tests/fail/data_race/rmw_race.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::thread::spawn;
diff --git a/src/tools/miri/tests/fail/data_race/stack_pop_race.rs b/src/tools/miri/tests/fail/data_race/stack_pop_race.rs
index 68d82bc..762a8e5 100644
--- a/src/tools/miri/tests/fail/data_race/stack_pop_race.rs
+++ b/src/tools/miri/tests/fail/data_race/stack_pop_race.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
+
 use std::thread;
 
 #[derive(Copy, Clone)]
diff --git a/src/tools/miri/tests/fail/data_race/write_write_race.rs b/src/tools/miri/tests/fail/data_race/write_write_race.rs
index e892470..993d8d2 100644
--- a/src/tools/miri/tests/fail/data_race/write_write_race.rs
+++ b/src/tools/miri/tests/fail/data_race/write_write_race.rs
@@ -1,5 +1,7 @@
 // We want to control preemption here.
 //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::thread::spawn;
 
diff --git a/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs
index 984ae2e..8070a7f 100644
--- a/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs
+++ b/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows
+// Avoid accidental synchronization via address reuse inside `thread::spawn`.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::ptr::null_mut;
 use std::sync::atomic::{AtomicPtr, Ordering};
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr
index ab32883..42beed4 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_bad_unwind1.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding
   --> $DIR/exported_symbol_bad_unwind1.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
index 9774e1e..d88781e 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a function that cannot unwind
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
index 9774e1e..d88781e 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a function that cannot unwind
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
index f89cee0..bc3e485 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding
   --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
index 7f035eb..a2fa4c1 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/return_pointer_on_unwind.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
   --> $DIR/return_pointer_on_unwind.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
index 4723edd..447f7ca 100644
--- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 aborted execution: attempted to instantiate uninhabited type `!`
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
index 9c6dd10..bae3414 100644
--- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 aborted execution: attempted to zero-initialize type `fn()`, which is invalid
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/panic/bad_unwind.stderr b/src/tools/miri/tests/fail/panic/bad_unwind.stderr
index e4af01f..230e833 100644
--- a/src/tools/miri/tests/fail/panic/bad_unwind.stderr
+++ b/src/tools/miri/tests/fail/panic/bad_unwind.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/bad_unwind.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding
   --> $DIR/bad_unwind.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr
index e3cacbd..3b7a151 100644
--- a/src/tools/miri/tests/fail/panic/double_panic.stderr
+++ b/src/tools/miri/tests/fail/panic/double_panic.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/double_panic.rs:LL:CC:
 first
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at $DIR/double_panic.rs:LL:CC:
 second
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/panic/no_std.rs b/src/tools/miri/tests/fail/panic/no_std.rs
index bad4258..4d32b6d 100644
--- a/src/tools/miri/tests/fail/panic/no_std.rs
+++ b/src/tools/miri/tests/fail/panic/no_std.rs
@@ -1,31 +1,11 @@
+//@compile-flags: -Cpanic=abort
 #![feature(start, core_intrinsics)]
 #![no_std]
-//@compile-flags: -Cpanic=abort
-// windows tls dtors go through libstd right now, thus this test
-// cannot pass. When windows tls dtors go through the special magic
-// windows linker section, we can run this test on windows again.
-//@ignore-target-windows: no-std not supported on Windows
-
-// Plumbing to let us use `writeln!` to host stderr:
-
-extern "Rust" {
-    fn miri_write_to_stderr(bytes: &[u8]);
-}
-
-struct HostErr;
 
 use core::fmt::Write;
 
-impl Write for HostErr {
-    fn write_str(&mut self, s: &str) -> core::fmt::Result {
-        unsafe {
-            miri_write_to_stderr(s.as_bytes());
-        }
-        Ok(())
-    }
-}
-
-// Aaaand the test:
+#[path = "../../utils/mod.no_std.rs"]
+mod utils;
 
 #[start]
 fn start(_: isize, _: *const *const u8) -> isize {
@@ -34,6 +14,6 @@
 
 #[panic_handler]
 fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
-    writeln!(HostErr, "{panic_info}").ok();
+    writeln!(utils::MiriStderr, "{panic_info}").ok();
     core::intrinsics::abort(); //~ ERROR: the program aborted execution
 }
diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.stderr b/src/tools/miri/tests/fail/panic/panic_abort1.stderr
index 6045569..7694cc7 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort1.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort1.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/panic_abort1.rs:LL:CC:
 panicking from libstd
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/panic_abort/src/lib.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.stderr b/src/tools/miri/tests/fail/panic/panic_abort2.stderr
index 7bb27e4..e6a4380 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort2.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort2.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/panic_abort2.rs:LL:CC:
 42-panicking from libstd
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/panic_abort/src/lib.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.stderr b/src/tools/miri/tests/fail/panic/panic_abort3.stderr
index b46e8c2..23e2021 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort3.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort3.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/panic_abort3.rs:LL:CC:
 panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/panic_abort/src/lib.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.stderr b/src/tools/miri/tests/fail/panic/panic_abort4.stderr
index b15f720..20a0ddb 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort4.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort4.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/panic_abort4.rs:LL:CC:
 42-panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/panic_abort/src/lib.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs
index 3de5170..a6ee7b4 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs
@@ -1,4 +1,5 @@
-//@compile-flags: -Zmiri-preemption-rate=0
+// Avoid accidental synchronization via address reuse.
+//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0
 use std::thread;
 
 #[derive(Copy, Clone)]
diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs
index 25c92dd..949f659 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs
@@ -1,5 +1,6 @@
 //! Make sure that a retag acts like a read for the data race model.
-//@compile-flags: -Zmiri-preemption-rate=0
+// Avoid accidental synchronization via address reuse.
+//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0
 #[derive(Copy, Clone)]
 struct SendPtr(*mut u8);
 
diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr
index 8dbc802..f737adc 100644
--- a/src/tools/miri/tests/fail/terminate-terminator.stderr
+++ b/src/tools/miri/tests/fail/terminate-terminator.stderr
@@ -3,6 +3,7 @@
 thread 'main' panicked at $DIR/terminate-terminator.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a function that cannot unwind
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr
index cca17a0..960cae9 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr
+++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.stderr
@@ -15,13 +15,13 @@
 LL |         drop(slice1.cmp(slice2));
    |              ^^^^^^^^^^^^^^^^^^
 
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
 Uninitialized memory occurred at ALLOC[0x4..0x10], in this allocation:
 ALLOC (Rust heap, size: 32, align: 8) {
     0x00 │ 41 42 43 44 __ __ __ __ __ __ __ __ __ __ __ __ │ ABCD░░░░░░░░░░░░
     0x10 │ 00 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ .░░░░░░░░░░░░░░░
 }
 
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
 error: aborting due to 1 previous error
 
diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
index 4dc2d27..5439418 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
+++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
@@ -15,8 +15,6 @@
 LL |         drop(slice1.cmp(slice2));
    |              ^^^^^^^^^^^^^^^^^^
 
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
 Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation:
 ALLOC (Rust heap, size: 16, align: 8) {
     ╾42[ALLOC]<TAG> (1 ptr byte)╼ 12 13 ╾43[ALLOC]<TAG> (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░
@@ -28,5 +26,7 @@
     00                                              │ .
 }
 
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
 error: aborting due to 1 previous error
 
diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.stderr b/src/tools/miri/tests/fail/unwind-action-terminate.stderr
index 1323a39..7e722f7 100644
--- a/src/tools/miri/tests/fail/unwind-action-terminate.stderr
+++ b/src/tools/miri/tests/fail/unwind-action-terminate.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/unwind-action-terminate.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a function that cannot unwind
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.rs b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.rs
index dfe9397..1193ddd 100644
--- a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.rs
+++ b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.rs
@@ -1,5 +1,6 @@
 // We want to control preemption here.
-//@compile-flags: -Zmiri-preemption-rate=0
+// Avoid accidental synchronization via address reuse.
+//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0
 
 #![feature(core_intrinsics)]
 
diff --git a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.rs b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.rs
index b946a75..0a0e372 100644
--- a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.rs
+++ b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.rs
@@ -1,5 +1,6 @@
 // We want to control preemption here.
-//@compile-flags: -Zmiri-preemption-rate=0
+// Avoid accidental synchronization via address reuse.
+//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::sync::atomic::Ordering::*;
 use std::sync::atomic::{AtomicU16, AtomicU32};
diff --git a/src/tools/miri/tests/panic/alloc_error_handler_hook.rs b/src/tools/miri/tests/panic/alloc_error_handler_hook.rs
new file mode 100644
index 0000000..a1eadb4
--- /dev/null
+++ b/src/tools/miri/tests/panic/alloc_error_handler_hook.rs
@@ -0,0 +1,20 @@
+#![feature(allocator_api, alloc_error_hook)]
+
+use std::alloc::*;
+
+struct Bomb;
+impl Drop for Bomb {
+    fn drop(&mut self) {
+        eprintln!("yes we are unwinding!");
+    }
+}
+
+#[allow(unreachable_code, unused_variables)]
+fn main() {
+    // This is a particularly tricky hook, since it unwinds, which the default one does not.
+    set_alloc_error_hook(|_layout| panic!("alloc error hook called"));
+
+    let bomb = Bomb;
+    handle_alloc_error(Layout::for_value(&0));
+    std::mem::forget(bomb); // defuse unwinding bomb
+}
diff --git a/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr b/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr
new file mode 100644
index 0000000..5b309ed
--- /dev/null
+++ b/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr
@@ -0,0 +1,5 @@
+thread 'main' panicked at $DIR/alloc_error_handler_hook.rs:LL:CC:
+alloc error hook called
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+yes we are unwinding!
diff --git a/src/tools/miri/tests/panic/alloc_error_handler_panic.rs b/src/tools/miri/tests/panic/alloc_error_handler_panic.rs
new file mode 100644
index 0000000..c434e8d
--- /dev/null
+++ b/src/tools/miri/tests/panic/alloc_error_handler_panic.rs
@@ -0,0 +1,18 @@
+//@compile-flags: -Zoom=panic
+#![feature(allocator_api)]
+
+use std::alloc::*;
+
+struct Bomb;
+impl Drop for Bomb {
+    fn drop(&mut self) {
+        eprintln!("yes we are unwinding!");
+    }
+}
+
+#[allow(unreachable_code, unused_variables)]
+fn main() {
+    let bomb = Bomb;
+    handle_alloc_error(Layout::for_value(&0));
+    std::mem::forget(bomb); // defuse unwinding bomb
+}
diff --git a/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr b/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr
new file mode 100644
index 0000000..3d54577
--- /dev/null
+++ b/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr
@@ -0,0 +1,5 @@
+thread 'main' panicked at RUSTLIB/std/src/alloc.rs:LL:CC:
+memory allocation of 4 bytes failed
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+yes we are unwinding!
diff --git a/src/tools/miri/tests/panic/div-by-zero-2.stderr b/src/tools/miri/tests/panic/div-by-zero-2.stderr
index 0f088dd..f0b84ea 100644
--- a/src/tools/miri/tests/panic/div-by-zero-2.stderr
+++ b/src/tools/miri/tests/panic/div-by-zero-2.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/div-by-zero-2.rs:LL:CC:
 attempt to divide by zero
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr b/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr
index acc0e58..f77f6f0 100644
--- a/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr
+++ b/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC:
 explicit panic
 thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC:
diff --git a/src/tools/miri/tests/panic/mir-validation.rs b/src/tools/miri/tests/panic/mir-validation.rs
new file mode 100644
index 0000000..f1d0ccc
--- /dev/null
+++ b/src/tools/miri/tests/panic/mir-validation.rs
@@ -0,0 +1,26 @@
+//! Ensure that the MIR validator runs on Miri's input.
+//@rustc-env:RUSTC_ICE=0
+//@normalize-stderr-test: "\n +[0-9]+:.+" -> ""
+//@normalize-stderr-test: "\n +at .+" -> ""
+//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\].*" -> ""
+//@normalize-stderr-test: "\n[ =]*note:.*" -> ""
+//@normalize-stderr-test: "DefId\([^()]*\)" -> "DefId"
+// Somehow on rustc Windows CI, the "Miri caused an ICE" message is not shown
+// and we don't even get a regular panic; rustc aborts with a different exit code instead.
+//@ignore-host-windows
+#![feature(custom_mir, core_intrinsics)]
+use core::intrinsics::mir::*;
+
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+pub fn main() {
+    mir! {
+        let x: i32;
+        let tuple: (*mut i32,);
+        {
+            tuple.0 = core::ptr::addr_of_mut!(x);
+            // Deref at the wrong place!
+            *(tuple.0) = 1;
+            Return()
+        }
+    }
+}
diff --git a/src/tools/miri/tests/panic/mir-validation.stderr b/src/tools/miri/tests/panic/mir-validation.stderr
new file mode 100644
index 0000000..d158c99
--- /dev/null
+++ b/src/tools/miri/tests/panic/mir-validation.stderr
@@ -0,0 +1,21 @@
+thread 'rustc' panicked at compiler/rustc_const_eval/src/transform/validate.rs:LL:CC:
+broken MIR in Item(DefId) (after phase change to runtime-optimized) at bb0[1]:
+(*(_2.0: *mut i32)), has deref at the wrong place
+stack backtrace:
+
+error: the compiler unexpectedly panicked. this is a bug.
+
+
+
+
+query stack during panic:
+#0 [optimized_mir] optimizing MIR for `main`
+end of query stack
+
+Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic:
+  --> RUSTLIB/core/src/ops/function.rs:LL:CC
+   |
+LL |     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+
diff --git a/src/tools/miri/tests/panic/oob_subslice.stderr b/src/tools/miri/tests/panic/oob_subslice.stderr
index 2bccb60..c116f8e 100644
--- a/src/tools/miri/tests/panic/oob_subslice.stderr
+++ b/src/tools/miri/tests/panic/oob_subslice.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/oob_subslice.rs:LL:CC:
 range end index 5 out of range for slice of length 4
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/overflowing-lsh-neg.stderr b/src/tools/miri/tests/panic/overflowing-lsh-neg.stderr
index 2cff81c..1d057ea 100644
--- a/src/tools/miri/tests/panic/overflowing-lsh-neg.stderr
+++ b/src/tools/miri/tests/panic/overflowing-lsh-neg.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/overflowing-lsh-neg.rs:LL:CC:
 attempt to shift left with overflow
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/overflowing-rsh-1.stderr b/src/tools/miri/tests/panic/overflowing-rsh-1.stderr
index 13117df..d1a7940 100644
--- a/src/tools/miri/tests/panic/overflowing-rsh-1.stderr
+++ b/src/tools/miri/tests/panic/overflowing-rsh-1.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/overflowing-rsh-1.rs:LL:CC:
 attempt to shift right with overflow
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/overflowing-rsh-2.stderr b/src/tools/miri/tests/panic/overflowing-rsh-2.stderr
index 986a66f..612b0c0 100644
--- a/src/tools/miri/tests/panic/overflowing-rsh-2.stderr
+++ b/src/tools/miri/tests/panic/overflowing-rsh-2.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/overflowing-rsh-2.rs:LL:CC:
 attempt to shift right with overflow
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/panic2.stderr b/src/tools/miri/tests/panic/panic2.stderr
index bee1af2..792c713 100644
--- a/src/tools/miri/tests/panic/panic2.stderr
+++ b/src/tools/miri/tests/panic/panic2.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/panic2.rs:LL:CC:
 42-panicking from libstd
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/panic3.stderr b/src/tools/miri/tests/panic/panic3.stderr
index 8dac000..f8016bc 100644
--- a/src/tools/miri/tests/panic/panic3.stderr
+++ b/src/tools/miri/tests/panic/panic3.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/panic3.rs:LL:CC:
 panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/panic4.stderr b/src/tools/miri/tests/panic/panic4.stderr
index 13437fe..67410bf 100644
--- a/src/tools/miri/tests/panic/panic4.stderr
+++ b/src/tools/miri/tests/panic/panic4.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/panic4.rs:LL:CC:
 42-panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/transmute_fat2.stderr b/src/tools/miri/tests/panic/transmute_fat2.stderr
index 1f09a2c..2ee01d4 100644
--- a/src/tools/miri/tests/panic/transmute_fat2.stderr
+++ b/src/tools/miri/tests/panic/transmute_fat2.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/transmute_fat2.rs:LL:CC:
 index out of bounds: the len is 0 but the index is 0
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/unsupported_foreign_function.stderr b/src/tools/miri/tests/panic/unsupported_foreign_function.stderr
index 4db1e29..d0a7d8d 100644
--- a/src/tools/miri/tests/panic/unsupported_foreign_function.stderr
+++ b/src/tools/miri/tests/panic/unsupported_foreign_function.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/unsupported_foreign_function.rs:LL:CC:
 unsupported Miri functionality: can't call foreign function `foo` on $OS
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/unsupported_syscall.stderr b/src/tools/miri/tests/panic/unsupported_syscall.stderr
index 40ba667..f802159 100644
--- a/src/tools/miri/tests/panic/unsupported_syscall.stderr
+++ b/src/tools/miri/tests/panic/unsupported_syscall.stderr
@@ -1,3 +1,4 @@
 thread 'main' panicked at $DIR/unsupported_syscall.rs:LL:CC:
 unsupported Miri functionality: can't execute syscall with ID 0
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/pass-dep/malloc.rs b/src/tools/miri/tests/pass-dep/malloc.rs
index f5e014c..35cd137 100644
--- a/src/tools/miri/tests/pass-dep/malloc.rs
+++ b/src/tools/miri/tests/pass-dep/malloc.rs
@@ -34,9 +34,8 @@
     }
 
     unsafe {
-        let p1 = libc::malloc(20);
-
-        let p2 = libc::realloc(p1, 0);
+        // Realloc with size 0 is okay for the null pointer
+        let p2 = libc::realloc(ptr::null_mut(), 0);
         assert!(p2.is_null());
     }
 
diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.rs b/src/tools/miri/tests/pass/alloc-access-tracking.rs
index 5c782fc..29c1ee2 100644
--- a/src/tools/miri/tests/pass/alloc-access-tracking.rs
+++ b/src/tools/miri/tests/pass/alloc-access-tracking.rs
@@ -1,6 +1,6 @@
 #![feature(start)]
 #![no_std]
-//@compile-flags: -Zmiri-track-alloc-id=17 -Zmiri-track-alloc-accesses -Cpanic=abort
+//@compile-flags: -Zmiri-track-alloc-id=18 -Zmiri-track-alloc-accesses -Cpanic=abort
 //@only-target-linux: alloc IDs differ between OSes for some reason
 
 extern "Rust" {
diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.stderr b/src/tools/miri/tests/pass/alloc-access-tracking.stderr
index 5e219fa..bef1370 100644
--- a/src/tools/miri/tests/pass/alloc-access-tracking.stderr
+++ b/src/tools/miri/tests/pass/alloc-access-tracking.stderr
@@ -2,7 +2,7 @@
   --> $DIR/alloc-access-tracking.rs:LL:CC
    |
 LL |         let ptr = miri_alloc(123, 1);
-   |                   ^^^^^^^^^^^^^^^^^^ created Miri bare-metal heap allocation of 123 bytes (alignment ALIGN bytes) with id 17
+   |                   ^^^^^^^^^^^^^^^^^^ created Miri bare-metal heap allocation of 123 bytes (alignment ALIGN bytes) with id 18
    |
    = note: BACKTRACE:
    = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC
@@ -11,7 +11,7 @@
   --> $DIR/alloc-access-tracking.rs:LL:CC
    |
 LL |         *ptr = 42; // Crucially, only a write is printed here, no read!
-   |         ^^^^^^^^^ write access to allocation with id 17
+   |         ^^^^^^^^^ write access to allocation with id 18
    |
    = note: BACKTRACE:
    = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC
@@ -20,7 +20,7 @@
   --> $DIR/alloc-access-tracking.rs:LL:CC
    |
 LL |         assert_eq!(*ptr, 42);
-   |         ^^^^^^^^^^^^^^^^^^^^ read access to allocation with id 17
+   |         ^^^^^^^^^^^^^^^^^^^^ read access to allocation with id 18
    |
    = note: BACKTRACE:
    = note: inside `start` at RUSTLIB/core/src/macros/mod.rs:LL:CC
@@ -30,7 +30,7 @@
   --> $DIR/alloc-access-tracking.rs:LL:CC
    |
 LL |         miri_dealloc(ptr, 123, 1);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ freed allocation with id 17
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ freed allocation with id 18
    |
    = note: BACKTRACE:
    = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC
diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs
new file mode 100644
index 0000000..f16206f
--- /dev/null
+++ b/src/tools/miri/tests/pass/async-drop.rs
@@ -0,0 +1,191 @@
+//@revisions: stack tree
+//@compile-flags: -Zmiri-strict-provenance
+//@[tree]compile-flags: -Zmiri-tree-borrows
+#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)]
+#![allow(incomplete_features, dead_code)]
+
+// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
+use core::future::{async_drop_in_place, AsyncDrop, Future};
+use core::hint::black_box;
+use core::mem::{self, ManuallyDrop};
+use core::pin::{pin, Pin};
+use core::task::{Context, Poll, Waker};
+
+async fn test_async_drop<T>(x: T) {
+    let mut x = mem::MaybeUninit::new(x);
+    let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) });
+    test_idempotency(dtor).await;
+}
+
+fn test_idempotency<T>(mut x: Pin<&mut T>) -> impl Future<Output = ()> + '_
+where
+    T: Future<Output = ()>,
+{
+    core::future::poll_fn(move |cx| {
+        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
+        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
+        Poll::Ready(())
+    })
+}
+
+fn main() {
+    let waker = Waker::noop();
+    let mut cx = Context::from_waker(&waker);
+
+    let i = 13;
+    let fut = pin!(async {
+        test_async_drop(Int(0)).await;
+        test_async_drop(AsyncInt(0)).await;
+        test_async_drop([AsyncInt(1), AsyncInt(2)]).await;
+        test_async_drop((AsyncInt(3), AsyncInt(4))).await;
+        test_async_drop(5).await;
+        let j = 42;
+        test_async_drop(&i).await;
+        test_async_drop(&j).await;
+        test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await;
+        test_async_drop(ManuallyDrop::new(AsyncInt(9))).await;
+
+        let foo = AsyncInt(10);
+        test_async_drop(AsyncReference { foo: &foo }).await;
+
+        let foo = AsyncInt(11);
+        test_async_drop(|| {
+            black_box(foo);
+            let foo = AsyncInt(10);
+            foo
+        })
+        .await;
+
+        test_async_drop(AsyncEnum::A(AsyncInt(12))).await;
+        test_async_drop(AsyncEnum::B(SyncInt(13))).await;
+
+        test_async_drop(SyncInt(14)).await;
+        test_async_drop(SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) })
+            .await;
+
+        let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19)));
+        test_idempotency(async_drop_fut).await;
+
+        let foo = AsyncInt(20);
+        test_async_drop(async || {
+            black_box(foo);
+            let foo = AsyncInt(19);
+            // Await point there, but this is async closure so it's fine
+            black_box(core::future::ready(())).await;
+            foo
+        })
+        .await;
+
+        test_async_drop(AsyncUnion { signed: 21 }).await;
+    });
+    let res = fut.poll(&mut cx);
+    assert_eq!(res, Poll::Ready(()));
+}
+
+struct AsyncInt(i32);
+
+impl AsyncDrop for AsyncInt {
+    type Dropper<'a> = impl Future<Output = ()>;
+
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            println!("AsyncInt::Dropper::poll: {}", self.0);
+        }
+    }
+}
+
+struct SyncInt(i32);
+
+impl Drop for SyncInt {
+    fn drop(&mut self) {
+        println!("SyncInt::drop: {}", self.0);
+    }
+}
+
+struct SyncThenAsync {
+    i: i32,
+    a: AsyncInt,
+    b: SyncInt,
+    c: AsyncInt,
+}
+
+impl Drop for SyncThenAsync {
+    fn drop(&mut self) {
+        println!("SyncThenAsync::drop: {}", self.i);
+    }
+}
+
+struct AsyncReference<'a> {
+    foo: &'a AsyncInt,
+}
+
+impl AsyncDrop for AsyncReference<'_> {
+    type Dropper<'a> = impl Future<Output = ()> where Self: 'a;
+
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            println!("AsyncReference::Dropper::poll: {}", self.foo.0);
+        }
+    }
+}
+
+struct Int(i32);
+
+struct AsyncStruct {
+    i: i32,
+    a: AsyncInt,
+    b: AsyncInt,
+}
+
+impl AsyncDrop for AsyncStruct {
+    type Dropper<'a> = impl Future<Output = ()>;
+
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            println!("AsyncStruct::Dropper::poll: {}", self.i);
+        }
+    }
+}
+
+enum AsyncEnum {
+    A(AsyncInt),
+    B(SyncInt),
+}
+
+impl AsyncDrop for AsyncEnum {
+    type Dropper<'a> = impl Future<Output = ()>;
+
+    fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            let new_self = match &*self {
+                AsyncEnum::A(foo) => {
+                    println!("AsyncEnum(A)::Dropper::poll: {}", foo.0);
+                    AsyncEnum::B(SyncInt(foo.0))
+                }
+                AsyncEnum::B(foo) => {
+                    println!("AsyncEnum(B)::Dropper::poll: {}", foo.0);
+                    AsyncEnum::A(AsyncInt(foo.0))
+                }
+            };
+            mem::forget(mem::replace(&mut *self, new_self));
+        }
+    }
+}
+
+// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions
+union AsyncUnion {
+    signed: i32,
+    unsigned: u32,
+}
+
+impl AsyncDrop for AsyncUnion {
+    type Dropper<'a> = impl Future<Output = ()>;
+
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe {
+                self.unsigned
+            });
+        }
+    }
+}
diff --git a/src/tools/miri/tests/pass/async-drop.stack.stdout b/src/tools/miri/tests/pass/async-drop.stack.stdout
new file mode 100644
index 0000000..9cae433
--- /dev/null
+++ b/src/tools/miri/tests/pass/async-drop.stack.stdout
@@ -0,0 +1,22 @@
+AsyncInt::Dropper::poll: 0
+AsyncInt::Dropper::poll: 1
+AsyncInt::Dropper::poll: 2
+AsyncInt::Dropper::poll: 3
+AsyncInt::Dropper::poll: 4
+AsyncStruct::Dropper::poll: 6
+AsyncInt::Dropper::poll: 7
+AsyncInt::Dropper::poll: 8
+AsyncReference::Dropper::poll: 10
+AsyncInt::Dropper::poll: 11
+AsyncEnum(A)::Dropper::poll: 12
+SyncInt::drop: 12
+AsyncEnum(B)::Dropper::poll: 13
+AsyncInt::Dropper::poll: 13
+SyncInt::drop: 14
+SyncThenAsync::drop: 15
+AsyncInt::Dropper::poll: 16
+SyncInt::drop: 17
+AsyncInt::Dropper::poll: 18
+AsyncInt::Dropper::poll: 19
+AsyncInt::Dropper::poll: 20
+AsyncUnion::Dropper::poll: 21, 21
diff --git a/src/tools/miri/tests/pass/async-drop.tree.stdout b/src/tools/miri/tests/pass/async-drop.tree.stdout
new file mode 100644
index 0000000..9cae433
--- /dev/null
+++ b/src/tools/miri/tests/pass/async-drop.tree.stdout
@@ -0,0 +1,22 @@
+AsyncInt::Dropper::poll: 0
+AsyncInt::Dropper::poll: 1
+AsyncInt::Dropper::poll: 2
+AsyncInt::Dropper::poll: 3
+AsyncInt::Dropper::poll: 4
+AsyncStruct::Dropper::poll: 6
+AsyncInt::Dropper::poll: 7
+AsyncInt::Dropper::poll: 8
+AsyncReference::Dropper::poll: 10
+AsyncInt::Dropper::poll: 11
+AsyncEnum(A)::Dropper::poll: 12
+SyncInt::drop: 12
+AsyncEnum(B)::Dropper::poll: 13
+AsyncInt::Dropper::poll: 13
+SyncInt::drop: 14
+SyncThenAsync::drop: 15
+AsyncInt::Dropper::poll: 16
+SyncInt::drop: 17
+AsyncInt::Dropper::poll: 18
+AsyncInt::Dropper::poll: 19
+AsyncInt::Dropper::poll: 20
+AsyncUnion::Dropper::poll: 21, 21
diff --git a/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs
new file mode 100644
index 0000000..cfc1ef7
--- /dev/null
+++ b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs
@@ -0,0 +1,61 @@
+//! Regression test for <https://github.com/rust-lang/miri/issues/3450>:
+//! When the address gets reused, there should be a happens-before relation.
+//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=1.0
+#![feature(strict_provenance)]
+#![feature(sync_unsafe_cell)]
+
+use std::cell::SyncUnsafeCell;
+use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+use std::thread;
+
+static ADDR: AtomicUsize = AtomicUsize::new(0);
+static VAL: SyncUnsafeCell<i32> = SyncUnsafeCell::new(0);
+
+fn addr() -> usize {
+    let alloc = Box::new(42);
+    <*const i32>::addr(&*alloc)
+}
+
+fn thread1() {
+    unsafe {
+        VAL.get().write(42);
+    }
+    let alloc = addr();
+    ADDR.store(alloc, Relaxed);
+}
+
+fn thread2() -> bool {
+    // We try to get an allocation at the same address as the global `ADDR`. If we fail too often,
+    // just bail. `main` will try again with a different allocation.
+    for _ in 0..16 {
+        let alloc = addr();
+        let addr = ADDR.load(Relaxed);
+        if alloc == addr {
+            // We got a reuse!
+            // If the new allocation is at the same address as the old one, there must be a
+            // happens-before relationship between them. Therefore, we can read VAL without racing
+            // and must observe the write above.
+            let val = unsafe { VAL.get().read() };
+            assert_eq!(val, 42);
+            return true;
+        }
+    }
+
+    false
+}
+
+fn main() {
+    let mut success = false;
+    while !success {
+        let t1 = thread::spawn(thread1);
+        let t2 = thread::spawn(thread2);
+        t1.join().unwrap();
+        success = t2.join().unwrap();
+
+        // Reset everything.
+        ADDR.store(0, Relaxed);
+        unsafe {
+            VAL.get().write(0);
+        }
+    }
+}
diff --git a/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs b/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs
index 049b5e7..354a4be 100644
--- a/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs
+++ b/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs
@@ -1,4 +1,6 @@
 //@compile-flags: -Zmiri-disable-data-race-detector
+// Avoid non-determinism
+//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0
 
 use std::thread::spawn;
 
diff --git a/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs
new file mode 100644
index 0000000..43ddc8a
--- /dev/null
+++ b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs
@@ -0,0 +1,21 @@
+#![feature(custom_mir, core_intrinsics, strict_provenance)]
+use std::intrinsics::mir::*;
+
+// The `Drop` terminator on a type with no drop glue should be a NOP.
+
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+fn drop_in_place_with_terminator(ptr: *mut i32) {
+    mir! {
+        {
+            Drop(*ptr, ReturnTo(after_call), UnwindContinue())
+        }
+        after_call = {
+            Return()
+        }
+    }
+}
+
+pub fn main() {
+    drop_in_place_with_terminator(std::ptr::without_provenance_mut(0));
+    drop_in_place_with_terminator(std::ptr::without_provenance_mut(1));
+}
diff --git a/src/tools/miri/tests/pass/no_std.rs b/src/tools/miri/tests/pass/no_std.rs
index 3c98ee5..fc1c16f 100644
--- a/src/tools/miri/tests/pass/no_std.rs
+++ b/src/tools/miri/tests/pass/no_std.rs
@@ -2,30 +2,14 @@
 #![feature(start)]
 #![no_std]
 
-// Plumbing to let us use `writeln!` to host stdout:
-
-extern "Rust" {
-    fn miri_write_to_stdout(bytes: &[u8]);
-}
-
-struct Host;
-
 use core::fmt::Write;
 
-impl Write for Host {
-    fn write_str(&mut self, s: &str) -> core::fmt::Result {
-        unsafe {
-            miri_write_to_stdout(s.as_bytes());
-        }
-        Ok(())
-    }
-}
-
-// Aaaand the test:
+#[path = "../utils/mod.no_std.rs"]
+mod utils;
 
 #[start]
 fn start(_: isize, _: *const *const u8) -> isize {
-    writeln!(Host, "hello, world!").unwrap();
+    writeln!(utils::MiriStdout, "hello, world!").unwrap();
     0
 }
 
diff --git a/src/tools/miri/tests/pass/panic/catch_panic.stderr b/src/tools/miri/tests/pass/panic/catch_panic.stderr
index cbcd626..a472a5d 100644
--- a/src/tools/miri/tests/pass/panic/catch_panic.stderr
+++ b/src/tools/miri/tests/pass/panic/catch_panic.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/catch_panic.rs:LL:CC:
 Hello from std::panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 Caught panic message (&str): Hello from std::panic
 thread 'main' panicked at $DIR/catch_panic.rs:LL:CC:
 Hello from std::panic: 1
diff --git a/src/tools/miri/tests/pass/panic/concurrent-panic.stderr b/src/tools/miri/tests/pass/panic/concurrent-panic.stderr
index 0bb3dcd..b2a5cf4 100644
--- a/src/tools/miri/tests/pass/panic/concurrent-panic.stderr
+++ b/src/tools/miri/tests/pass/panic/concurrent-panic.stderr
@@ -3,6 +3,7 @@
 thread '<unnamed>' panicked at $DIR/concurrent-panic.rs:LL:CC:
 panic in thread 2
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 Thread 2 blocking on thread 1
 Thread 2 reported it has started
 Unlocking mutex
diff --git a/src/tools/miri/tests/pass/panic/nested_panic_caught.stderr b/src/tools/miri/tests/pass/panic/nested_panic_caught.stderr
index 4684beb..3efb4be 100644
--- a/src/tools/miri/tests/pass/panic/nested_panic_caught.stderr
+++ b/src/tools/miri/tests/pass/panic/nested_panic_caught.stderr
@@ -1,6 +1,7 @@
 thread 'main' panicked at $DIR/nested_panic_caught.rs:LL:CC:
 once
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at $DIR/nested_panic_caught.rs:LL:CC:
 twice
 stack backtrace:
diff --git a/src/tools/miri/tests/pass/panic/thread_panic.stderr b/src/tools/miri/tests/pass/panic/thread_panic.stderr
index badd409..bdfe4f9 100644
--- a/src/tools/miri/tests/pass/panic/thread_panic.stderr
+++ b/src/tools/miri/tests/pass/panic/thread_panic.stderr
@@ -1,5 +1,6 @@
 thread '<unnamed>' panicked at $DIR/thread_panic.rs:LL:CC:
 Hello!
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'childthread' panicked at $DIR/thread_panic.rs:LL:CC:
 Hello, world!
diff --git a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
index 43ba490..c75824d 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
@@ -265,13 +265,15 @@
     assert_eq!(*x, 1337); // oops, the value changed! I guess not all pointers were invalidated
 }
 
-fn box_into_raw_allows_interior_mutable_alias() { unsafe {
-    let b = Box::new(std::cell::Cell::new(42));
-    let raw = Box::into_raw(b);
-    let c = &*raw;
-    let d = raw.cast::<i32>(); // bypassing `Cell` -- only okay in Miri tests
-    // `c` and `d` should permit arbitrary aliasing with each other now.
-    *d = 1;
-    c.set(2);
-    drop(Box::from_raw(raw));
-} }
+fn box_into_raw_allows_interior_mutable_alias() {
+    unsafe {
+        let b = Box::new(std::cell::Cell::new(42));
+        let raw = Box::into_raw(b);
+        let c = &*raw;
+        let d = raw.cast::<i32>(); // bypassing `Cell` -- only okay in Miri tests
+        // `c` and `d` should permit arbitrary aliasing with each other now.
+        *d = 1;
+        c.set(2);
+        drop(Box::from_raw(raw));
+    }
+}
diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.rs b/src/tools/miri/tests/pass/tree_borrows/reserved.rs
index 87ce91a..f93cac8 100644
--- a/src/tools/miri/tests/pass/tree_borrows/reserved.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/reserved.rs
@@ -27,9 +27,8 @@
     }
 }
 
-unsafe fn print(msg: &str) {
-    utils::miri_write_to_stderr(msg.as_bytes());
-    utils::miri_write_to_stderr("\n".as_bytes());
+fn print(msg: &str) {
+    eprintln!("{msg}");
 }
 
 unsafe fn read_second<T>(x: &mut T, y: *mut u8) {
diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs
index e10ccc2..dac63ee 100644
--- a/src/tools/miri/tests/pass/weak_memory/weak.rs
+++ b/src/tools/miri/tests/pass/weak_memory/weak.rs
@@ -37,6 +37,8 @@
     let x = static_atomic(0);
     let j1 = spawn(move || {
         x.store(1, Relaxed);
+        // Preemption is disabled, so the store above will never be the
+        // latest store visible to another thread.
         x.store(2, Relaxed);
     });
 
@@ -138,6 +140,7 @@
 }
 
 /// Asserts that the function returns true at least once in 100 runs
+#[track_caller]
 fn assert_once(f: fn() -> bool) {
     assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x));
 }
diff --git a/src/tools/miri/tests/utils/io.rs b/src/tools/miri/tests/utils/io.rs
new file mode 100644
index 0000000..e3eaa6c
--- /dev/null
+++ b/src/tools/miri/tests/utils/io.rs
@@ -0,0 +1,25 @@
+use core::fmt::{self, Write};
+
+use super::miri_extern;
+
+pub struct MiriStderr;
+
+impl Write for MiriStderr {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        unsafe {
+            miri_extern::miri_write_to_stderr(s.as_bytes());
+        }
+        Ok(())
+    }
+}
+
+pub struct MiriStdout;
+
+impl Write for MiriStdout {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        unsafe {
+            miri_extern::miri_write_to_stdout(s.as_bytes());
+        }
+        Ok(())
+    }
+}
diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs
index e2983f6..d6c43b1 100644
--- a/src/tools/miri/tests/utils/miri_extern.rs
+++ b/src/tools/miri/tests/utils/miri_extern.rs
@@ -133,8 +133,8 @@
     /// with a null terminator.
     /// Returns 0 if the `out` buffer was large enough, and the required size otherwise.
     pub fn miri_host_to_target_path(
-        path: *const std::ffi::c_char,
-        out: *mut std::ffi::c_char,
+        path: *const core::ffi::c_char,
+        out: *mut core::ffi::c_char,
         out_size: usize,
     ) -> usize;
 
diff --git a/src/tools/miri/tests/utils/mod.no_std.rs b/src/tools/miri/tests/utils/mod.no_std.rs
new file mode 100644
index 0000000..aaf2bf5
--- /dev/null
+++ b/src/tools/miri/tests/utils/mod.no_std.rs
@@ -0,0 +1,11 @@
+#![allow(dead_code)]
+#![allow(unused_imports)]
+
+#[macro_use]
+mod macros;
+
+mod io;
+mod miri_extern;
+
+pub use self::io::*;
+pub use self::miri_extern::*;
diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs
index cb9380f..138ada4 100644
--- a/src/tools/miri/tests/utils/mod.rs
+++ b/src/tools/miri/tests/utils/mod.rs
@@ -5,9 +5,11 @@
 mod macros;
 
 mod fs;
+mod io;
 mod miri_extern;
 
 pub use self::fs::*;
+pub use self::io::*;
 pub use self::miri_extern::*;
 
 pub fn run_provenance_gc() {
diff --git a/src/tools/run-make-support/src/clang.rs b/src/tools/run-make-support/src/clang.rs
new file mode 100644
index 0000000..ed9f838
--- /dev/null
+++ b/src/tools/run-make-support/src/clang.rs
@@ -0,0 +1,73 @@
+use std::env;
+use std::path::Path;
+use std::process::Command;
+
+use crate::{bin_name, handle_failed_output, tmp_dir};
+
+/// Construct a new `clang` invocation. `clang` is not always available for all targets.
+pub fn clang() -> Clang {
+    Clang::new()
+}
+
+/// A `clang` invocation builder.
+#[derive(Debug)]
+pub struct Clang {
+    cmd: Command,
+}
+
+crate::impl_common_helpers!(Clang);
+
+impl Clang {
+    /// Construct a new `clang` invocation. `clang` is not always available for all targets.
+    pub fn new() -> Self {
+        let clang =
+            env::var("CLANG").expect("`CLANG` not specified, but this is required to find `clang`");
+        let cmd = Command::new(clang);
+        Self { cmd }
+    }
+
+    /// Provide an input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+
+    /// Specify the name of the executable. The executable will be placed under `$TMPDIR`, and the
+    /// extension will be determined by [`bin_name`].
+    pub fn out_exe(&mut self, name: &str) -> &mut Self {
+        self.cmd.arg("-o");
+        self.cmd.arg(tmp_dir().join(bin_name(name)));
+        self
+    }
+
+    /// Specify which target triple clang should target.
+    pub fn target(&mut self, target_triple: &str) -> &mut Self {
+        self.cmd.arg("-target");
+        self.cmd.arg(target_triple);
+        self
+    }
+
+    /// Pass `-nostdlib` to disable linking the C standard library.
+    pub fn no_stdlib(&mut self) -> &mut Self {
+        self.cmd.arg("-nostdlib");
+        self
+    }
+
+    /// Specify architecture.
+    pub fn arch(&mut self, arch: &str) -> &mut Self {
+        self.cmd.arg(format!("-march={arch}"));
+        self
+    }
+
+    /// Specify LTO settings.
+    pub fn lto(&mut self, lto: &str) -> &mut Self {
+        self.cmd.arg(format!("-flto={lto}"));
+        self
+    }
+
+    /// Specify which ld to use.
+    pub fn use_ld(&mut self, ld: &str) -> &mut Self {
+        self.cmd.arg(format!("-fuse-ld={ld}"));
+        self
+    }
+}
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 9a4fdff..e723e82 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -4,6 +4,8 @@
 //! as `object` or `wasmparser`, they can be re-exported and be made available through this library.
 
 pub mod cc;
+pub mod clang;
+pub mod llvm_readobj;
 pub mod run;
 pub mod rustc;
 pub mod rustdoc;
@@ -17,6 +19,8 @@
 pub use wasmparser;
 
 pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc};
+pub use clang::{clang, Clang};
+pub use llvm_readobj::{llvm_readobj, LlvmReadobj};
 pub use run::{run, run_fail};
 pub use rustc::{aux_build, rustc, Rustc};
 pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
diff --git a/src/tools/run-make-support/src/llvm_readobj.rs b/src/tools/run-make-support/src/llvm_readobj.rs
new file mode 100644
index 0000000..32ea07e
--- /dev/null
+++ b/src/tools/run-make-support/src/llvm_readobj.rs
@@ -0,0 +1,44 @@
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use crate::handle_failed_output;
+
+/// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
+/// at `$LLVM_BIN_DIR/llvm-readobj`.
+pub fn llvm_readobj() -> LlvmReadobj {
+    LlvmReadobj::new()
+}
+
+/// A `llvm-readobj` invocation builder.
+#[derive(Debug)]
+pub struct LlvmReadobj {
+    cmd: Command,
+}
+
+crate::impl_common_helpers!(LlvmReadobj);
+
+impl LlvmReadobj {
+    /// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
+    /// at `$LLVM_BIN_DIR/llvm-readobj`.
+    pub fn new() -> Self {
+        let llvm_bin_dir = env::var("LLVM_BIN_DIR")
+            .expect("`LLVM_BIN_DIR` not specified, but this is required to find `llvm-readobj`");
+        let llvm_bin_dir = PathBuf::from(llvm_bin_dir);
+        let llvm_readobj = llvm_bin_dir.join("llvm-readobj");
+        let cmd = Command::new(llvm_readobj);
+        Self { cmd }
+    }
+
+    /// Provide an input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+
+    /// Pass `--file-header` to display file headers.
+    pub fn file_header(&mut self) -> &mut Self {
+        self.cmd.arg("--file-header");
+        self
+    }
+}
diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md
index 97c1b64..0d99d06 100644
--- a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md
@@ -22,6 +22,8 @@
 
 **rustc version**: (eg. output of `rustc -V`)
 
+**editor or extension**: (eg. VSCode, Vim, Emacs, etc. For VSCode users, specify your extension version; for users of other editors, provide the distribution if applicable)
+
 **relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTC`, `RUSTUP_HOME` or `CARGO_HOME`)
 
 **repository link (if public, optional)**: (eg. [rust-analyzer](https://github.com/rust-lang/rust-analyzer))
diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml
index 08ad10c..a10345a 100644
--- a/src/tools/rust-analyzer/.github/workflows/ci.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml
@@ -91,7 +91,7 @@
         run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }}
 
       - name: Test
-        if: matrix.os == 'ubuntu-latest' || github.event_name == 'push'
+        if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' || github.event_name == 'push'
         run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet
 
       - name: Switch to stable toolchain
diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml
index de61b23..b6cd4a7 100644
--- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml
@@ -58,7 +58,7 @@
           key: ${{ runner.os }}-target-${{ github.sha }}
 
       - name: Upload build metrics
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: build-${{ github.sha }}
           path: target/build.json
@@ -95,7 +95,7 @@
         run: cargo xtask metrics "${{ matrix.names }}"
 
       - name: Upload metrics
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: ${{ matrix.names }}-${{ github.sha }}
           path: target/${{ matrix.names }}.json
@@ -109,32 +109,32 @@
         uses: actions/checkout@v4
 
       - name: Download build metrics
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@v4
         with:
           name: build-${{ github.sha }}
 
       - name: Download self metrics
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@v4
         with:
           name: self-${{ github.sha }}
 
       - name: Download ripgrep-13.0.0 metrics
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@v4
         with:
           name: ripgrep-13.0.0-${{ github.sha }}
 
       - name: Download webrender-2022 metrics
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@v4
         with:
           name: webrender-2022-${{ github.sha }}
 
       - name: Download diesel-1.4.8 metrics
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@v4
         with:
           name: diesel-1.4.8-${{ github.sha }}
 
       - name: Download hyper-0.14.18 metrics
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@v4
         with:
           name: hyper-0.14.18-${{ github.sha }}
 
diff --git a/src/tools/rust-analyzer/CONTRIBUTING.md b/src/tools/rust-analyzer/CONTRIBUTING.md
new file mode 100644
index 0000000..da65b03
--- /dev/null
+++ b/src/tools/rust-analyzer/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# Contributing to rust-analyzer
+
+Thank you for your interest in contributing to rust-analyzer! There are many ways to contribute
+and we appreciate all of them.
+
+To get a quick overview of the crates and structure of the project take a look at the
+[./docs/dev](./docs/dev) folder.
+
+If you have any questions please ask them in the [rust-analyzer zulip stream](
+https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer) or if unsure where
+to start out when working on a concrete issue drop a comment on the related issue for mentoring
+instructions (general discussions are recommended to happen on zulip though).
+
+## Fixing a bug or improving a feature
+
+Generally it's fine to just work on these kinds of things and put a pull-request out for it. If there
+is an issue accompanying it make sure to link it in the pull request description so it can be closed
+afterwards or linked for context.
+
+If you want to find something to fix or work on keep a look out for the `C-bug` and `C-enhancement`
+labels.
+
+## Implementing a new feature
+
+It's advised to first open an issue for any kind of new feature so the team can tell upfront whether
+the feature is desirable or not before any implementation work happens. We want to minimize the
+possibility of someone putting a lot of work into a feature that is then going to waste as we deem
+it out of scope (be it due to generally not fitting in with rust-analyzer, or just not having the
+maintenance capacity). If there already is a feature issue open but it is not clear whether it is
+considered accepted feel free to just drop a comment and ask!
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index c7cf447..a6e4601 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -161,9 +161,9 @@
 
 [[package]]
 name = "chalk-derive"
-version = "0.96.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5676cea088c32290fe65c82895be9d06dd21e0fa49bb97ca840529e9417ab71a"
+checksum = "92a0aedc4ac2adc5c0b7dc9ec38c5c816284ad28da6d4ecd01873b9683f54972"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -173,20 +173,19 @@
 
 [[package]]
 name = "chalk-ir"
-version = "0.96.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff550c2cdd63ff74394214dce03d06386928a641c0f08837535f04af573a966d"
+checksum = "db18493569b190f7266a04901e520fc3a5c00564475154287906f8a27302c119"
 dependencies = [
  "bitflags 2.4.2",
  "chalk-derive",
- "lazy_static",
 ]
 
 [[package]]
 name = "chalk-recursive"
-version = "0.96.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4559e5c9b200240453b07d893f9c3c74413b53b0d33cbe272c68b0b77aa1c3"
+checksum = "ae4ba8ce5bd2e1b59f1f79495bc8704db09a8285e51cc5ddf01d9baee1bf447d"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -197,9 +196,9 @@
 
 [[package]]
 name = "chalk-solve"
-version = "0.96.0"
+version = "0.97.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0882e68ce9eb5a0a2413806538494d19df6ee520ab17d1faf489e952f32e98b8"
+checksum = "b2ec1b3b7f7b1ec38f099ef39c2bc3ea29335be1b8316d114baff46d96d131e9"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -552,6 +551,7 @@
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "limit",
  "mbe",
+ "parser",
  "rustc-hash",
  "smallvec",
  "span",
@@ -695,6 +695,7 @@
 dependencies = [
  "arrayvec",
  "base-db",
+ "bitflags 2.4.2",
  "cov-mark",
  "crossbeam-channel",
  "either",
@@ -781,6 +782,7 @@
 dependencies = [
  "equivalent",
  "hashbrown",
+ "serde",
 ]
 
 [[package]]
@@ -1594,6 +1596,7 @@
  "ide",
  "ide-db",
  "ide-ssr",
+ "indexmap",
  "itertools",
  "load-cargo",
  "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1622,6 +1625,7 @@
  "test-fixture",
  "test-utils",
  "tikv-jemallocator",
+ "toml",
  "toolchain",
  "tracing",
  "tracing-subscriber",
@@ -1776,6 +1780,15 @@
 ]
 
 [[package]]
+name = "serde_spanned"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
 name = "sharded-slab"
 version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1822,6 +1835,7 @@
  "salsa",
  "stdx",
  "syntax",
+ "text-size",
  "vfs",
 ]
 
@@ -2026,6 +2040,40 @@
 checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
+name = "toml"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
 name = "toolchain"
 version = "0.0.0"
 dependencies = [
@@ -2402,6 +2450,15 @@
 checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
 
 [[package]]
+name = "winnow"
+version = "0.5.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "write-json"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index d9343d2..f7e3ae5 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -22,6 +22,7 @@
 text-size.opt-level = 3
 # This speeds up `cargo xtask dist`.
 miniz_oxide.opt-level = 3
+salsa.opt-level = 3
 
 [profile.release]
 incremental = true
@@ -106,10 +107,10 @@
 bitflags = "2.4.1"
 cargo_metadata = "0.18.1"
 camino = "1.1.6"
-chalk-solve = { version = "0.96.0", default-features = false }
-chalk-ir = "0.96.0"
-chalk-recursive = { version = "0.96.0", default-features = false }
-chalk-derive = "0.96.0"
+chalk-solve = { version = "0.97.0", default-features = false }
+chalk-ir = "0.97.0"
+chalk-recursive = { version = "0.97.0", default-features = false }
+chalk-derive = "0.97.0"
 command-group = "2.0.1"
 crossbeam-channel = "0.5.8"
 dissimilar = "1.0.7"
@@ -188,6 +189,8 @@
 new_ret_no_self = "allow"
 # Has a bunch of false positives
 useless_asref = "allow"
+# Has false positives
+assigning_clones = "allow"
 
 ## Following lints should be tackled at some point
 too_many_arguments = "allow"
diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md
index 8c3f6f8..552f71f 100644
--- a/src/tools/rust-analyzer/README.md
+++ b/src/tools/rust-analyzer/README.md
@@ -13,8 +13,9 @@
 
 ## Documentation
 
-If you want to **contribute** to rust-analyzer or are just curious about how
-things work under the hood, check the [./docs/dev](./docs/dev) folder.
+If you want to **contribute** to rust-analyzer check out the [CONTRIBUTING.md](./CONTRIBUTING.md) or
+if you are just curious about how things work under the hood, check the [./docs/dev](./docs/dev)
+folder.
 
 If you want to **use** rust-analyzer's language server with your editor of
 choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder.
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index 27eb05c..240af79 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -19,6 +19,10 @@
 // Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
 // then the crate for the proc-macro hasn't been build yet as the build data is missing.
 pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct SourceRootId(pub u32);
+
 /// Files are grouped into source roots. A source root is a directory on the
 /// file systems which is watched for changes. Typically it corresponds to a
 /// Rust crate. Source roots *might* be nested: in this case, a file belongs to
@@ -26,9 +30,6 @@
 /// source root, and the analyzer does not know the root path of the source root at
 /// all. So, a file from one source root can't refer to a file in another source
 /// root by path.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct SourceRootId(pub u32);
-
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct SourceRoot {
     /// Sysroot or crates.io library.
@@ -285,20 +286,39 @@
     /// For purposes of analysis, crates are anonymous (only names in
     /// `Dependency` matters), this name should only be used for UI.
     pub display_name: Option<CrateDisplayName>,
-    pub cfg_options: CfgOptions,
+    pub cfg_options: Arc<CfgOptions>,
     /// The cfg options that could be used by the crate
-    pub potential_cfg_options: Option<CfgOptions>,
+    pub potential_cfg_options: Option<Arc<CfgOptions>>,
     pub env: Env,
     pub dependencies: Vec<Dependency>,
     pub origin: CrateOrigin,
     pub is_proc_macro: bool,
 }
 
-#[derive(Default, Debug, Clone, PartialEq, Eq)]
+#[derive(Default, Clone, PartialEq, Eq)]
 pub struct Env {
     entries: FxHashMap<String, String>,
 }
 
+impl fmt::Debug for Env {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        struct EnvDebug<'s>(Vec<(&'s String, &'s String)>);
+
+        impl fmt::Debug for EnvDebug<'_> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.debug_map().entries(self.0.iter().copied()).finish()
+            }
+        }
+        f.debug_struct("Env")
+            .field("entries", &{
+                let mut entries: Vec<_> = self.entries.iter().collect();
+                entries.sort();
+                EnvDebug(entries)
+            })
+            .finish()
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Dependency {
     pub crate_id: CrateId,
@@ -328,12 +348,13 @@
         edition: Edition,
         display_name: Option<CrateDisplayName>,
         version: Option<String>,
-        cfg_options: CfgOptions,
-        potential_cfg_options: Option<CfgOptions>,
-        env: Env,
+        cfg_options: Arc<CfgOptions>,
+        potential_cfg_options: Option<Arc<CfgOptions>>,
+        mut env: Env,
         is_proc_macro: bool,
         origin: CrateOrigin,
     ) -> CrateId {
+        env.entries.shrink_to_fit();
         let data = CrateData {
             root_file_id,
             edition,
@@ -650,16 +671,24 @@
 }
 
 impl Env {
-    pub fn set(&mut self, env: &str, value: String) {
-        self.entries.insert(env.to_owned(), value);
+    pub fn set(&mut self, env: &str, value: impl Into<String>) {
+        self.entries.insert(env.to_owned(), value.into());
     }
 
     pub fn get(&self, env: &str) -> Option<String> {
         self.entries.get(env).cloned()
     }
 
-    pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
-        self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str()))
+    pub fn extend_from_other(&mut self, other: &Env) {
+        self.entries.extend(other.entries.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
+    }
+}
+
+impl From<Env> for Vec<(String, String)> {
+    fn from(env: Env) -> Vec<(String, String)> {
+        let mut entries: Vec<_> = env.entries.into_iter().collect();
+        entries.sort();
+        entries
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index 785ff9c..2b64a07 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -45,7 +45,7 @@
 
 pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16;
 pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
-pub const DEFAULT_BORROWCK_LRU_CAP: usize = 1024;
+pub const DEFAULT_BORROWCK_LRU_CAP: usize = 2024;
 
 pub trait FileLoader {
     /// Text of the file.
@@ -83,7 +83,8 @@
 fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
     let _p = tracing::span!(tracing::Level::INFO, "parse_query", ?file_id).entered();
     let text = db.file_text(file_id);
-    SourceFile::parse(&text)
+    // FIXME: Edition based parsing
+    SourceFile::parse(&text, span::Edition::CURRENT)
 }
 
 /// We don't want to give HIR knowledge of source roots, hence we extract these
diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
index 454d6fc..9a36588 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
@@ -58,13 +58,6 @@
         self.enabled.insert(CfgAtom::KeyValue { key, value });
     }
 
-    pub fn difference<'a>(
-        &'a self,
-        other: &'a CfgOptions,
-    ) -> impl Iterator<Item = &'a CfgAtom> + 'a {
-        self.enabled.difference(&other.enabled)
-    }
-
     pub fn apply_diff(&mut self, diff: CfgDiff) {
         for atom in diff.enable {
             self.enabled.insert(atom);
diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs
index 62fb429..a1ae15f 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs
@@ -1,12 +1,12 @@
 use arbitrary::{Arbitrary, Unstructured};
 use expect_test::{expect, Expect};
 use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY};
-use syntax::{ast, AstNode};
+use syntax::{ast, AstNode, Edition};
 
 use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
 
 fn assert_parse_result(input: &str, expected: CfgExpr) {
-    let source_file = ast::SourceFile::parse(input).ok().unwrap();
+    let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
     let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY);
     let cfg = CfgExpr::parse(&tt);
@@ -14,7 +14,7 @@
 }
 
 fn check_dnf(input: &str, expect: Expect) {
-    let source_file = ast::SourceFile::parse(input).ok().unwrap();
+    let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
     let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY);
     let cfg = CfgExpr::parse(&tt);
@@ -23,7 +23,7 @@
 }
 
 fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
-    let source_file = ast::SourceFile::parse(input).ok().unwrap();
+    let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
     let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY);
     let cfg = CfgExpr::parse(&tt);
@@ -34,7 +34,7 @@
 
 #[track_caller]
 fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
-    let source_file = ast::SourceFile::parse(input).ok().unwrap();
+    let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
     let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY);
     let cfg = CfgExpr::parse(&tt);
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/command.rs b/src/tools/rust-analyzer/crates/flycheck/src/command.rs
index 091146a..8ba7018 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/command.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/command.rs
@@ -4,12 +4,13 @@
 use std::{
     ffi::OsString,
     fmt, io,
+    marker::PhantomData,
     path::PathBuf,
     process::{ChildStderr, ChildStdout, Command, Stdio},
 };
 
 use command_group::{CommandGroup, GroupChild};
-use crossbeam_channel::{unbounded, Receiver, Sender};
+use crossbeam_channel::Sender;
 use stdx::process::streaming_output;
 
 /// Cargo output is structured as a one JSON per line. This trait abstracts parsing one line of
@@ -99,10 +100,10 @@
     /// a read syscall dropping and therefore terminating the process is our best option.
     child: JodGroupChild,
     thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
-    pub(crate) receiver: Receiver<T>,
     program: OsString,
     arguments: Vec<OsString>,
     current_dir: Option<PathBuf>,
+    _phantom: PhantomData<T>,
 }
 
 impl<T> fmt::Debug for CommandHandle<T> {
@@ -116,7 +117,7 @@
 }
 
 impl<T: ParseFromLine> CommandHandle<T> {
-    pub(crate) fn spawn(mut command: Command) -> std::io::Result<Self> {
+    pub(crate) fn spawn(mut command: Command, sender: Sender<T>) -> std::io::Result<Self> {
         command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
         let mut child = command.group_spawn().map(JodGroupChild)?;
 
@@ -127,13 +128,12 @@
         let stdout = child.0.inner().stdout.take().unwrap();
         let stderr = child.0.inner().stderr.take().unwrap();
 
-        let (sender, receiver) = unbounded();
         let actor = CargoActor::<T>::new(sender, stdout, stderr);
         let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
             .name("CommandHandle".to_owned())
             .spawn(move || actor.run())
             .expect("failed to spawn thread");
-        Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver })
+        Ok(CommandHandle { program, arguments, current_dir, child, thread, _phantom: PhantomData })
     }
 
     pub(crate) fn cancel(mut self) {
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
index 4ee8695..5dfaaf7 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
@@ -42,18 +42,49 @@
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
+pub struct CargoOptions {
+    pub target_triples: Vec<String>,
+    pub all_targets: bool,
+    pub no_default_features: bool,
+    pub all_features: bool,
+    pub features: Vec<String>,
+    pub extra_args: Vec<String>,
+    pub extra_env: FxHashMap<String, String>,
+    pub target_dir: Option<Utf8PathBuf>,
+}
+
+impl CargoOptions {
+    fn apply_on_command(&self, cmd: &mut Command) {
+        for target in &self.target_triples {
+            cmd.args(["--target", target.as_str()]);
+        }
+        if self.all_targets {
+            cmd.arg("--all-targets");
+        }
+        if self.all_features {
+            cmd.arg("--all-features");
+        } else {
+            if self.no_default_features {
+                cmd.arg("--no-default-features");
+            }
+            if !self.features.is_empty() {
+                cmd.arg("--features");
+                cmd.arg(self.features.join(" "));
+            }
+        }
+        if let Some(target_dir) = &self.target_dir {
+            cmd.arg("--target-dir").arg(target_dir);
+        }
+        cmd.envs(&self.extra_env);
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum FlycheckConfig {
     CargoCommand {
         command: String,
-        target_triples: Vec<String>,
-        all_targets: bool,
-        no_default_features: bool,
-        all_features: bool,
-        features: Vec<String>,
-        extra_args: Vec<String>,
-        extra_env: FxHashMap<String, String>,
+        options: CargoOptions,
         ansi_color_output: bool,
-        target_dir: Option<Utf8PathBuf>,
     },
     CustomCommand {
         command: String,
@@ -184,6 +215,8 @@
     /// have to wrap sub-processes output handling in a thread and pass messages
     /// back over a channel.
     command_handle: Option<CommandHandle<CargoCheckMessage>>,
+    /// The receiver side of the channel mentioned above.
+    command_receiver: Option<Receiver<CargoCheckMessage>>,
 }
 
 enum Event {
@@ -209,6 +242,7 @@
             sysroot_root,
             root: workspace_root,
             command_handle: None,
+            command_receiver: None,
         }
     }
 
@@ -217,14 +251,13 @@
     }
 
     fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
-        let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver);
         if let Ok(msg) = inbox.try_recv() {
             // give restarts a preference so check outputs don't block a restart or stop
             return Some(Event::RequestStateChange(msg));
         }
         select! {
             recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange),
-            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
+            recv(self.command_receiver.as_ref().unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
         }
     }
 
@@ -253,10 +286,12 @@
                     let formatted_command = format!("{:?}", command);
 
                     tracing::debug!(?command, "will restart flycheck");
-                    match CommandHandle::spawn(command) {
+                    let (sender, receiver) = unbounded();
+                    match CommandHandle::spawn(command, sender) {
                         Ok(command_handle) => {
-                            tracing::debug!(command = formatted_command, "did  restart flycheck");
+                            tracing::debug!(command = formatted_command, "did restart flycheck");
                             self.command_handle = Some(command_handle);
+                            self.command_receiver = Some(receiver);
                             self.report_progress(Progress::DidStart);
                         }
                         Err(error) => {
@@ -272,13 +307,15 @@
 
                     // Watcher finished
                     let command_handle = self.command_handle.take().unwrap();
+                    self.command_receiver.take();
                     let formatted_handle = format!("{:?}", command_handle);
 
                     let res = command_handle.join();
-                    if res.is_err() {
+                    if let Err(error) = &res {
                         tracing::error!(
-                            "Flycheck failed to run the following command: {}",
-                            formatted_handle
+                            "Flycheck failed to run the following command: {}, error={}",
+                            formatted_handle,
+                            error
                         );
                     }
                     self.report_progress(Progress::DidFinish(res));
@@ -332,18 +369,7 @@
         saved_file: Option<&AbsPath>,
     ) -> Option<Command> {
         let (mut cmd, args) = match &self.config {
-            FlycheckConfig::CargoCommand {
-                command,
-                target_triples,
-                no_default_features,
-                all_targets,
-                all_features,
-                extra_args,
-                features,
-                extra_env,
-                ansi_color_output,
-                target_dir,
-            } => {
+            FlycheckConfig::CargoCommand { command, options, ansi_color_output } => {
                 let mut cmd = Command::new(Tool::Cargo.path());
                 if let Some(sysroot_root) = &self.sysroot_root {
                     cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root));
@@ -365,28 +391,8 @@
                 cmd.arg("--manifest-path");
                 cmd.arg(self.root.join("Cargo.toml"));
 
-                for target in target_triples {
-                    cmd.args(["--target", target.as_str()]);
-                }
-                if *all_targets {
-                    cmd.arg("--all-targets");
-                }
-                if *all_features {
-                    cmd.arg("--all-features");
-                } else {
-                    if *no_default_features {
-                        cmd.arg("--no-default-features");
-                    }
-                    if !features.is_empty() {
-                        cmd.arg("--features");
-                        cmd.arg(features.join(" "));
-                    }
-                }
-                if let Some(target_dir) = target_dir {
-                    cmd.arg("--target-dir").arg(target_dir);
-                }
-                cmd.envs(extra_env);
-                (cmd, extra_args.clone())
+                options.apply_on_command(&mut cmd);
+                (cmd, options.extra_args.clone())
             }
             FlycheckConfig::CustomCommand {
                 command,
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs
index 9f761c9..c136dd1 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs
@@ -3,11 +3,15 @@
 
 use std::process::Command;
 
-use crossbeam_channel::Receiver;
+use crossbeam_channel::Sender;
+use paths::AbsPath;
 use serde::Deserialize;
 use toolchain::Tool;
 
-use crate::command::{CommandHandle, ParseFromLine};
+use crate::{
+    command::{CommandHandle, ParseFromLine},
+    CargoOptions,
+};
 
 #[derive(Debug, Deserialize)]
 #[serde(tag = "event", rename_all = "camelCase")]
@@ -51,30 +55,34 @@
 
 #[derive(Debug)]
 pub struct CargoTestHandle {
-    handle: CommandHandle<CargoTestMessage>,
+    _handle: CommandHandle<CargoTestMessage>,
 }
 
 // Example of a cargo test command:
 // cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json
 
 impl CargoTestHandle {
-    pub fn new(path: Option<&str>) -> std::io::Result<Self> {
+    pub fn new(
+        path: Option<&str>,
+        options: CargoOptions,
+        root: &AbsPath,
+        sender: Sender<CargoTestMessage>,
+    ) -> std::io::Result<Self> {
         let mut cmd = Command::new(Tool::Cargo.path());
         cmd.env("RUSTC_BOOTSTRAP", "1");
         cmd.arg("test");
         cmd.arg("--workspace");
         // --no-fail-fast is needed to ensure that all requested tests will run
         cmd.arg("--no-fail-fast");
+        cmd.arg("--manifest-path");
+        cmd.arg(root.join("Cargo.toml"));
+        options.apply_on_command(&mut cmd);
         cmd.arg("--");
         if let Some(path) = path {
             cmd.arg(path);
         }
         cmd.args(["-Z", "unstable-options"]);
         cmd.arg("--format=json");
-        Ok(Self { handle: CommandHandle::spawn(cmd)? })
-    }
-
-    pub fn receiver(&self) -> &Receiver<CargoTestMessage> {
-        &self.handle.receiver
+        Ok(Self { _handle: CommandHandle::spawn(cmd, sender)? })
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 523ff6f..41c59ea 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -54,7 +54,7 @@
 test-fixture.workspace = true
 
 [features]
-in-rust-tree = []
+in-rust-tree = ["hir-expand/in-rust-tree"]
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index fa7730f..d9eeffd 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -5,7 +5,7 @@
 #[cfg(test)]
 mod tests;
 
-use std::{hash::Hash, ops, slice::Iter as SliceIter};
+use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter};
 
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
@@ -141,6 +141,10 @@
         }
     }
 
+    pub fn cfgs(&self) -> impl Iterator<Item = CfgExpr> + '_ {
+        self.by_key("cfg").tt_values().map(CfgExpr::parse)
+    }
+
     pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
         match self.cfg() {
             None => true,
@@ -569,6 +573,10 @@
         self.attrs().find_map(|attr| attr.string_value())
     }
 
+    pub fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
+        self.attrs().find_map(|attr| attr.string_value_unescape())
+    }
+
     pub fn exists(self) -> bool {
         self.attrs().next().is_some()
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
index 1a63e96..9b68797 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
@@ -11,7 +11,7 @@
 use crate::attr::{DocAtom, DocExpr};
 
 fn assert_parse_result(input: &str, expected: DocExpr) {
-    let source_file = ast::SourceFile::parse(input).ok().unwrap();
+    let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap();
     let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
     let tt = syntax_node_to_token_tree(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index da790f1..e3d750d3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -510,6 +510,7 @@
     pub type_ref: Interned<TypeRef>,
     pub visibility: RawVisibility,
     pub rustc_allow_incoherent_impl: bool,
+    pub has_body: bool,
 }
 
 impl ConstData {
@@ -533,6 +534,7 @@
             type_ref: konst.type_ref.clone(),
             visibility,
             rustc_allow_incoherent_impl,
+            has_body: konst.has_body,
         })
     }
 }
@@ -737,7 +739,7 @@
                     &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
                     ctxt,
                     expand_to,
-                    self.expander.module.krate(),
+                    self.expander.krate(),
                     resolver,
                 ) {
                     Ok(Some(call_id)) => {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index a7461b7..0fe7341 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -26,7 +26,7 @@
     tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
     type_ref::TypeRef,
     visibility::RawVisibility,
-    EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId,
+    EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId,
 };
 
 /// Note that we use `StructData` for unions as well!
@@ -191,8 +191,6 @@
         let krate = loc.container.krate;
         let item_tree = loc.id.item_tree(db);
         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
-        let cfg_options = db.crate_graph()[krate].cfg_options.clone();
-
         let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
 
         let mut flags = StructFlags::NO_FLAGS;
@@ -219,7 +217,7 @@
             loc.id.file_id(),
             loc.container.local_id,
             &item_tree,
-            &cfg_options,
+            &db.crate_graph()[krate].cfg_options,
             &strukt.fields,
             None,
         );
@@ -248,8 +246,6 @@
         let krate = loc.container.krate;
         let item_tree = loc.id.item_tree(db);
         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
-        let cfg_options = db.crate_graph()[krate].cfg_options.clone();
-
         let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
         let mut flags = StructFlags::NO_FLAGS;
         if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
@@ -266,7 +262,7 @@
             loc.id.file_id(),
             loc.container.local_id,
             &item_tree,
-            &cfg_options,
+            &db.crate_graph()[krate].cfg_options,
             &union.fields,
             None,
         );
@@ -338,7 +334,6 @@
         let container = loc.parent.lookup(db).container;
         let krate = container.krate;
         let item_tree = loc.id.item_tree(db);
-        let cfg_options = db.crate_graph()[krate].cfg_options.clone();
         let variant = &item_tree[loc.id.value];
 
         let (var_data, diagnostics) = lower_fields(
@@ -347,7 +342,7 @@
             loc.id.file_id(),
             container.local_id,
             &item_tree,
-            &cfg_options,
+            &db.crate_graph()[krate].cfg_options,
             &variant.fields,
             Some(item_tree[loc.parent.lookup(db).id.value].visibility),
         );
@@ -383,6 +378,15 @@
             VariantData::Unit => StructKind::Unit,
         }
     }
+
+    #[allow(clippy::self_named_constructors)]
+    pub(crate) fn variant_data(db: &dyn DefDatabase, id: VariantId) -> Arc<VariantData> {
+        match id {
+            VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
+            VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(),
+            VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
+        }
+    }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index 30d52d8..55ecabd 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -12,7 +12,7 @@
     attr::{Attrs, AttrsWithOwner},
     body::{scope::ExprScopes, Body, BodySourceMap},
     data::{
-        adt::{EnumData, EnumVariantData, StructData},
+        adt::{EnumData, EnumVariantData, StructData, VariantData},
         ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
         ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
     },
@@ -128,6 +128,9 @@
     ) -> (Arc<EnumVariantData>, DefDiagnostics);
 
     #[salsa::transparent]
+    #[salsa::invoke(VariantData::variant_data)]
+    fn variant_data(&self, id: VariantId) -> Arc<VariantData>;
+    #[salsa::transparent]
     #[salsa::invoke(ImplData::impl_data_query)]
     fn impl_data(&self, e: ImplId) -> Arc<ImplData>;
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
index b0872fc..73ce942 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -11,6 +11,7 @@
 };
 use limit::Limit;
 use syntax::{ast, Parse};
+use triomphe::Arc;
 
 use crate::{
     attr::Attrs, db::DefDatabase, lower::LowerCtx, path::Path, AsMacroCall, MacroId, ModuleId,
@@ -19,9 +20,8 @@
 
 #[derive(Debug)]
 pub struct Expander {
-    cfg_options: CfgOptions,
+    cfg_options: Arc<CfgOptions>,
     span_map: OnceCell<SpanMap>,
-    krate: CrateId,
     current_file_id: HirFileId,
     pub(crate) module: ModuleId,
     /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
@@ -45,10 +45,13 @@
             recursion_limit,
             cfg_options: db.crate_graph()[module.krate].cfg_options.clone(),
             span_map: OnceCell::new(),
-            krate: module.krate,
         }
     }
 
+    pub fn krate(&self) -> CrateId {
+        self.module.krate
+    }
+
     pub fn enter_expand<T: ast::AstNode>(
         &mut self,
         db: &dyn DefDatabase,
@@ -112,7 +115,7 @@
     pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
         Attrs::filter(
             db,
-            self.krate,
+            self.krate(),
             RawAttrs::new(
                 db.upcast(),
                 owner,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index 0cd4a5d..bf728a7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -30,6 +30,8 @@
     find_path_inner(FindPathCtx { db, prefixed: None, prefer_no_std, prefer_prelude }, item, from)
 }
 
+/// Find a path that can be used to refer to a certain item. This can depend on
+/// *from where* you're referring to the item, hence the `from` parameter.
 pub fn find_path_prefixed(
     db: &dyn DefDatabase,
     item: ItemInNs,
@@ -255,7 +257,7 @@
     item: ItemInNs,
 ) -> Option<Name> {
     def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
-        def_map[local_id].scope.name_of(item).map(|(name, _, _)| name.clone())
+        def_map[local_id].scope.names_of(item, |name, _, _| Some(name.clone()))
     })
 }
 
@@ -608,7 +610,8 @@
     ) {
         let (db, pos) = TestDB::with_position(ra_fixture);
         let module = db.module_at_position(pos);
-        let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};"));
+        let parsed_path_file =
+            syntax::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT);
         let ast_path =
             parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
         let mod_path = ModPath::from_src(&db, ast_path, &mut |range| {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index 4638b37..acc60e1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -3,13 +3,15 @@
 //! generic parameters. See also the `Generics` type and the `generics_of` query
 //! in rustc.
 
+use std::ops;
+
 use either::Either;
 use hir_expand::{
     name::{AsName, Name},
     ExpandResult,
 };
 use intern::Interned;
-use la_arena::{Arena, Idx};
+use la_arena::Arena;
 use once_cell::unsync::Lazy;
 use stdx::impl_from;
 use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
@@ -23,12 +25,14 @@
     nameres::{DefMap, MacroSubNs},
     type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
     AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
-    LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
+    LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
 };
 
 /// Data about a generic type parameter (to a function, struct, impl, ...).
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct TypeParamData {
+    /// [`None`] only if the type ref is an [`TypeRef::ImplTrait`]. FIXME: Might be better to just
+    /// make it always be a value, giving impl trait a special name.
     pub name: Option<Name>,
     pub default: Option<Interned<TypeRef>>,
     pub provenance: TypeParamProvenance,
@@ -156,6 +160,20 @@
     pub where_predicates: Box<[WherePredicate]>,
 }
 
+impl ops::Index<LocalTypeOrConstParamId> for GenericParams {
+    type Output = TypeOrConstParamData;
+    fn index(&self, index: LocalTypeOrConstParamId) -> &TypeOrConstParamData {
+        &self.type_or_consts[index]
+    }
+}
+
+impl ops::Index<LocalLifetimeParamId> for GenericParams {
+    type Output = LifetimeParamData;
+    fn index(&self, index: LocalLifetimeParamId) -> &LifetimeParamData {
+        &self.lifetimes[index]
+    }
+}
+
 /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
 /// where clauses like `where T: Foo + Bar` are turned into multiple of these.
 /// It might still result in multiple actual predicates though, because of
@@ -197,7 +215,7 @@
         lower_ctx: &LowerCtx<'_>,
         node: &dyn HasGenericParams,
         add_param_attrs: impl FnMut(
-            Either<Idx<TypeOrConstParamData>, Idx<LifetimeParamData>>,
+            Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
             ast::GenericParam,
         ),
     ) {
@@ -225,7 +243,7 @@
         lower_ctx: &LowerCtx<'_>,
         params: ast::GenericParamList,
         mut add_param_attrs: impl FnMut(
-            Either<Idx<TypeOrConstParamData>, Idx<LifetimeParamData>>,
+            Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
             ast::GenericParam,
         ),
     ) {
@@ -414,16 +432,16 @@
     }
 
     /// Iterator of type_or_consts field
-    pub fn iter(
+    pub fn iter_type_or_consts(
         &self,
-    ) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> {
+    ) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
         self.type_or_consts.iter()
     }
 
     /// Iterator of lifetimes field
     pub fn iter_lt(
         &self,
-    ) -> impl DoubleEndedIterator<Item = (Idx<LifetimeParamData>, &LifetimeParamData)> {
+    ) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
         self.lifetimes.iter()
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 2b059d1..a60b9f9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -277,13 +277,43 @@
             ItemInNs::Types(def) => self.types.iter().find_map(|(name, &(other_def, vis, i))| {
                 (other_def == def).then_some((name, vis, i.is_none()))
             }),
-
             ItemInNs::Values(def) => self.values.iter().find_map(|(name, &(other_def, vis, i))| {
                 (other_def == def).then_some((name, vis, i.is_none()))
             }),
         }
     }
 
+    /// XXX: this is O(N) rather than O(1), try to not introduce new usages.
+    pub(crate) fn names_of<T>(
+        &self,
+        item: ItemInNs,
+        mut cb: impl FnMut(&Name, Visibility, bool) -> Option<T>,
+    ) -> Option<T> {
+        match item {
+            ItemInNs::Macros(def) => self
+                .macros
+                .iter()
+                .filter_map(|(name, &(other_def, vis, i))| {
+                    (other_def == def).then_some((name, vis, i.is_none()))
+                })
+                .find_map(|(a, b, c)| cb(a, b, c)),
+            ItemInNs::Types(def) => self
+                .types
+                .iter()
+                .filter_map(|(name, &(other_def, vis, i))| {
+                    (other_def == def).then_some((name, vis, i.is_none()))
+                })
+                .find_map(|(a, b, c)| cb(a, b, c)),
+            ItemInNs::Values(def) => self
+                .values
+                .iter()
+                .filter_map(|(name, &(other_def, vis, i))| {
+                    (other_def == def).then_some((name, vis, i.is_none()))
+                })
+                .find_map(|(a, b, c)| cb(a, b, c)),
+        }
+    }
+
     pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ {
         self.types
             .values()
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 585e93c..6104807 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -716,6 +716,7 @@
     pub visibility: RawVisibilityId,
     pub type_ref: Interned<TypeRef>,
     pub ast_id: FileAstId<ast::Const>,
+    pub has_body: bool,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index f02163c..4b5ef56 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -446,7 +446,7 @@
         let type_ref = self.lower_type_ref_opt(konst.ty());
         let visibility = self.lower_visibility(konst);
         let ast_id = self.source_ast_id_map.ast_id(konst);
-        let res = Const { name, visibility, type_ref, ast_id };
+        let res = Const { name, visibility, type_ref, ast_id, has_body: konst.body().is_some() };
         id(self.data().consts.alloc(res))
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 0c84057..cef2a3f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -357,7 +357,7 @@
                 wln!(self, "}}");
             }
             ModItem::Const(it) => {
-                let Const { name, visibility, type_ref, ast_id } = &self.tree[it];
+                let Const { name, visibility, type_ref, ast_id, has_body: _ } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "const ");
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 46898ce..88d4572 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -422,6 +422,10 @@
         }
     }
 
+    pub fn crate_def_map(self, db: &dyn DefDatabase) -> Arc<DefMap> {
+        db.crate_def_map(self.krate)
+    }
+
     pub fn krate(self) -> CrateId {
         self.krate
     }
@@ -438,6 +442,8 @@
         })
     }
 
+    /// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing
+    /// the block, if `self` corresponds to a block expression.
     pub fn containing_module(self, db: &dyn DefDatabase) -> Option<ModuleId> {
         self.def_map(db).containing_module(self.local_id)
     }
@@ -929,6 +935,18 @@
             GenericDefId::EnumVariantId(_) => (FileId::BOGUS.into(), None),
         }
     }
+
+    pub fn assoc_trait_container(self, db: &dyn DefDatabase) -> Option<TraitId> {
+        match match self {
+            GenericDefId::FunctionId(f) => f.lookup(db).container,
+            GenericDefId::TypeAliasId(t) => t.lookup(db).container,
+            GenericDefId::ConstId(c) => c.lookup(db).container,
+            _ => return None,
+        } {
+            ItemContainerId::TraitId(trait_) => Some(trait_),
+            _ => None,
+        }
+    }
 }
 
 impl From<AssocItemId> for GenericDefId {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index 89c1b44..163211f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -610,6 +610,10 @@
     field1: i32,
     #[cfg(never)]
     field2: (),
+    #[cfg(feature = "never")]
+    field3: (),
+    #[cfg(not(feature = "never"))]
+    field4: (),
 }
 #[derive(Default)]
 enum Bar {
@@ -618,12 +622,16 @@
     Bar,
 }
 "#,
-        expect![[r#"
+        expect![[r##"
 #[derive(Default)]
 struct Foo {
     field1: i32,
     #[cfg(never)]
     field2: (),
+    #[cfg(feature = "never")]
+    field3: (),
+    #[cfg(not(feature = "never"))]
+    field4: (),
 }
 #[derive(Default)]
 enum Bar {
@@ -635,7 +643,7 @@
 impl < > $crate::default::Default for Foo< > where {
     fn default() -> Self {
         Foo {
-            field1: $crate::default::Default::default(),
+            field1: $crate::default::Default::default(), field4: $crate::default::Default::default(),
         }
     }
 }
@@ -643,6 +651,6 @@
     fn default() -> Self {
         Bar::Bar
     }
-}"#]],
+}"##]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 23b10cf..8904aca 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -316,8 +316,11 @@
         _: Span,
         _: Span,
     ) -> Result<Subtree, ProcMacroExpansionError> {
-        let (parse, _) =
-            ::mbe::token_tree_to_syntax_node(subtree, ::mbe::TopEntryPoint::MacroItems);
+        let (parse, _) = ::mbe::token_tree_to_syntax_node(
+            subtree,
+            ::mbe::TopEntryPoint::MacroItems,
+            span::Edition::CURRENT,
+        );
         if parse.errors().is_empty() {
             Ok(subtree.clone())
         } else {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index ae8f028..0a6cd0f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -534,8 +534,7 @@
             Edition::Edition2015 => name![rust_2015],
             Edition::Edition2018 => name![rust_2018],
             Edition::Edition2021 => name![rust_2021],
-            // FIXME: update this when rust_2024 exists
-            Edition::Edition2024 => name![rust_2021],
+            Edition::Edition2024 => name![rust_2024],
         };
 
         let path_kind = match self.def_map.data.edition {
@@ -1918,7 +1917,7 @@
     }
 
     fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
-        let path_attr = attrs.by_key("path").string_value();
+        let path_attr = attrs.by_key("path").string_value_unescape();
         let is_macro_use = attrs.by_key("macro_use").exists();
         let module = &self.item_tree[module_id];
         match &module.kind {
@@ -1932,7 +1931,8 @@
                     module_id,
                 );
 
-                let Some(mod_dir) = self.mod_dir.descend_into_definition(&module.name, path_attr)
+                let Some(mod_dir) =
+                    self.mod_dir.descend_into_definition(&module.name, path_attr.as_deref())
                 else {
                     return;
                 };
@@ -1953,8 +1953,12 @@
             ModKind::Outline => {
                 let ast_id = AstId::new(self.file_id(), module.ast_id);
                 let db = self.def_collector.db;
-                match self.mod_dir.resolve_declaration(db, self.file_id(), &module.name, path_attr)
-                {
+                match self.mod_dir.resolve_declaration(
+                    db,
+                    self.file_id(),
+                    &module.name,
+                    path_attr.as_deref(),
+                ) {
                     Ok((file_id, is_mod_rs, mod_dir)) => {
                         let item_tree = db.file_item_tree(file_id.into());
                         let krate = self.def_collector.def_map.krate;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
index 4f30808..ca05618 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
@@ -32,9 +32,13 @@
 mbe.workspace = true
 limit.workspace = true
 span.workspace = true
+parser.workspace = true
 
 [dev-dependencies]
 expect-test = "1.4.0"
 
+[features]
+in-rust-tree = ["syntax/in-rust-tree"]
+
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index f154049..f8bf88d 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -1,5 +1,5 @@
 //! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{fmt, ops};
+use std::{borrow::Cow, fmt, ops};
 
 use base_db::CrateId;
 use cfg::CfgExpr;
@@ -8,6 +8,7 @@
 use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
 use smallvec::{smallvec, SmallVec};
 use span::{Span, SyntaxContextId};
+use syntax::unescape;
 use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
 use triomphe::ThinArc;
 
@@ -54,8 +55,7 @@
                     Attr {
                         id,
                         input: Some(Interned::new(AttrInput::Literal(tt::Literal {
-                            // FIXME: Escape quotes from comment content
-                            text: SmolStr::new(format_smolstr!("\"{doc}\"",)),
+                            text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))),
                             span,
                         }))),
                         path: Interned::new(ModPath::from(crate::name!(doc))),
@@ -74,6 +74,10 @@
         RawAttrs { entries }
     }
 
+    fn escape_chars(s: &str) -> String {
+        s.replace('\\', r#"\\"#).replace('"', r#"\""#)
+    }
+
     pub fn from_attrs_owner(
         db: &dyn ExpandDatabase,
         owner: InFile<&dyn ast::HasAttrs>,
@@ -297,6 +301,18 @@
         }
     }
 
+    pub fn string_value_unescape(&self) -> Option<Cow<'_, str>> {
+        match self.input.as_deref()? {
+            AttrInput::Literal(it) => match it.text.strip_prefix('r') {
+                Some(it) => {
+                    it.trim_matches('#').strip_prefix('"')?.strip_suffix('"').map(Cow::Borrowed)
+                }
+                None => it.text.strip_prefix('"')?.strip_suffix('"').and_then(unescape),
+            },
+            _ => None,
+        }
+    }
+
     /// #[path(ident)]
     pub fn single_ident_value(&self) -> Option<&tt::Ident> {
         match self.input.as_deref()? {
@@ -346,6 +362,33 @@
     }
 }
 
+fn unescape(s: &str) -> Option<Cow<'_, str>> {
+    let mut buf = String::new();
+    let mut prev_end = 0;
+    let mut has_error = false;
+    unescape::unescape_unicode(s, unescape::Mode::Str, &mut |char_range, unescaped_char| match (
+        unescaped_char,
+        buf.capacity() == 0,
+    ) {
+        (Ok(c), false) => buf.push(c),
+        (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
+            prev_end = char_range.end
+        }
+        (Ok(c), true) => {
+            buf.reserve_exact(s.len());
+            buf.push_str(&s[..prev_end]);
+            buf.push(c);
+        }
+        (Err(_), _) => has_error = true,
+    });
+
+    match (has_error, buf.capacity() == 0) {
+        (true, _) => None,
+        (false, false) => Some(Cow::Owned(buf)),
+        (false, true) => Some(Cow::Borrowed(s)),
+    }
+}
+
 pub fn collect_attrs(
     owner: &dyn ast::HasAttrs,
 ) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
index 528038a..94681b4 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
@@ -204,7 +204,11 @@
 }
 
 fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> {
-    let (parsed, tm) = &mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems);
+    let (parsed, tm) = &mbe::token_tree_to_syntax_node(
+        tt,
+        mbe::TopEntryPoint::MacroItems,
+        parser::Edition::CURRENT,
+    );
     let macro_items = ast::MacroItems::cast(parsed.syntax_node())
         .ok_or_else(|| ExpandError::other("invalid item definition"))?;
     let item = macro_items.items().next().ok_or_else(|| ExpandError::other("no item found"))?;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index fd3e4e7..4d6fe6d 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -219,7 +219,7 @@
     span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let call_site_span = span_with_call_site_ctxt(db, span, id);
-    let args = parse_exprs_with_sep(tt, ',', call_site_span);
+    let args = parse_exprs_with_sep(tt, ',', call_site_span, Edition::CURRENT);
     let dollar_crate = dollar_crate(span);
     let expanded = match &*args {
         [cond, panic_args @ ..] => {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
index db3558a..9dd4426 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
@@ -1,57 +1,59 @@
 //! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
 use std::iter::Peekable;
 
+use base_db::CrateId;
 use cfg::{CfgAtom, CfgExpr};
 use rustc_hash::FxHashSet;
 use syntax::{
     ast::{self, Attr, HasAttrs, Meta, VariantList},
-    AstNode, NodeOrToken, SyntaxElement, SyntaxNode, T,
+    AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T,
 };
 use tracing::{debug, warn};
 use tt::SmolStr;
 
 use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind};
 
-fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> {
+fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
     if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
         return None;
     }
-    debug!("Evaluating cfg {}", attr);
     let cfg = parse_from_attr_meta(attr.meta()?)?;
-    debug!("Checking cfg {:?}", cfg);
-    let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg) != Some(false);
+    let enabled = db.crate_graph()[krate].cfg_options.check(&cfg) != Some(false);
     Some(enabled)
 }
 
-fn check_cfg_attr_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> {
+fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
     if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? {
         return None;
     }
-    debug!("Evaluating cfg_attr {}", attr);
     let cfg_expr = parse_from_attr_meta(attr.meta()?)?;
-    debug!("Checking cfg_attr {:?}", cfg_expr);
-    let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg_expr) != Some(false);
+    let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false);
     Some(enabled)
 }
 
 fn process_has_attrs_with_possible_comma<I: HasAttrs>(
-    items: impl Iterator<Item = I>,
-    loc: &MacroCallLoc,
     db: &dyn ExpandDatabase,
+    items: impl Iterator<Item = I>,
+    krate: CrateId,
     remove: &mut FxHashSet<SyntaxElement>,
 ) -> Option<()> {
     for item in items {
         let field_attrs = item.attrs();
         'attrs: for attr in field_attrs {
-            if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() {
-                debug!("censoring type {:?}", item.syntax());
-                remove.insert(item.syntax().clone().into());
-                // We need to remove the , as well
-                remove_possible_comma(&item, remove);
-                break 'attrs;
+            if let Some(enabled) = check_cfg(db, &attr, krate) {
+                if enabled {
+                    debug!("censoring {:?}", attr.syntax());
+                    remove.insert(attr.syntax().clone().into());
+                } else {
+                    debug!("censoring {:?}", item.syntax());
+                    remove.insert(item.syntax().clone().into());
+                    // We need to remove the , as well
+                    remove_possible_comma(&item, remove);
+                    break 'attrs;
+                }
             }
 
-            if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) {
+            if let Some(enabled) = check_cfg_attr(db, &attr, krate) {
                 if enabled {
                     debug!("Removing cfg_attr tokens {:?}", attr);
                     let meta = attr.meta()?;
@@ -60,13 +62,13 @@
                 } else {
                     debug!("censoring type cfg_attr {:?}", item.syntax());
                     remove.insert(attr.syntax().clone().into());
-                    continue;
                 }
             }
         }
     }
     Some(())
 }
+
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 enum CfgExprStage {
     /// Stripping the CFGExpr part of the attribute
@@ -78,6 +80,7 @@
     // Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110
     EverythingElse,
 }
+
 /// This function creates its own set of tokens to remove. To help prevent malformed syntax as input.
 fn remove_tokens_within_cfg_attr(meta: Meta) -> Option<FxHashSet<SyntaxElement>> {
     let mut remove: FxHashSet<SyntaxElement> = FxHashSet::default();
@@ -131,23 +134,28 @@
     }
 }
 fn process_enum(
-    variants: VariantList,
-    loc: &MacroCallLoc,
     db: &dyn ExpandDatabase,
+    variants: VariantList,
+    krate: CrateId,
     remove: &mut FxHashSet<SyntaxElement>,
 ) -> Option<()> {
     'variant: for variant in variants.variants() {
         for attr in variant.attrs() {
-            if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() {
-                // Rustc does not strip the attribute if it is enabled. So we will leave it
-                debug!("censoring type {:?}", variant.syntax());
-                remove.insert(variant.syntax().clone().into());
-                // We need to remove the , as well
-                remove_possible_comma(&variant, remove);
-                continue 'variant;
-            };
+            if let Some(enabled) = check_cfg(db, &attr, krate) {
+                if enabled {
+                    debug!("censoring {:?}", attr.syntax());
+                    remove.insert(attr.syntax().clone().into());
+                } else {
+                    // Rustc does not strip the attribute if it is enabled. So we will leave it
+                    debug!("censoring type {:?}", variant.syntax());
+                    remove.insert(variant.syntax().clone().into());
+                    // We need to remove the , as well
+                    remove_possible_comma(&variant, remove);
+                    continue 'variant;
+                }
+            }
 
-            if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) {
+            if let Some(enabled) = check_cfg_attr(db, &attr, krate) {
                 if enabled {
                     debug!("Removing cfg_attr tokens {:?}", attr);
                     let meta = attr.meta()?;
@@ -156,17 +164,16 @@
                 } else {
                     debug!("censoring type cfg_attr {:?}", variant.syntax());
                     remove.insert(attr.syntax().clone().into());
-                    continue;
                 }
             }
         }
         if let Some(fields) = variant.field_list() {
             match fields {
                 ast::FieldList::RecordFieldList(fields) => {
-                    process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?;
+                    process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?;
                 }
                 ast::FieldList::TupleFieldList(fields) => {
-                    process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?;
+                    process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?;
                 }
             }
         }
@@ -175,9 +182,9 @@
 }
 
 pub(crate) fn process_cfg_attrs(
+    db: &dyn ExpandDatabase,
     node: &SyntaxNode,
     loc: &MacroCallLoc,
-    db: &dyn ExpandDatabase,
 ) -> Option<FxHashSet<SyntaxElement>> {
     // FIXME: #[cfg_eval] is not implemented. But it is not stable yet
     let is_derive = match loc.def.kind {
@@ -193,36 +200,35 @@
 
     let item = ast::Item::cast(node.clone())?;
     for attr in item.attrs() {
-        if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) {
+        if let Some(enabled) = check_cfg_attr(db, &attr, loc.krate) {
             if enabled {
                 debug!("Removing cfg_attr tokens {:?}", attr);
                 let meta = attr.meta()?;
                 let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?;
                 remove.extend(removes_from_cfg_attr);
             } else {
-                debug!("censoring type cfg_attr {:?}", item.syntax());
+                debug!("Removing type cfg_attr {:?}", item.syntax());
                 remove.insert(attr.syntax().clone().into());
-                continue;
             }
         }
     }
     match item {
         ast::Item::Struct(it) => match it.field_list()? {
             ast::FieldList::RecordFieldList(fields) => {
-                process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?;
+                process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?;
             }
             ast::FieldList::TupleFieldList(fields) => {
-                process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?;
+                process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?;
             }
         },
         ast::Item::Enum(it) => {
-            process_enum(it.variant_list()?, loc, db, &mut remove)?;
+            process_enum(db, it.variant_list()?, loc.krate, &mut remove)?;
         }
         ast::Item::Union(it) => {
             process_has_attrs_with_possible_comma(
-                it.record_field_list()?.fields(),
-                loc,
                 db,
+                it.record_field_list()?.fields(),
+                loc.krate,
                 &mut remove,
             )?;
         }
@@ -234,10 +240,22 @@
 /// Parses a `cfg` attribute from the meta
 fn parse_from_attr_meta(meta: Meta) -> Option<CfgExpr> {
     let tt = meta.token_tree()?;
-    let mut iter = tt.token_trees_and_tokens().skip(1).peekable();
+    let mut iter = tt
+        .token_trees_and_tokens()
+        .filter(is_not_whitespace)
+        .skip(1)
+        .take_while(is_not_closing_paren)
+        .peekable();
     next_cfg_expr_from_syntax(&mut iter)
 }
 
+fn is_not_closing_paren(element: &NodeOrToken<ast::TokenTree, syntax::SyntaxToken>) -> bool {
+    !matches!(element, NodeOrToken::Token(token) if (token.kind() == syntax::T![')']))
+}
+fn is_not_whitespace(element: &NodeOrToken<ast::TokenTree, syntax::SyntaxToken>) -> bool {
+    !matches!(element, NodeOrToken::Token(token) if (token.kind() == SyntaxKind::WHITESPACE))
+}
+
 fn next_cfg_expr_from_syntax<I>(iter: &mut Peekable<I>) -> Option<CfgExpr>
 where
     I: Iterator<Item = NodeOrToken<ast::TokenTree, syntax::SyntaxToken>>,
@@ -256,14 +274,13 @@
             let Some(NodeOrToken::Node(tree)) = iter.next() else {
                 return Some(CfgExpr::Invalid);
             };
-            let mut tree_iter = tree.token_trees_and_tokens().skip(1).peekable();
-            while tree_iter
-                .peek()
-                .filter(
-                    |element| matches!(element, NodeOrToken::Token(token) if (token.kind() != syntax::T![')'])),
-                )
-                .is_some()
-            {
+            let mut tree_iter = tree
+                .token_trees_and_tokens()
+                .filter(is_not_whitespace)
+                .skip(1)
+                .take_while(is_not_closing_paren)
+                .peekable();
+            while tree_iter.peek().is_some() {
                 let pred = next_cfg_expr_from_syntax(&mut tree_iter);
                 if let Some(pred) = pred {
                     preds.push(pred);
@@ -310,7 +327,7 @@
     use crate::cfg_process::parse_from_attr_meta;
 
     fn check_dnf_from_syntax(input: &str, expect: Expect) {
-        let parse = SourceFile::parse(input);
+        let parse = SourceFile::parse(input, span::Edition::CURRENT);
         let node = match parse.tree().syntax().descendants().find_map(Attr::cast) {
             Some(it) => it,
             None => {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 5461c1c..d7233a8 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -3,7 +3,7 @@
 use base_db::{salsa, CrateId, FileId, SourceDatabase};
 use either::Either;
 use limit::Limit;
-use mbe::syntax_node_to_token_tree;
+use mbe::{syntax_node_to_token_tree, MatchedArmIndex};
 use rustc_hash::FxHashSet;
 use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId};
 use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
@@ -175,7 +175,7 @@
             };
 
             let censor_cfg =
-                cfg_process::process_cfg_attrs(speculative_args, &loc, db).unwrap_or_default();
+                cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default();
             let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span);
             fixups.append.retain(|it, _| match it {
                 syntax::NodeOrToken::Token(_) => true,
@@ -225,43 +225,45 @@
 
     // Do the actual expansion, we need to directly expand the proc macro due to the attribute args
     // Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
-    let mut speculative_expansion = match loc.def.kind {
-        MacroDefKind::ProcMacro(expander, _, ast) => {
-            let span = db.proc_macro_span(ast);
-            tt.delimiter = tt::Delimiter::invisible_spanned(span);
-            expander.expand(
-                db,
-                loc.def.krate,
-                loc.krate,
-                &tt,
-                attr_arg.as_ref(),
-                span_with_def_site_ctxt(db, span, actual_macro_call),
-                span_with_call_site_ctxt(db, span, actual_macro_call),
-                span_with_mixed_site_ctxt(db, span, actual_macro_call),
-            )
-        }
-        MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
-            pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
-        }
-        MacroDefKind::Declarative(it) => {
-            db.decl_macro_expander(loc.krate, it).expand_unhygienic(db, tt, loc.def.krate, span)
-        }
-        MacroDefKind::BuiltIn(it, _) => {
-            it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
-        }
-        MacroDefKind::BuiltInDerive(it, ..) => {
-            it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
-        }
-        MacroDefKind::BuiltInEager(it, _) => {
-            it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
-        }
-        MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span),
-    };
+    let mut speculative_expansion =
+        match loc.def.kind {
+            MacroDefKind::ProcMacro(expander, _, ast) => {
+                let span = db.proc_macro_span(ast);
+                tt.delimiter = tt::Delimiter::invisible_spanned(span);
+                expander.expand(
+                    db,
+                    loc.def.krate,
+                    loc.krate,
+                    &tt,
+                    attr_arg.as_ref(),
+                    span_with_def_site_ctxt(db, span, actual_macro_call),
+                    span_with_call_site_ctxt(db, span, actual_macro_call),
+                    span_with_mixed_site_ctxt(db, span, actual_macro_call),
+                )
+            }
+            MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
+                pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
+            }
+            MacroDefKind::Declarative(it) => db
+                .decl_macro_expander(loc.krate, it)
+                .expand_unhygienic(db, tt, loc.def.krate, span, loc.def.edition),
+            MacroDefKind::BuiltIn(it, _) => {
+                it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
+            }
+            MacroDefKind::BuiltInDerive(it, ..) => {
+                it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
+            }
+            MacroDefKind::BuiltInEager(it, _) => {
+                it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
+            }
+            MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span),
+        };
 
     let expand_to = loc.expand_to();
 
     fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info);
-    let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to);
+    let (node, rev_tmap) =
+        token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition);
 
     let syntax_node = node.syntax_node();
     let token = rev_tmap
@@ -309,16 +311,20 @@
 ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
     let _p = tracing::span!(tracing::Level::INFO, "parse_macro_expansion").entered();
     let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
+    let edition = loc.def.edition;
     let expand_to = loc.expand_to();
-    let mbe::ValueResult { value: tt, err } = macro_expand(db, macro_file.macro_call_id, loc);
+    let mbe::ValueResult { value: (tt, matched_arm), err } =
+        macro_expand(db, macro_file.macro_call_id, loc);
 
-    let (parse, rev_token_map) = token_tree_to_syntax_node(
+    let (parse, mut rev_token_map) = token_tree_to_syntax_node(
         match &tt {
             CowArc::Arc(it) => it,
             CowArc::Owned(it) => it,
         },
         expand_to,
+        edition,
     );
+    rev_token_map.matched_arm = matched_arm;
 
     ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
 }
@@ -462,7 +468,7 @@
 
     let (mut tt, undo_info) = {
         let syntax = item_node.syntax();
-        let censor_cfg = cfg_process::process_cfg_attrs(syntax, &loc, db).unwrap_or_default();
+        let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default();
         let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span);
         fixups.append.retain(|it, _| match it {
             syntax::NodeOrToken::Token(_) => true,
@@ -540,11 +546,13 @@
     db: &dyn ExpandDatabase,
     macro_call_id: MacroCallId,
     loc: MacroCallLoc,
-) -> ExpandResult<CowArc<tt::Subtree>> {
+) -> ExpandResult<(CowArc<tt::Subtree>, MatchedArmIndex)> {
     let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered();
 
-    let (ExpandResult { value: tt, err }, span) = match loc.def.kind {
-        MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
+    let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
+        MacroDefKind::ProcMacro(..) => {
+            return db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None)
+        }
         _ => {
             let (macro_arg, undo_info, span) =
                 db.macro_arg_considering_derives(macro_call_id, &loc.kind);
@@ -556,10 +564,10 @@
                         .decl_macro_expander(loc.def.krate, id)
                         .expand(db, arg.clone(), macro_call_id, span),
                     MacroDefKind::BuiltIn(it, _) => {
-                        it.expand(db, macro_call_id, arg, span).map_err(Into::into)
+                        it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
                     }
                     MacroDefKind::BuiltInDerive(it, _) => {
-                        it.expand(db, macro_call_id, arg, span).map_err(Into::into)
+                        it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
                     }
                     MacroDefKind::BuiltInEager(it, _) => {
                         // This might look a bit odd, but we do not expand the inputs to eager macros here.
@@ -570,7 +578,8 @@
                         // As such we just return the input subtree here.
                         let eager = match &loc.kind {
                             MacroCallKind::FnLike { eager: None, .. } => {
-                                return ExpandResult::ok(CowArc::Arc(macro_arg.clone()));
+                                return ExpandResult::ok(CowArc::Arc(macro_arg.clone()))
+                                    .zip_val(None);
                             }
                             MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
                             _ => None,
@@ -582,12 +591,12 @@
                             // FIXME: We should report both errors!
                             res.err = error.clone().or(res.err);
                         }
-                        res
+                        res.zip_val(None)
                     }
                     MacroDefKind::BuiltInAttr(it, _) => {
                         let mut res = it.expand(db, macro_call_id, arg, span);
                         fixup::reverse_fixups(&mut res.value, &undo_info);
-                        res
+                        res.zip_val(None)
                     }
                     _ => unreachable!(),
                 };
@@ -599,16 +608,18 @@
     if !loc.def.is_include() {
         // Set a hard limit for the expanded tt
         if let Err(value) = check_tt_count(&tt) {
-            return value.map(|()| {
-                CowArc::Owned(tt::Subtree {
-                    delimiter: tt::Delimiter::invisible_spanned(span),
-                    token_trees: Box::new([]),
+            return value
+                .map(|()| {
+                    CowArc::Owned(tt::Subtree {
+                        delimiter: tt::Delimiter::invisible_spanned(span),
+                        token_trees: Box::new([]),
+                    })
                 })
-            });
+                .zip_val(matched_arm);
         }
     }
 
-    ExpandResult { value: CowArc::Owned(tt), err }
+    ExpandResult { value: (CowArc::Owned(tt), matched_arm), err }
 }
 
 fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
@@ -668,6 +679,7 @@
 fn token_tree_to_syntax_node(
     tt: &tt::Subtree,
     expand_to: ExpandTo,
+    edition: parser::Edition,
 ) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
     let entry_point = match expand_to {
         ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
@@ -676,7 +688,7 @@
         ExpandTo::Type => mbe::TopEntryPoint::Type,
         ExpandTo::Expr => mbe::TopEntryPoint::Expr,
     };
-    mbe::token_tree_to_syntax_node(tt, entry_point)
+    mbe::token_tree_to_syntax_node(tt, entry_point, edition)
 }
 
 fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
index 9a0b218..66465ce 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
@@ -2,7 +2,8 @@
 use std::sync::OnceLock;
 
 use base_db::{CrateId, VersionReq};
-use span::{MacroCallId, Span, SyntaxContextId};
+use span::{Edition, MacroCallId, Span, SyntaxContextId};
+use stdx::TupleExt;
 use syntax::{ast, AstNode};
 use triomphe::Arc;
 
@@ -30,7 +31,7 @@
         tt: tt::Subtree,
         call_id: MacroCallId,
         span: Span,
-    ) -> ExpandResult<tt::Subtree> {
+    ) -> ExpandResult<(tt::Subtree, Option<u32>)> {
         let loc = db.lookup_intern_macro_call(call_id);
         let toolchain = db.toolchain(loc.def.krate);
         let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
@@ -46,7 +47,7 @@
         });
         match self.mac.err() {
             Some(_) => ExpandResult::new(
-                tt::Subtree::empty(tt::DelimSpan { open: span, close: span }),
+                (tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), None),
                 ExpandError::MacroDefinition,
             ),
             None => self
@@ -56,6 +57,7 @@
                     |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
                     new_meta_vars,
                     span,
+                    loc.def.edition,
                 )
                 .map_err(Into::into),
         }
@@ -67,6 +69,7 @@
         tt: tt::Subtree,
         krate: CrateId,
         call_site: Span,
+        def_site_edition: Edition,
     ) -> ExpandResult<tt::Subtree> {
         let toolchain = db.toolchain(krate);
         let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
@@ -85,7 +88,11 @@
                 tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
                 ExpandError::MacroDefinition,
             ),
-            None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into),
+            None => self
+                .mac
+                .expand(&tt, |_| (), new_meta_vars, call_site, def_site_edition)
+                .map(TupleExt::head)
+                .map_err(Into::into),
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
index 959595a..711acfe 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -396,7 +396,7 @@
 
     #[track_caller]
     fn check(ra_fixture: &str, mut expect: Expect) {
-        let parsed = syntax::SourceFile::parse(ra_fixture);
+        let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT);
         let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
         let fixups = super::fixup_syntax(
             span_map.as_ref(),
@@ -417,7 +417,11 @@
         expect.assert_eq(&actual);
 
         // the fixed-up tree should be syntactically valid
-        let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
+        let (parse, _) = mbe::token_tree_to_syntax_node(
+            &tt,
+            ::mbe::TopEntryPoint::MacroItems,
+            parser::Edition::CURRENT,
+        );
         assert!(
             parse.errors().is_empty(),
             "parse has syntax errors. parse tree:\n{:#?}",
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index db8bbec..338bd25 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -3,7 +3,7 @@
 //! Specifically, it implements a concept of `MacroFile` -- a file whose syntax
 //! tree originates not from the text of some `FileId`, but from some macro
 //! expansion.
-
+#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 pub mod attrs;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 0b69799..8f74bff 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -303,6 +303,7 @@
         rust_2015,
         rust_2018,
         rust_2021,
+        rust_2024,
         v1,
         new_display,
         new_debug,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index bf47374..a83ee98 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -61,7 +61,7 @@
 test-fixture.workspace = true
 
 [features]
-in-rust-tree = []
+in-rust-tree = ["hir-expand/in-rust-tree"]
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
index cb118a3..41acd35 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
@@ -74,6 +74,10 @@
         (self.data, subst)
     }
 
+    pub fn build_into_subst(self) -> Substitution {
+        self.build_internal().1
+    }
+
     pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self {
         assert!(self.remaining() > 0);
         let arg = arg.cast(Interner);
@@ -291,7 +295,6 @@
     ) -> Self {
         // Note that we're building ADT, so we never have parent generic parameters.
         let defaults = db.generic_defaults(self.data.into());
-        let dummy_ty = TyKind::Error.intern(Interner).cast(Interner);
         for default_ty in defaults.iter().skip(self.vec.len()) {
             // NOTE(skip_binders): we only check if the arg type is error type.
             if let Some(x) = default_ty.skip_binders().ty(Interner) {
@@ -301,13 +304,16 @@
                 }
             }
             // Each default can only depend on the previous parameters.
-            // FIXME: we don't handle const generics here.
             let subst_so_far = Substitution::from_iter(
                 Interner,
                 self.vec
                     .iter()
                     .cloned()
-                    .chain(iter::repeat(dummy_ty.clone()))
+                    .chain(self.param_kinds[self.vec.len()..].iter().map(|it| match it {
+                        ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
+                        ParamKind::Lifetime => error_lifetime().cast(Interner),
+                        ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
+                    }))
                     .take(self.param_kinds.len()),
             );
             self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index d1aebef..0bf01b0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -1,6 +1,8 @@
 //! Various extensions traits for Chalk types.
 
-use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
+use chalk_ir::{
+    cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, TypeOutlives, UintTy,
+};
 use hir_def::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
     generics::TypeOrConstParamData,
@@ -312,7 +314,7 @@
                                 .generic_predicates(id.parent)
                                 .iter()
                                 .map(|pred| pred.clone().substitute(Interner, &substs))
-                                .filter(|wc| match &wc.skip_binders() {
+                                .filter(|wc| match wc.skip_binders() {
                                     WhereClause::Implemented(tr) => {
                                         &tr.self_type_parameter(Interner) == self
                                     }
@@ -320,6 +322,9 @@
                                         alias: AliasTy::Projection(proj),
                                         ty: _,
                                     }) => &proj.self_type_parameter(db) == self,
+                                    WhereClause::TypeOutlives(TypeOutlives { ty, lifetime: _ }) => {
+                                        ty == self
+                                    }
                                     _ => false,
                                 })
                                 .collect::<Vec<_>>();
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 705609b..f09277a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -77,30 +77,32 @@
     resolver: &Resolver,
     path: &Path,
     mode: ParamLoweringMode,
-    args_lazy: impl FnOnce() -> Generics,
+    args: impl FnOnce() -> Option<Generics>,
     debruijn: DebruijnIndex,
     expected_ty: Ty,
 ) -> Option<Const> {
     match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
         Some(ValueNs::GenericParam(p)) => {
             let ty = db.const_param_ty(p);
-            let args = args_lazy();
             let value = match mode {
                 ParamLoweringMode::Placeholder => {
                     ConstValue::Placeholder(to_placeholder_idx(db, p.into()))
                 }
-                ParamLoweringMode::Variable => match args.param_idx(p.into()) {
-                    Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)),
-                    None => {
-                        never!(
-                            "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
-                            args,
-                            path,
-                            p
-                        );
-                        return None;
+                ParamLoweringMode::Variable => {
+                    let args = args();
+                    match args.as_ref().and_then(|args| args.type_or_const_param_idx(p.into())) {
+                        Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)),
+                        None => {
+                            never!(
+                                "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
+                                args,
+                                path,
+                                p
+                            );
+                            return None;
+                        }
                     }
-                },
+                }
             };
             Some(ConstData { ty, value }.intern(Interner))
         }
@@ -285,7 +287,6 @@
     expr: ExprId,
     mode: ParamLoweringMode,
     ctx: &mut InferenceContext<'_>,
-    args: impl FnOnce() -> Generics,
     debruijn: DebruijnIndex,
 ) -> Const {
     let db = ctx.db;
@@ -304,7 +305,9 @@
     }
     if let Expr::Path(p) = &ctx.body.exprs[expr] {
         let resolver = &ctx.resolver;
-        if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) {
+        if let Some(c) =
+            path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone())
+        {
             return c;
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index 38eb337..ecbb1d4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -43,7 +43,7 @@
 }
 
 pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec<IncorrectCase> {
-    let _p = tracing::span!(tracing::Level::INFO, "validate_module_item").entered();
+    let _p = tracing::span!(tracing::Level::INFO, "incorrect_case").entered();
     let mut validator = DeclValidator::new(db);
     validator.validate_item(owner);
     validator.sink
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 20b0da4..a5a42c5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -11,6 +11,7 @@
 use hir_expand::name;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
+use rustc_pattern_analysis::constructor::Constructor;
 use syntax::{ast, AstNode};
 use tracing::debug;
 use triomphe::Arc;
@@ -190,45 +191,45 @@
         let pattern_arena = Arena::new();
         let mut m_arms = Vec::with_capacity(arms.len());
         let mut has_lowering_errors = false;
+        // Note: Skipping the entire diagnostic rather than just not including a faulty match arm is
+        // preferred to avoid the chance of false positives.
         for arm in arms {
-            if let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) {
-                // We only include patterns whose type matches the type
-                // of the scrutinee expression. If we had an InvalidMatchArmPattern
-                // diagnostic or similar we could raise that in an else
-                // block here.
-                //
-                // When comparing the types, we also have to consider that rustc
-                // will automatically de-reference the scrutinee expression type if
-                // necessary.
-                //
-                // FIXME we should use the type checker for this.
-                if (pat_ty == scrut_ty
-                    || scrut_ty
-                        .as_reference()
-                        .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
-                        .unwrap_or(false))
-                    && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer)
-                {
-                    // If we had a NotUsefulMatchArm diagnostic, we could
-                    // check the usefulness of each pattern as we added it
-                    // to the matrix here.
-                    let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors);
-                    let m_arm = pat_analysis::MatchArm {
-                        pat: pattern_arena.alloc(pat),
-                        has_guard: arm.guard.is_some(),
-                        arm_data: (),
-                    };
-                    m_arms.push(m_arm);
-                    if !has_lowering_errors {
-                        continue;
-                    }
+            let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else {
+                return;
+            };
+
+            // We only include patterns whose type matches the type
+            // of the scrutinee expression. If we had an InvalidMatchArmPattern
+            // diagnostic or similar we could raise that in an else
+            // block here.
+            //
+            // When comparing the types, we also have to consider that rustc
+            // will automatically de-reference the scrutinee expression type if
+            // necessary.
+            //
+            // FIXME we should use the type checker for this.
+            if (pat_ty == scrut_ty
+                || scrut_ty
+                    .as_reference()
+                    .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
+                    .unwrap_or(false))
+                && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer)
+            {
+                // If we had a NotUsefulMatchArm diagnostic, we could
+                // check the usefulness of each pattern as we added it
+                // to the matrix here.
+                let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors);
+                let m_arm = pat_analysis::MatchArm {
+                    pat: pattern_arena.alloc(pat),
+                    has_guard: arm.guard.is_some(),
+                    arm_data: (),
+                };
+                m_arms.push(m_arm);
+                if !has_lowering_errors {
+                    continue;
                 }
             }
-
-            // If we can't resolve the type of a pattern, or the pattern type doesn't
-            // fit the match expression, we skip this diagnostic. Skipping the entire
-            // diagnostic rather than just not including this match arm is preferred
-            // to avoid the chance of false positives.
+            // If the pattern type doesn't fit the match expression, we skip this diagnostic.
             cov_mark::hit!(validate_match_bailed_out);
             return;
         }
@@ -266,15 +267,17 @@
 
             let mut have_errors = false;
             let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors);
+
+            // optimization, wildcard trivially hold
+            if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
+                continue;
+            }
+
             let match_arm = rustc_pattern_analysis::MatchArm {
                 pat: pattern_arena.alloc(deconstructed_pat),
                 has_guard: false,
                 arm_data: (),
             };
-            if have_errors {
-                continue;
-            }
-
             let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
                 Ok(v) => v,
                 Err(e) => {
@@ -531,8 +534,16 @@
     fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) {
         match infer.type_mismatch_for_pat(pat) {
             Some(_) => *has_type_mismatches = true,
+            None if *has_type_mismatches => (),
             None => {
-                body[pat].walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches))
+                let pat = &body[pat];
+                if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat {
+                    *has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some();
+                    if *has_type_mismatches {
+                        return;
+                    }
+                }
+                pat.walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches))
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index f45beb4..c171dbc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -1,9 +1,9 @@
 //! Interface with `rustc_pattern_analysis`.
 
 use std::fmt;
-use tracing::debug;
 
-use hir_def::{DefWithBodyId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
+use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
+use once_cell::unsync::Lazy;
 use rustc_hash::FxHashMap;
 use rustc_pattern_analysis::{
     constructor::{Constructor, ConstructorSet, VariantVisibility},
@@ -36,6 +36,24 @@
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(crate) enum Void {}
 
+/// An index type for enum variants. This ranges from 0 to `variants.len()`, whereas `EnumVariantId`
+/// can take arbitrary large values (and hence mustn't be used with `IndexVec`/`BitSet`).
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub(crate) struct EnumVariantContiguousIndex(usize);
+
+impl EnumVariantContiguousIndex {
+    fn from_enum_variant_id(db: &dyn HirDatabase, target_evid: EnumVariantId) -> Self {
+        // Find the index of this variant in the list of variants.
+        use hir_def::Lookup;
+        let i = target_evid.lookup(db.upcast()).index as usize;
+        EnumVariantContiguousIndex(i)
+    }
+
+    fn to_enum_variant_id(self, db: &dyn HirDatabase, eid: EnumId) -> EnumVariantId {
+        db.enum_data(eid).variants[self.0].0
+    }
+}
+
 #[derive(Clone)]
 pub(crate) struct MatchCheckCtx<'p> {
     module: ModuleId,
@@ -73,25 +91,27 @@
     }
 
     fn is_uninhabited(&self, ty: &Ty) -> bool {
-        is_ty_uninhabited_from(ty, self.module, self.db)
+        is_ty_uninhabited_from(self.db, ty, self.module)
     }
 
-    /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
-    fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool {
-        match ty.as_adt() {
-            Some((adt @ hir_def::AdtId::EnumId(_), _)) => {
-                let has_non_exhaustive_attr =
-                    self.db.attrs(adt.into()).by_key("non_exhaustive").exists();
-                let is_local = adt.module(self.db.upcast()).krate() == self.module.krate();
-                has_non_exhaustive_attr && !is_local
-            }
-            _ => false,
-        }
+    /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
+    fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool {
+        let is_local = adt.krate(self.db.upcast()) == self.module.krate();
+        !is_local && self.db.attrs(adt.into()).by_key("non_exhaustive").exists()
     }
 
-    fn variant_id_for_adt(ctor: &Constructor<Self>, adt: hir_def::AdtId) -> Option<VariantId> {
+    fn variant_id_for_adt(
+        db: &'p dyn HirDatabase,
+        ctor: &Constructor<Self>,
+        adt: hir_def::AdtId,
+    ) -> Option<VariantId> {
         match ctor {
-            &Variant(id) => Some(id.into()),
+            Variant(id) => {
+                let hir_def::AdtId::EnumId(eid) = adt else {
+                    panic!("bad constructor {ctor:?} for adt {adt:?}")
+                };
+                Some(id.to_enum_variant_id(db, eid).into())
+            }
             Struct | UnionField => match adt {
                 hir_def::AdtId::EnumId(_) => None,
                 hir_def::AdtId::StructId(id) => Some(id.into()),
@@ -175,19 +195,24 @@
                         ctor = Struct;
                         arity = 1;
                     }
-                    &TyKind::Adt(adt, _) => {
+                    &TyKind::Adt(AdtId(adt), _) => {
                         ctor = match pat.kind.as_ref() {
-                            PatKind::Leaf { .. } if matches!(adt.0, hir_def::AdtId::UnionId(_)) => {
+                            PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => {
                                 UnionField
                             }
                             PatKind::Leaf { .. } => Struct,
-                            PatKind::Variant { enum_variant, .. } => Variant(*enum_variant),
+                            PatKind::Variant { enum_variant, .. } => {
+                                Variant(EnumVariantContiguousIndex::from_enum_variant_id(
+                                    self.db,
+                                    *enum_variant,
+                                ))
+                            }
                             _ => {
                                 never!();
                                 Wildcard
                             }
                         };
-                        let variant = Self::variant_id_for_adt(&ctor, adt.0).unwrap();
+                        let variant = Self::variant_id_for_adt(self.db, &ctor, adt).unwrap();
                         arity = variant.variant_data(self.db.upcast()).fields().len();
                     }
                     _ => {
@@ -239,7 +264,7 @@
                     PatKind::Deref { subpattern: subpatterns.next().unwrap() }
                 }
                 TyKind::Adt(adt, substs) => {
-                    let variant = Self::variant_id_for_adt(pat.ctor(), adt.0).unwrap();
+                    let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap();
                     let subpatterns = self
                         .list_variant_fields(pat.ty(), variant)
                         .zip(subpatterns)
@@ -277,7 +302,7 @@
 impl<'p> PatCx for MatchCheckCtx<'p> {
     type Error = ();
     type Ty = Ty;
-    type VariantIdx = EnumVariantId;
+    type VariantIdx = EnumVariantContiguousIndex;
     type StrLit = Void;
     type ArmData = ();
     type PatData = PatData<'p>;
@@ -303,7 +328,7 @@
                         // patterns. If we're here we can assume this is a box pattern.
                         1
                     } else {
-                        let variant = Self::variant_id_for_adt(ctor, adt).unwrap();
+                        let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
                         variant.variant_data(self.db.upcast()).fields().len()
                     }
                 }
@@ -343,25 +368,22 @@
                         let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
                         single(subst_ty)
                     } else {
-                        let variant = Self::variant_id_for_adt(ctor, adt).unwrap();
-                        let (adt, _) = ty.as_adt().unwrap();
+                        let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
 
-                        let adt_is_local =
-                            variant.module(self.db.upcast()).krate() == self.module.krate();
                         // Whether we must not match the fields of this variant exhaustively.
-                        let is_non_exhaustive =
-                            self.db.attrs(variant.into()).by_key("non_exhaustive").exists()
-                                && !adt_is_local;
-                        let visibilities = self.db.field_visibilities(variant);
+                        let is_non_exhaustive = Lazy::new(|| self.is_foreign_non_exhaustive(adt));
+                        let visibilities = Lazy::new(|| self.db.field_visibilities(variant));
 
                         self.list_variant_fields(ty, variant)
                             .map(move |(fid, ty)| {
-                                let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
-                                    || visibilities[fid]
-                                        .is_visible_from(self.db.upcast(), self.module);
+                                let is_visible = || {
+                                    matches!(adt, hir_def::AdtId::EnumId(..))
+                                        || visibilities[fid]
+                                            .is_visible_from(self.db.upcast(), self.module)
+                                };
                                 let is_uninhabited = self.is_uninhabited(&ty);
                                 let private_uninhabited =
-                                    is_uninhabited && (!is_visible || is_non_exhaustive);
+                                    is_uninhabited && (!is_visible() || *is_non_exhaustive);
                                 (ty, PrivateUninhabitedField(private_uninhabited))
                             })
                             .collect()
@@ -413,23 +435,26 @@
             TyKind::Scalar(Scalar::Char) => unhandled(),
             TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(),
             TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
-            TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => {
-                let enum_data = cx.db.enum_data(*enum_id);
-                let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
+            &TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => {
+                let enum_data = cx.db.enum_data(enum_id);
+                let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
 
                 if enum_data.variants.is_empty() && !is_declared_nonexhaustive {
                     ConstructorSet::NoConstructors
                 } else {
-                    let mut variants = FxHashMap::default();
-                    for &(variant, _) in enum_data.variants.iter() {
+                    let mut variants = FxHashMap::with_capacity_and_hasher(
+                        enum_data.variants.len(),
+                        Default::default(),
+                    );
+                    for (i, &(variant, _)) in enum_data.variants.iter().enumerate() {
                         let is_uninhabited =
-                            is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db);
+                            is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
                         let visibility = if is_uninhabited {
                             VariantVisibility::Empty
                         } else {
                             VariantVisibility::Visible
                         };
-                        variants.insert(variant, visibility);
+                        variants.insert(EnumVariantContiguousIndex(i), visibility);
                     }
 
                     ConstructorSet::Variants {
@@ -453,10 +478,10 @@
         f: &mut fmt::Formatter<'_>,
         pat: &rustc_pattern_analysis::pat::DeconstructedPat<Self>,
     ) -> fmt::Result {
-        let variant =
-            pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt));
-
         let db = pat.data().db;
+        let variant =
+            pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(db, pat.ctor(), adt));
+
         if let Some(variant) = variant {
             match variant {
                 VariantId::EnumVariantId(v) => {
@@ -474,7 +499,7 @@
     }
 
     fn bug(&self, fmt: fmt::Arguments<'_>) {
-        debug!("{}", fmt)
+        never!("{}", fmt)
     }
 
     fn complexity_exceeded(&self) -> Result<(), Self::Error> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index cbca0e8..081b4d8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -4,7 +4,7 @@
 use hir_def::{
     body::Body,
     hir::{Expr, ExprId, UnaryOp},
-    resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+    resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
     DefWithBodyId,
 };
 
@@ -13,9 +13,9 @@
 };
 
 pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
-    let infer = db.infer(def);
-    let mut res = Vec::new();
+    let _p = tracing::span!(tracing::Level::INFO, "missing_unsafe").entered();
 
+    let mut res = Vec::new();
     let is_unsafe = match def {
         DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
         DefWithBodyId::StaticId(_)
@@ -28,6 +28,7 @@
     }
 
     let body = db.body(def);
+    let infer = db.infer(def);
     unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
         if !expr.inside_unsafe_block {
             res.push(expr.expr);
@@ -51,14 +52,24 @@
     current: ExprId,
     unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
 ) {
-    walk_unsafe(db, infer, def, body, current, false, unsafe_expr_cb)
+    walk_unsafe(
+        db,
+        infer,
+        body,
+        &mut resolver_for_expr(db.upcast(), def, current),
+        def,
+        current,
+        false,
+        unsafe_expr_cb,
+    )
 }
 
 fn walk_unsafe(
     db: &dyn HirDatabase,
     infer: &InferenceResult,
-    def: DefWithBodyId,
     body: &Body,
+    resolver: &mut Resolver,
+    def: DefWithBodyId,
     current: ExprId,
     inside_unsafe_block: bool,
     unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
@@ -73,13 +84,14 @@
             }
         }
         Expr::Path(path) => {
-            let resolver = resolver_for_expr(db.upcast(), def, current);
+            let g = resolver.update_to_inner_scope(db.upcast(), def, current);
             let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
             if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
                 if db.static_data(id).mutable {
                     unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
                 }
             }
+            resolver.reset_to_guard(g);
         }
         Expr::MethodCall { .. } => {
             if infer
@@ -97,13 +109,13 @@
         }
         Expr::Unsafe { .. } => {
             return expr.walk_child_exprs(|child| {
-                walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb);
+                walk_unsafe(db, infer, body, resolver, def, child, true, unsafe_expr_cb);
             });
         }
         _ => {}
     }
 
     expr.walk_child_exprs(|child| {
-        walk_unsafe(db, infer, def, body, child, inside_unsafe_block, unsafe_expr_cb);
+        walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb);
     });
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 8740ae6..a357e85 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -9,6 +9,7 @@
 
 use base_db::CrateId;
 use chalk_ir::{BoundVar, Safety, TyKind};
+use either::Either;
 use hir_def::{
     data::adt::VariantData,
     db::DefDatabase,
@@ -27,7 +28,7 @@
 use itertools::Itertools;
 use la_arena::ArenaMap;
 use smallvec::SmallVec;
-use stdx::never;
+use stdx::{never, IsNoneOr};
 use triomphe::Arc;
 
 use crate::{
@@ -40,10 +41,11 @@
     mir::pad16,
     primitive, to_assoc_type_id,
     utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
-    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
-    DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
-    MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
-    Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause,
+    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const,
+    ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime,
+    LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
+    QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty,
+    TyExt, WhereClause,
 };
 
 pub trait HirWrite: fmt::Write {
@@ -58,11 +60,18 @@
 impl HirWrite for fmt::Formatter<'_> {}
 
 pub struct HirFormatter<'a> {
+    /// The database handle
     pub db: &'a dyn HirDatabase,
+    /// The sink to write into
     fmt: &'a mut dyn HirWrite,
+    /// A buffer to intercept writes with, this allows us to track the overall size of the formatted output.
     buf: String,
+    /// The current size of the formatted output.
     curr_size: usize,
-    pub(crate) max_size: Option<usize>,
+    /// Size from which we should truncate the output.
+    max_size: Option<usize>,
+    /// When rendering something that has a concept of "children" (like fields in a struct), this limits
+    /// how many should be rendered.
     pub entity_limit: Option<usize>,
     omit_verbose_types: bool,
     closure_style: ClosureStyle,
@@ -302,7 +311,6 @@
 #[derive(Debug)]
 pub enum DisplaySourceCodeError {
     PathNotFound,
-    UnknownType,
     Coroutine,
     OpaqueType,
 }
@@ -414,12 +422,7 @@
         let proj_params_count =
             self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
         let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
-        if !proj_params.is_empty() {
-            write!(f, "<")?;
-            f.write_joined(proj_params, ", ")?;
-            write!(f, ">")?;
-        }
-        Ok(())
+        hir_fmt_generics(f, proj_params, None)
     }
 }
 
@@ -452,7 +455,7 @@
             ConstValue::Placeholder(idx) => {
                 let id = from_placeholder_idx(f.db, *idx);
                 let generics = generics(f.db.upcast(), id.parent);
-                let param_data = &generics.params.type_or_consts[id.local_id];
+                let param_data = &generics.params[id.local_id];
                 write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?;
                 Ok(())
             }
@@ -460,7 +463,11 @@
                 ConstScalar::Bytes(b, m) => render_const_scalar(f, b, m, &data.ty),
                 ConstScalar::UnevaluatedConst(c, parameters) => {
                     write!(f, "{}", c.name(f.db.upcast()))?;
-                    hir_fmt_generics(f, parameters, c.generic_def(f.db.upcast()))?;
+                    hir_fmt_generics(
+                        f,
+                        parameters.as_slice(Interner),
+                        c.generic_def(f.db.upcast()),
+                    )?;
                     Ok(())
                 }
                 ConstScalar::Unknown => f.write_char('_'),
@@ -936,36 +943,31 @@
                     }
                 };
                 f.end_location_link();
+
                 if parameters.len(Interner) > 0 {
                     let generics = generics(db.upcast(), def.into());
-                    let (
-                        parent_params,
-                        self_param,
-                        type_params,
-                        const_params,
-                        _impl_trait_params,
-                        lifetime_params,
-                    ) = generics.provenance_split();
-                    let total_len =
-                        parent_params + self_param + type_params + const_params + lifetime_params;
+                    let (parent_len, self_, type_, const_, impl_, lifetime) =
+                        generics.provenance_split();
+                    let parameters = parameters.as_slice(Interner);
                     // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
-                    if total_len > 0 {
+                    if parameters.len() - impl_ > 0 {
                         // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes
                         // parent's params (those from enclosing impl or trait, if any).
-                        let parameters = parameters.as_slice(Interner);
-                        let fn_params_len = self_param + type_params + const_params;
-                        // This will give slice till last type or const
-                        let fn_params = parameters.get(..fn_params_len);
-                        let fn_lt_params =
-                            parameters.get(fn_params_len..(fn_params_len + lifetime_params));
-                        let parent_params = parameters.get(parameters.len() - parent_params..);
-                        let params = parent_params
-                            .into_iter()
-                            .chain(fn_lt_params)
-                            .chain(fn_params)
-                            .flatten();
+                        let (fn_params, other) =
+                            parameters.split_at(self_ + type_ + const_ + lifetime);
+                        let (_impl, parent_params) = other.split_at(impl_);
+                        debug_assert_eq!(parent_params.len(), parent_len);
+
+                        let parent_params =
+                            generic_args_sans_defaults(f, Some(def.into()), parent_params);
+                        let fn_params = generic_args_sans_defaults(f, Some(def.into()), fn_params);
+
                         write!(f, "<")?;
-                        f.write_joined(params, ", ")?;
+                        hir_fmt_generic_arguments(f, parent_params)?;
+                        if !parent_params.is_empty() && !fn_params.is_empty() {
+                            write!(f, ", ")?;
+                        }
+                        hir_fmt_generic_arguments(f, fn_params)?;
                         write!(f, ">")?;
                     }
                 }
@@ -1009,7 +1011,7 @@
 
                 let generic_def = self.as_generic_def(db);
 
-                hir_fmt_generics(f, parameters, generic_def)?;
+                hir_fmt_generics(f, parameters.as_slice(Interner), generic_def)?;
             }
             TyKind::AssociatedType(assoc_type_id, parameters) => {
                 let type_alias = from_assoc_type_id(*assoc_type_id);
@@ -1032,20 +1034,15 @@
                     f.end_location_link();
                     // Note that the generic args for the associated type come before those for the
                     // trait (including the self type).
-                    // FIXME: reconsider the generic args order upon formatting?
-                    if parameters.len(Interner) > 0 {
-                        write!(f, "<")?;
-                        f.write_joined(parameters.as_slice(Interner), ", ")?;
-                        write!(f, ">")?;
-                    }
+                    hir_fmt_generics(f, parameters.as_slice(Interner), None)
                 } else {
                     let projection_ty = ProjectionTy {
                         associated_ty_id: to_assoc_type_id(type_alias),
                         substitution: parameters.clone(),
                     };
 
-                    projection_ty.hir_fmt(f)?;
-                }
+                    projection_ty.hir_fmt(f)
+                }?;
             }
             TyKind::Foreign(type_alias) => {
                 let alias = from_foreign_def_id(*type_alias);
@@ -1072,6 +1069,7 @@
                         write_bounds_like_dyn_trait_with_prefix(
                             f,
                             "impl",
+                            Either::Left(self),
                             bounds.skip_binders(),
                             SizedByDefault::Sized { anchor: krate },
                         )?;
@@ -1087,6 +1085,7 @@
                         write_bounds_like_dyn_trait_with_prefix(
                             f,
                             "impl",
+                            Either::Left(self),
                             bounds.skip_binders(),
                             SizedByDefault::Sized { anchor: krate },
                         )?;
@@ -1137,7 +1136,7 @@
                     }
                     ClosureStyle::ClosureWithSubst => {
                         write!(f, "{{closure#{:?}}}", id.0.as_u32())?;
-                        return hir_fmt_generics(f, substs, None);
+                        return hir_fmt_generics(f, substs.as_slice(Interner), None);
                     }
                     _ => (),
                 }
@@ -1173,7 +1172,7 @@
             TyKind::Placeholder(idx) => {
                 let id = from_placeholder_idx(db, *idx);
                 let generics = generics(db.upcast(), id.parent);
-                let param_data = &generics.params.type_or_consts[id.local_id];
+                let param_data = &generics.params[id.local_id];
                 match param_data {
                     TypeOrConstParamData::TypeParamData(p) => match p.provenance {
                         TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
@@ -1189,21 +1188,24 @@
                                 .generic_predicates(id.parent)
                                 .iter()
                                 .map(|pred| pred.clone().substitute(Interner, &substs))
-                                .filter(|wc| match &wc.skip_binders() {
+                                .filter(|wc| match wc.skip_binders() {
                                     WhereClause::Implemented(tr) => {
-                                        &tr.self_type_parameter(Interner) == self
+                                        tr.self_type_parameter(Interner) == *self
                                     }
                                     WhereClause::AliasEq(AliasEq {
                                         alias: AliasTy::Projection(proj),
                                         ty: _,
-                                    }) => &proj.self_type_parameter(db) == self,
-                                    _ => false,
+                                    }) => proj.self_type_parameter(db) == *self,
+                                    WhereClause::AliasEq(_) => false,
+                                    WhereClause::TypeOutlives(to) => to.ty == *self,
+                                    WhereClause::LifetimeOutlives(_) => false,
                                 })
                                 .collect::<Vec<_>>();
                             let krate = id.parent.module(db.upcast()).krate();
                             write_bounds_like_dyn_trait_with_prefix(
                                 f,
                                 "impl",
+                                Either::Left(self),
                                 &bounds,
                                 SizedByDefault::Sized { anchor: krate },
                             )?;
@@ -1229,6 +1231,7 @@
                 write_bounds_like_dyn_trait_with_prefix(
                     f,
                     "dyn",
+                    Either::Left(self),
                     &bounds,
                     SizedByDefault::NotSized,
                 )?;
@@ -1252,6 +1255,7 @@
                         write_bounds_like_dyn_trait_with_prefix(
                             f,
                             "impl",
+                            Either::Left(self),
                             bounds.skip_binders(),
                             SizedByDefault::Sized { anchor: krate },
                         )?;
@@ -1266,6 +1270,7 @@
                         write_bounds_like_dyn_trait_with_prefix(
                             f,
                             "impl",
+                            Either::Left(self),
                             bounds.skip_binders(),
                             SizedByDefault::Sized { anchor: krate },
                         )?;
@@ -1277,11 +1282,10 @@
             }
             TyKind::Error => {
                 if f.display_target.is_source_code() {
-                    return Err(HirDisplayError::DisplaySourceCodeError(
-                        DisplaySourceCodeError::UnknownType,
-                    ));
+                    f.write_char('_')?;
+                } else {
+                    write!(f, "{{unknown}}")?;
                 }
-                write!(f, "{{unknown}}")?;
             }
             TyKind::InferenceVar(..) => write!(f, "_")?,
             TyKind::Coroutine(_, subst) => {
@@ -1318,93 +1322,92 @@
 
 fn hir_fmt_generics(
     f: &mut HirFormatter<'_>,
-    parameters: &Substitution,
+    parameters: &[GenericArg],
     generic_def: Option<hir_def::GenericDefId>,
 ) -> Result<(), HirDisplayError> {
-    let db = f.db;
-    if parameters.len(Interner) > 0 {
-        use std::cmp::Ordering;
-        let param_compare =
-            |a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) {
-                (crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => {
-                    Ordering::Equal
-                }
-                (crate::GenericArgData::Lifetime(_), _) => Ordering::Less,
-                (_, crate::GenericArgData::Lifetime(_)) => Ordering::Less,
-                (_, _) => Ordering::Equal,
-            };
-        let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
-            match generic_def
-                .map(|generic_def_id| db.generic_defaults(generic_def_id))
-                .filter(|defaults| !defaults.is_empty())
-            {
-                None => parameters.as_slice(Interner),
-                Some(default_parameters) => {
-                    fn should_show(
-                        parameter: &GenericArg,
-                        default_parameters: &[Binders<GenericArg>],
-                        i: usize,
-                        parameters: &Substitution,
-                    ) -> bool {
-                        if parameter.ty(Interner).map(|it| it.kind(Interner))
-                            == Some(&TyKind::Error)
-                        {
-                            return true;
-                        }
-                        if let Some(ConstValue::Concrete(c)) =
-                            parameter.constant(Interner).map(|it| &it.data(Interner).value)
-                        {
-                            if c.interned == ConstScalar::Unknown {
-                                return true;
-                            }
-                        }
-                        if parameter.lifetime(Interner).map(|it| it.data(Interner))
-                            == Some(&crate::LifetimeData::Static)
-                        {
-                            return true;
-                        }
-                        let default_parameter = match default_parameters.get(i) {
-                            Some(it) => it,
-                            None => return true,
-                        };
-                        let actual_default =
-                            default_parameter.clone().substitute(Interner, &parameters);
-                        parameter != &actual_default
-                    }
-                    let mut default_from = 0;
-                    for (i, parameter) in parameters.iter(Interner).enumerate() {
-                        if should_show(parameter, &default_parameters, i, parameters) {
-                            default_from = i + 1;
-                        }
-                    }
-                    &parameters.as_slice(Interner)[0..default_from]
-                }
-            }
-        } else {
-            parameters.as_slice(Interner)
-        };
-        //FIXME: Should handle the ordering of lifetimes when creating substitutions
-        let mut parameters_to_write = parameters_to_write.to_vec();
-        parameters_to_write.sort_by(param_compare);
-        if !parameters_to_write.is_empty() {
-            write!(f, "<")?;
-            let mut first = true;
-            for generic_arg in parameters_to_write {
-                if !first {
-                    write!(f, ", ")?;
-                }
-                first = false;
-                if f.display_target.is_source_code()
-                    && generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) == Some(&TyKind::Error)
-                {
-                    write!(f, "_")?;
-                } else {
-                    generic_arg.hir_fmt(f)?;
-                }
-            }
+    if parameters.is_empty() {
+        return Ok(());
+    }
 
-            write!(f, ">")?;
+    let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters);
+    if !parameters_to_write.is_empty() {
+        write!(f, "<")?;
+        hir_fmt_generic_arguments(f, parameters_to_write)?;
+        write!(f, ">")?;
+    }
+
+    Ok(())
+}
+
+fn generic_args_sans_defaults<'ga>(
+    f: &mut HirFormatter<'_>,
+    generic_def: Option<hir_def::GenericDefId>,
+    parameters: &'ga [GenericArg],
+) -> &'ga [GenericArg] {
+    if f.display_target.is_source_code() || f.omit_verbose_types() {
+        match generic_def
+            .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
+            .filter(|it| !it.is_empty())
+        {
+            None => parameters,
+            Some(default_parameters) => {
+                let should_show = |arg: &GenericArg, i: usize| {
+                    let is_err = |arg: &GenericArg| match arg.data(Interner) {
+                        chalk_ir::GenericArgData::Lifetime(it) => {
+                            *it.data(Interner) == LifetimeData::Error
+                        }
+                        chalk_ir::GenericArgData::Ty(it) => *it.kind(Interner) == TyKind::Error,
+                        chalk_ir::GenericArgData::Const(it) => matches!(
+                            it.data(Interner).value,
+                            ConstValue::Concrete(ConcreteConst {
+                                interned: ConstScalar::Unknown,
+                                ..
+                            })
+                        ),
+                    };
+                    // if the arg is error like, render it to inform the user
+                    if is_err(arg) {
+                        return true;
+                    }
+                    // otherwise, if the arg is equal to the param default, hide it (unless the
+                    // default is an error which can happen for the trait Self type)
+                    default_parameters.get(i).is_none_or(|default_parameter| {
+                        // !is_err(default_parameter.skip_binders())
+                        //     &&
+                        arg != &default_parameter.clone().substitute(Interner, &parameters)
+                    })
+                };
+                let mut default_from = 0;
+                for (i, parameter) in parameters.iter().enumerate() {
+                    if should_show(parameter, i) {
+                        default_from = i + 1;
+                    }
+                }
+                &parameters[0..default_from]
+            }
         }
+    } else {
+        parameters
+    }
+}
+
+fn hir_fmt_generic_arguments(
+    f: &mut HirFormatter<'_>,
+    parameters: &[GenericArg],
+) -> Result<(), HirDisplayError> {
+    let mut first = true;
+    let lifetime_offset = parameters.iter().position(|arg| arg.lifetime(Interner).is_some());
+
+    let (ty_or_const, lifetimes) = match lifetime_offset {
+        Some(offset) => parameters.split_at(offset),
+        None => (parameters, &[][..]),
+    };
+    for generic_arg in lifetimes.iter().chain(ty_or_const) {
+        if !first {
+            write!(f, ", ")?;
+        }
+        first = false;
+        generic_arg.hir_fmt(f)?;
     }
     Ok(())
 }
@@ -1468,6 +1471,7 @@
 pub fn write_bounds_like_dyn_trait_with_prefix(
     f: &mut HirFormatter<'_>,
     prefix: &str,
+    this: Either<&Ty, &Lifetime>,
     predicates: &[QuantifiedWhereClause],
     default_sized: SizedByDefault,
 ) -> Result<(), HirDisplayError> {
@@ -1476,7 +1480,7 @@
         || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. })
     {
         write!(f, " ")?;
-        write_bounds_like_dyn_trait(f, predicates, default_sized)
+        write_bounds_like_dyn_trait(f, this, predicates, default_sized)
     } else {
         Ok(())
     }
@@ -1484,6 +1488,7 @@
 
 fn write_bounds_like_dyn_trait(
     f: &mut HirFormatter<'_>,
+    this: Either<&Ty, &Lifetime>,
     predicates: &[QuantifiedWhereClause],
     default_sized: SizedByDefault,
 ) -> Result<(), HirDisplayError> {
@@ -1524,23 +1529,54 @@
                 f.start_location_link(trait_.into());
                 write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
                 f.end_location_link();
-                if let [_, params @ ..] = trait_ref.substitution.as_slice(Interner) {
-                    if is_fn_trait {
+                if is_fn_trait {
+                    if let [_self, params @ ..] = trait_ref.substitution.as_slice(Interner) {
                         if let Some(args) =
                             params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
                         {
                             write!(f, "(")?;
-                            f.write_joined(args.as_slice(Interner), ", ")?;
+                            hir_fmt_generic_arguments(f, args.as_slice(Interner))?;
                             write!(f, ")")?;
                         }
-                    } else if !params.is_empty() {
-                        write!(f, "<")?;
-                        f.write_joined(params, ", ")?;
-                        // there might be assoc type bindings, so we leave the angle brackets open
-                        angle_open = true;
+                    }
+                } else {
+                    let params = generic_args_sans_defaults(
+                        f,
+                        Some(trait_.into()),
+                        trait_ref.substitution.as_slice(Interner),
+                    );
+                    if let [_self, params @ ..] = params {
+                        if !params.is_empty() {
+                            write!(f, "<")?;
+                            hir_fmt_generic_arguments(f, params)?;
+                            // there might be assoc type bindings, so we leave the angle brackets open
+                            angle_open = true;
+                        }
                     }
                 }
             }
+            WhereClause::TypeOutlives(to) if Either::Left(&to.ty) == this => {
+                if !is_fn_trait && angle_open {
+                    write!(f, ">")?;
+                    angle_open = false;
+                }
+                if !first {
+                    write!(f, " + ")?;
+                }
+                to.lifetime.hir_fmt(f)?;
+            }
+            WhereClause::TypeOutlives(_) => {}
+            WhereClause::LifetimeOutlives(lo) if Either::Right(&lo.a) == this => {
+                if !is_fn_trait && angle_open {
+                    write!(f, ">")?;
+                    angle_open = false;
+                }
+                if !first {
+                    write!(f, " + ")?;
+                }
+                lo.b.hir_fmt(f)?;
+            }
+            WhereClause::LifetimeOutlives(_) => {}
             WhereClause::AliasEq(alias_eq) if is_fn_trait => {
                 is_fn_trait = false;
                 if !alias_eq.ty.is_unit() {
@@ -1567,9 +1603,9 @@
                     let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
                     if proj_arg_count > 0 {
                         write!(f, "<")?;
-                        f.write_joined(
+                        hir_fmt_generic_arguments(
+                            f,
                             &proj.substitution.as_slice(Interner)[..proj_arg_count],
-                            ", ",
                         )?;
                         write!(f, ">")?;
                     }
@@ -1577,10 +1613,6 @@
                 }
                 ty.hir_fmt(f)?;
             }
-
-            // FIXME implement these
-            WhereClause::LifetimeOutlives(_) => {}
-            WhereClause::TypeOutlives(_) => {}
         }
         first = false;
     }
@@ -1630,12 +1662,7 @@
     f.start_location_link(trait_.into());
     write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
     f.end_location_link();
-    if tr.substitution.len(Interner) > 1 {
-        write!(f, "<")?;
-        f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?;
-        write!(f, ">")?;
-    }
-    Ok(())
+    hir_fmt_generics(f, &tr.substitution.as_slice(Interner)[1..], None)
 }
 
 impl HirDisplay for TraitRef {
@@ -1690,16 +1717,18 @@
 impl HirDisplay for LifetimeData {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         match self {
-            LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
-            LifetimeData::InferenceVar(_) => write!(f, "_"),
             LifetimeData::Placeholder(idx) => {
                 let id = lt_from_placeholder_idx(f.db, *idx);
                 let generics = generics(f.db.upcast(), id.parent);
-                let param_data = &generics.params.lifetimes[id.local_id];
+                let param_data = &generics.params[id.local_id];
                 write!(f, "{}", param_data.name.display(f.db.upcast()))?;
                 Ok(())
             }
+            _ if f.display_target.is_source_code() => write!(f, "'_"),
+            LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
+            LifetimeData::InferenceVar(_) => write!(f, "_"),
             LifetimeData::Static => write!(f, "'static"),
+            LifetimeData::Error => write!(f, "'{{error}}"),
             LifetimeData::Erased => Ok(()),
             LifetimeData::Phantom(_, _) => Ok(()),
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index be3b50e..281386e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -55,12 +55,12 @@
 
 use crate::{
     db::HirDatabase,
-    fold_tys,
+    error_lifetime, fold_tys,
     infer::{coerce::CoerceMany, unify::InferenceTable},
     lower::ImplTraitLoweringMode,
-    static_lifetime, to_assoc_type_id,
+    to_assoc_type_id,
     traits::FnTrait,
-    utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
+    utils::{Generics, InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
     AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
     ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution,
     TraitEnvironment, Ty, TyBuilder, TyExt,
@@ -326,7 +326,7 @@
 
 impl Adjustment {
     pub fn borrow(m: Mutability, ty: Ty) -> Self {
-        let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
+        let ty = TyKind::Ref(m, error_lifetime(), ty).intern(Interner);
         Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
     }
 }
@@ -429,7 +429,10 @@
     /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
     pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
     type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
+    /// Whether there are any type-mismatching errors in the result.
+    pub(crate) has_errors: bool,
     /// Interned common types to return references to.
+    // FIXME: Move this into `InferenceContext`
     standard_types: InternedStandardTypes,
     /// Stores the types which were implicitly dereferenced in pattern binding modes.
     pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
@@ -628,6 +631,10 @@
         }
     }
 
+    pub(crate) fn generics(&self) -> Option<Generics> {
+        Some(crate::utils::generics(self.db.upcast(), self.resolver.generic_def()?))
+    }
+
     // FIXME: This function should be private in module. It is currently only used in the consteval, since we need
     // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
     // used this function for another workaround, mention it here. If you really need this function and believe that
@@ -654,6 +661,7 @@
             type_of_rpit,
             type_of_for_iterator,
             type_mismatches,
+            has_errors,
             standard_types: _,
             pat_adjustments,
             binding_modes: _,
@@ -695,6 +703,9 @@
         for ty in type_of_for_iterator.values_mut() {
             *ty = table.resolve_completely(ty.clone());
         }
+
+        *has_errors = !type_mismatches.is_empty();
+
         type_mismatches.retain(|_, mismatch| {
             mismatch.expected = table.resolve_completely(mismatch.expected.clone());
             mismatch.actual = table.resolve_completely(mismatch.actual.clone());
@@ -1646,9 +1657,11 @@
         *self = *self | other;
     }
 }
-/// A zipper that checks for unequal `{unknown}` occurrences in the two types. Used to filter out
-/// mismatch diagnostics that only differ in `{unknown}`. These mismatches are usually not helpful.
-/// As the cause is usually an underlying name resolution problem.
+
+/// A zipper that checks for unequal occurrences of `{unknown}` and unresolved projections
+/// in the two types. Used to filter out mismatch diagnostics that only differ in
+/// `{unknown}` and unresolved projections. These mismatches are usually not helpful.
+/// As the cause is usually an underlying name resolution problem
 struct UnknownMismatch<'db>(&'db dyn HirDatabase);
 impl chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_> {
     fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> {
@@ -1721,7 +1734,12 @@
                 zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)?
             }
             (TyKind::Error, TyKind::Error) => (),
-            (TyKind::Error, _) | (_, TyKind::Error) => return Err(chalk_ir::NoSolution),
+            (TyKind::Error, _)
+            | (_, TyKind::Error)
+            | (TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _), _)
+            | (_, TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _)) => {
+                return Err(chalk_ir::NoSolution)
+            }
             _ => (),
         }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
index f8c03ee..060b5f3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
@@ -19,10 +19,6 @@
         let expr_ty = table.resolve_ty_shallow(&self.expr_ty);
         let cast_ty = table.resolve_ty_shallow(&self.cast_ty);
 
-        if expr_ty.contains_unknown() || cast_ty.contains_unknown() {
-            return;
-        }
-
         if table.coerce(&expr_ty, &cast_ty).is_ok() {
             return;
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 32845ac..a25498e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -22,11 +22,11 @@
 
 use crate::{
     db::{HirDatabase, InternedClosure},
-    from_chalk_trait_id, from_placeholder_idx, make_binders,
+    error_lifetime, from_chalk_trait_id, from_placeholder_idx, make_binders,
     mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
-    static_lifetime, to_chalk_trait_id,
+    to_chalk_trait_id,
     traits::FnTrait,
-    utils::{self, elaborate_clause_supertraits, generics, Generics},
+    utils::{self, elaborate_clause_supertraits, Generics},
     Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy,
     DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTyExt, Substitution, Ty,
     TyExt, WhereClause,
@@ -324,21 +324,17 @@
                     BorrowKind::Mut { .. } => Mutability::Mut,
                     _ => Mutability::Not,
                 };
-                TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
+                TyKind::Ref(m, error_lifetime(), ty).intern(Interner)
             }
         };
         return CapturedItem {
             place: self.place,
             kind: self.kind,
             span: self.span,
-            ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty),
+            ty: replace_placeholder_with_binder(ctx, ty),
         };
 
-        fn replace_placeholder_with_binder(
-            db: &dyn HirDatabase,
-            owner: DefWithBodyId,
-            ty: Ty,
-        ) -> Binders<Ty> {
+        fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders<Ty> {
             struct Filler<'a> {
                 db: &'a dyn HirDatabase,
                 generics: Generics,
@@ -361,7 +357,7 @@
                     outer_binder: DebruijnIndex,
                 ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
                     let x = from_placeholder_idx(self.db, idx);
-                    let Some(idx) = self.generics.param_idx(x) else {
+                    let Some(idx) = self.generics.type_or_const_param_idx(x) else {
                         return Err(());
                     };
                     Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty))
@@ -373,18 +369,18 @@
                     outer_binder: DebruijnIndex,
                 ) -> std::result::Result<Ty, Self::Error> {
                     let x = from_placeholder_idx(self.db, idx);
-                    let Some(idx) = self.generics.param_idx(x) else {
+                    let Some(idx) = self.generics.type_or_const_param_idx(x) else {
                         return Err(());
                     };
                     Ok(BoundVar::new(outer_binder, idx).to_ty(Interner))
                 }
             }
-            let Some(generic_def) = owner.as_generic_def_id() else {
+            let Some(generics) = ctx.generics() else {
                 return Binders::empty(Interner, ty);
             };
-            let filler = &mut Filler { db, generics: generics(db.upcast(), generic_def) };
+            let filler = &mut Filler { db: ctx.db, generics };
             let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty);
-            make_binders(db, &filler.generics, result)
+            make_binders(ctx.db, &filler.generics, result)
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index cfbbc9d..7292885 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -18,11 +18,11 @@
 use crate::{
     autoderef::{Autoderef, AutoderefKind},
     db::HirDatabase,
+    error_lifetime,
     infer::{
         Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
         TypeError, TypeMismatch,
     },
-    static_lifetime,
     utils::ClosureSubst,
     Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
     Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
@@ -427,7 +427,7 @@
             // compare those. Note that this means we use the target
             // mutability [1], since it may be that we are coercing
             // from `&mut T` to `&U`.
-            let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc
+            let lt = error_lifetime(); // FIXME: handle lifetimes correctly, see rustc
             let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner);
             match autoderef.table.try_unify(&derefd_from_ty, to_ty) {
                 Ok(result) => {
@@ -621,7 +621,7 @@
             (TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => {
                 coerce_mutabilities(*from_mt, to_mt)?;
 
-                let lt = static_lifetime();
+                let lt = error_lifetime();
                 Some((
                     Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() },
                     Adjustment {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 35d5967..d011a62 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -23,6 +23,7 @@
     autoderef::{builtin_deref, deref_by_trait, Autoderef},
     consteval,
     db::{InternedClosure, InternedCoroutine},
+    error_lifetime,
     infer::{
         coerce::{CoerceMany, CoercionCause},
         find_continuable,
@@ -630,7 +631,7 @@
                 let inner_ty = self.infer_expr_inner(*expr, &expectation);
                 match rawness {
                     Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
-                    Rawness::Ref => TyKind::Ref(mutability, static_lifetime(), inner_ty),
+                    Rawness::Ref => TyKind::Ref(mutability, error_lifetime(), inner_ty),
                 }
                 .intern(Interner)
             }
@@ -1039,18 +1040,12 @@
 
                 (
                     elem_ty,
-                    if let Some(g_def) = self.owner.as_generic_def_id() {
-                        let generics = generics(self.db.upcast(), g_def);
-                        consteval::eval_to_const(
-                            repeat,
-                            ParamLoweringMode::Placeholder,
-                            self,
-                            || generics,
-                            DebruijnIndex::INNERMOST,
-                        )
-                    } else {
-                        consteval::usize_const(self.db, None, krate)
-                    },
+                    consteval::eval_to_const(
+                        repeat,
+                        ParamLoweringMode::Placeholder,
+                        self,
+                        DebruijnIndex::INNERMOST,
+                    ),
                 )
             }
         };
@@ -1851,7 +1846,7 @@
                             ty,
                             c,
                             ParamLoweringMode::Placeholder,
-                            || generics(this.db.upcast(), this.resolver.generic_def().unwrap()),
+                            || this.generics(),
                             DebruijnIndex::INNERMOST,
                         )
                     },
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 09a4d99..1b35493 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -2,21 +2,22 @@
 
 use std::iter::repeat_with;
 
-use chalk_ir::Mutability;
 use hir_def::{
     body::Body,
     hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
     path::Path,
 };
 use hir_expand::name::Name;
+use stdx::TupleExt;
 
 use crate::{
     consteval::{try_const_usize, usize_const},
+    error_lifetime,
     infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
     lower::lower_to_chalk_mutability,
     primitive::UintTy,
-    static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt,
-    TyKind,
+    static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
+    TyBuilder, TyExt, TyKind,
 };
 
 /// Used to generalize patterns and assignee expressions.
@@ -89,9 +90,6 @@
 
         self.unify(&ty, expected);
 
-        let substs =
-            ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
-
         match def {
             _ if subs.is_empty() => {}
             Some(def) => {
@@ -108,8 +106,10 @@
                 let pre_iter = pre.iter().enumerate();
                 let post_iter = (post_idx_offset..).zip(post.iter());
 
+                let substs = ty.as_adt().map(TupleExt::tail);
+
                 for (i, &subpat) in pre_iter.chain(post_iter) {
-                    let field_def = {
+                    let expected_ty = {
                         match variant_data.field(&Name::new_tuple_field(i)) {
                             Some(local_id) => {
                                 if !visibilities[local_id]
@@ -117,17 +117,17 @@
                                 {
                                     // FIXME(DIAGNOSE): private tuple field
                                 }
-                                Some(local_id)
+                                let f = field_types[local_id].clone();
+                                let expected_ty = match substs {
+                                    Some(substs) => f.substitute(Interner, substs),
+                                    None => f.substitute(Interner, &Substitution::empty(Interner)),
+                                };
+                                self.normalize_associated_types_in(expected_ty)
                             }
-                            None => None,
+                            None => self.err_ty(),
                         }
                     };
 
-                    let expected_ty = field_def.map_or(self.err_ty(), |f| {
-                        field_types[f].clone().substitute(Interner, &substs)
-                    });
-                    let expected_ty = self.normalize_associated_types_in(expected_ty);
-
                     T::infer(self, subpat, &expected_ty, default_bm);
                 }
             }
@@ -149,7 +149,7 @@
         expected: &Ty,
         default_bm: T::BindingMode,
         id: T,
-        subs: impl Iterator<Item = (Name, T)> + ExactSizeIterator,
+        subs: impl ExactSizeIterator<Item = (Name, T)>,
     ) -> Ty {
         let (ty, def) = self.resolve_variant(path, false);
         if let Some(variant) = def {
@@ -158,9 +158,6 @@
 
         self.unify(&ty, expected);
 
-        let substs =
-            ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
-
         match def {
             _ if subs.len() == 0 => {}
             Some(def) => {
@@ -168,8 +165,10 @@
                 let variant_data = def.variant_data(self.db.upcast());
                 let visibilities = self.db.field_visibilities(def);
 
+                let substs = ty.as_adt().map(TupleExt::tail);
+
                 for (name, inner) in subs {
-                    let field_def = {
+                    let expected_ty = {
                         match variant_data.field(&name) {
                             Some(local_id) => {
                                 if !visibilities[local_id]
@@ -180,23 +179,23 @@
                                         private: true,
                                     });
                                 }
-                                Some(local_id)
+                                let f = field_types[local_id].clone();
+                                let expected_ty = match substs {
+                                    Some(substs) => f.substitute(Interner, substs),
+                                    None => f.substitute(Interner, &Substitution::empty(Interner)),
+                                };
+                                self.normalize_associated_types_in(expected_ty)
                             }
                             None => {
                                 self.push_diagnostic(InferenceDiagnostic::NoSuchField {
                                     field: inner.into(),
                                     private: false,
                                 });
-                                None
+                                self.err_ty()
                             }
                         }
                     };
 
-                    let expected_ty = field_def.map_or(self.err_ty(), |f| {
-                        field_types[f].clone().substitute(Interner, &substs)
-                    });
-                    let expected_ty = self.normalize_associated_types_in(expected_ty);
-
                     T::infer(self, inner, &expected_ty, default_bm);
                 }
             }
@@ -396,14 +395,14 @@
             None => {
                 let inner_ty = self.table.new_type_var();
                 let ref_ty =
-                    TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner);
+                    TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner);
                 // Unification failure will be reported by the caller.
                 self.unify(&ref_ty, expected);
                 inner_ty
             }
         };
         let subty = self.infer_pat(inner_pat, &expectation, default_bm);
-        TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
+        TyKind::Ref(mutability, error_lifetime(), subty).intern(Interner)
     }
 
     fn infer_bind_pat(
@@ -430,7 +429,7 @@
 
         let bound_ty = match mode {
             BindingMode::Ref(mutability) => {
-                TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner)
+                TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner)
             }
             BindingMode::Move => inner_ty.clone(),
         };
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index afb89fe..b68fefc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -16,8 +16,8 @@
 
 use super::{InferOk, InferResult, InferenceContext, TypeError};
 use crate::{
-    consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts,
-    static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical,
+    consteval::unknown_const, db::HirDatabase, error_lifetime, fold_generic_args,
+    fold_tys_and_consts, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical,
     Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData,
     Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy,
     ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
@@ -43,40 +43,21 @@
         let obligations = pending_obligations
             .iter()
             .filter_map(|obligation| match obligation.value.value.goal.data(Interner) {
-                GoalData::DomainGoal(DomainGoal::Holds(
-                    clause @ WhereClause::AliasEq(AliasEq {
-                        alias: AliasTy::Projection(projection),
-                        ..
-                    }),
-                )) => {
-                    let projection_self = projection.self_type_parameter(self.db);
-                    let uncanonical = chalk_ir::Substitute::apply(
-                        &obligation.free_vars,
-                        projection_self,
-                        Interner,
-                    );
-                    if matches!(
-                        self.resolve_ty_shallow(&uncanonical).kind(Interner),
-                        TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root,
-                    ) {
-                        Some(chalk_ir::Substitute::apply(
-                            &obligation.free_vars,
-                            clause.clone(),
-                            Interner,
-                        ))
-                    } else {
-                        None
-                    }
-                }
-                GoalData::DomainGoal(DomainGoal::Holds(
-                    clause @ WhereClause::Implemented(trait_ref),
-                )) => {
-                    let trait_ref_self = trait_ref.self_type_parameter(Interner);
-                    let uncanonical = chalk_ir::Substitute::apply(
-                        &obligation.free_vars,
-                        trait_ref_self,
-                        Interner,
-                    );
+                GoalData::DomainGoal(DomainGoal::Holds(clause)) => {
+                    let ty = match clause {
+                        WhereClause::AliasEq(AliasEq {
+                            alias: AliasTy::Projection(projection),
+                            ..
+                        }) => projection.self_type_parameter(self.db),
+                        WhereClause::Implemented(trait_ref) => {
+                            trait_ref.self_type_parameter(Interner)
+                        }
+                        WhereClause::TypeOutlives(to) => to.ty.clone(),
+                        _ => return None,
+                    };
+
+                    let uncanonical =
+                        chalk_ir::Substitute::apply(&obligation.free_vars, ty, Interner);
                     if matches!(
                         self.resolve_ty_shallow(&uncanonical).kind(Interner),
                         TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root,
@@ -121,8 +102,9 @@
                 VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(Interner),
                 VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(Interner),
                 VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner),
-                // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere
-                VariableKind::Lifetime => static_lifetime().cast(Interner),
+                // Chalk can sometimes return new lifetime variables. We just replace them by errors
+                // for now.
+                VariableKind::Lifetime => error_lifetime().cast(Interner),
                 VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner),
             }),
         );
@@ -1020,11 +1002,11 @@
             _var: InferenceVar,
             _outer_binder: DebruijnIndex,
         ) -> Lifetime {
-            // fall back all lifetimes to 'static -- currently we don't deal
+            // fall back all lifetimes to 'error -- currently we don't deal
             // with any lifetimes, but we can sometimes get some lifetime
             // variables through Chalk's unification, and this at least makes
             // sure we don't leak them outside of inference
-            crate::static_lifetime()
+            crate::error_lifetime()
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
index 532b650..7546369 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
@@ -5,42 +5,36 @@
     visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
     DebruijnIndex,
 };
-use hir_def::{
-    attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
-    ModuleId, VariantId,
-};
+use hir_def::{visibility::Visibility, AdtId, EnumVariantId, HasModule, ModuleId, VariantId};
 use rustc_hash::FxHashSet;
 
 use crate::{
     consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
 };
 
+// FIXME: Turn this into a query, it can be quite slow
 /// Checks whether a type is visibly uninhabited from a particular module.
-pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
+pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
+    let _p = tracing::span!(tracing::Level::INFO, "is_ty_uninhabited_from", ?ty).entered();
     let mut uninhabited_from =
         UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
     let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
     inhabitedness == BREAK_VISIBLY_UNINHABITED
 }
 
+// FIXME: Turn this into a query, it can be quite slow
 /// Checks whether a variant is visibly uninhabited from a particular module.
 pub(crate) fn is_enum_variant_uninhabited_from(
+    db: &dyn HirDatabase,
     variant: EnumVariantId,
     subst: &Substitution,
     target_mod: ModuleId,
-    db: &dyn HirDatabase,
 ) -> bool {
-    let is_local = variant.module(db.upcast()).krate() == target_mod.krate();
+    let _p = tracing::span!(tracing::Level::INFO, "is_enum_variant_uninhabited_from").entered();
 
     let mut uninhabited_from =
         UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
-    let inhabitedness = uninhabited_from.visit_variant(
-        variant.into(),
-        &db.enum_variant_data(variant).variant_data,
-        subst,
-        &db.attrs(variant.into()),
-        is_local,
-    );
+    let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
     inhabitedness == BREAK_VISIBLY_UNINHABITED
 }
 
@@ -98,34 +92,18 @@
 
 impl UninhabitedFrom<'_> {
     fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
-        let attrs = self.db.attrs(adt.into());
-        let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
-        let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
-        if adt_non_exhaustive && !is_local {
-            return CONTINUE_OPAQUELY_INHABITED;
-        }
-
         // An ADT is uninhabited iff all its variants uninhabited.
         match adt {
             // rustc: For now, `union`s are never considered uninhabited.
             AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
-            AdtId::StructId(s) => {
-                let struct_data = self.db.struct_data(s);
-                self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
-            }
+            AdtId::StructId(s) => self.visit_variant(s.into(), subst),
             AdtId::EnumId(e) => {
                 let enum_data = self.db.enum_data(e);
 
                 for &(variant, _) in enum_data.variants.iter() {
-                    let variant_inhabitedness = self.visit_variant(
-                        variant.into(),
-                        &self.db.enum_variant_data(variant).variant_data,
-                        subst,
-                        &self.db.attrs(variant.into()),
-                        is_local,
-                    );
+                    let variant_inhabitedness = self.visit_variant(variant.into(), subst);
                     match variant_inhabitedness {
-                        Break(VisiblyUninhabited) => continue,
+                        Break(VisiblyUninhabited) => (),
                         Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
                     }
                 }
@@ -137,34 +115,36 @@
     fn visit_variant(
         &mut self,
         variant: VariantId,
-        variant_data: &VariantData,
         subst: &Substitution,
-        attrs: &Attrs,
-        is_local: bool,
     ) -> ControlFlow<VisiblyUninhabited> {
-        let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists();
-        if non_exhaustive_field_list && !is_local {
+        let is_local = variant.krate(self.db.upcast()) == self.target_mod.krate();
+        if !is_local && self.db.attrs(variant.into()).by_key("non_exhaustive").exists() {
+            return CONTINUE_OPAQUELY_INHABITED;
+        }
+
+        let variant_data = self.db.variant_data(variant);
+        let fields = variant_data.fields();
+        if fields.is_empty() {
             return CONTINUE_OPAQUELY_INHABITED;
         }
 
         let is_enum = matches!(variant, VariantId::EnumVariantId(..));
         let field_tys = self.db.field_types(variant);
-        let field_vis = self.db.field_visibilities(variant);
+        let field_vis = if is_enum { None } else { Some(self.db.field_visibilities(variant)) };
 
-        for (fid, _) in variant_data.fields().iter() {
-            self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?;
+        for (fid, _) in fields.iter() {
+            self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?;
         }
         CONTINUE_OPAQUELY_INHABITED
     }
 
     fn visit_field(
         &mut self,
-        vis: Visibility,
+        vis: Option<Visibility>,
         ty: &Binders<Ty>,
         subst: &Substitution,
-        is_enum: bool,
     ) -> ControlFlow<VisiblyUninhabited> {
-        if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) {
+        if vis.map_or(true, |it| it.is_visible_from(self.db.upcast(), self.target_mod)) {
             let ty = ty.clone().substitute(Interner, subst);
             ty.visit_with(self, DebruijnIndex::INNERMOST)
         } else {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index ba64f5c..1727cec 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -56,7 +56,6 @@
 use chalk_ir::{
     fold::{Shift, TypeFoldable},
     interner::HasInterner,
-    visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
     NoSolution,
 };
 use either::Either;
@@ -98,7 +97,9 @@
 pub use utils::{all_super_traits, is_fn_unsafe_to_call};
 
 pub use chalk_ir::{
-    cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
+    cast::Cast,
+    visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
+    AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
 };
 
 pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
@@ -288,7 +289,7 @@
 
 /// Return an index of a parameter in the generic type parameter list by it's id.
 pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
-    generics(db.upcast(), id.parent).param_idx(id)
+    generics(db.upcast(), id.parent).type_or_const_param_idx(id)
 }
 
 pub(crate) fn wrap_empty_binders<T>(value: T) -> Binders<T>
@@ -603,14 +604,14 @@
 }
 impl_intern_value_trivial!(ImplTraitId);
 
-#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+#[derive(PartialEq, Eq, Debug, Hash)]
 pub struct ImplTraits {
     pub(crate) impl_traits: Arena<ImplTrait>,
 }
 
 has_interner!(ImplTraits);
 
-#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+#[derive(PartialEq, Eq, Debug, Hash)]
 pub struct ImplTrait {
     pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>,
 }
@@ -622,7 +623,7 @@
 }
 
 pub fn error_lifetime() -> Lifetime {
-    static_lifetime()
+    LifetimeData::Error.intern(Interner)
 }
 
 pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
@@ -861,7 +862,7 @@
             if cfg!(debug_assertions) {
                 Err(NoSolution)
             } else {
-                Ok(static_lifetime())
+                Ok(error_lifetime())
             }
         }
 
@@ -873,7 +874,7 @@
             if cfg!(debug_assertions) {
                 Err(NoSolution)
             } else {
-                Ok(static_lifetime())
+                Ok(error_lifetime())
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 25ccc84..4d0516e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -15,7 +15,10 @@
     CrateId,
 };
 use chalk_ir::{
-    cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety,
+    cast::Cast,
+    fold::{Shift, TypeFoldable},
+    interner::HasInterner,
+    Mutability, Safety, TypeOutlives,
 };
 
 use either::Either;
@@ -59,14 +62,14 @@
     mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
     static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
     utils::{
-        all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics,
-        InTypeConstIdMetadata,
+        self, all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
+        Generics, InTypeConstIdMetadata,
     },
     AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
     FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
-    LifetimeData, ParamKind, PolyFnSig, ProjectionTy, QuantifiedWhereClause,
-    QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
-    TyKind, WhereClause,
+    LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, ProjectionTy,
+    QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef,
+    TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
 };
 
 #[derive(Debug)]
@@ -242,13 +245,8 @@
         )
     }
 
-    fn generics(&self) -> Generics {
-        generics(
-            self.db.upcast(),
-            self.resolver
-                .generic_def()
-                .expect("there should be generics if there's a generic param"),
-        )
+    fn generics(&self) -> Option<Generics> {
+        Some(generics(self.db.upcast(), self.resolver.generic_def()?))
     }
 
     pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
@@ -282,7 +280,7 @@
                 let inner_ty = self.lower_ty(inner);
                 // FIXME: It should infer the eldided lifetimes instead of stubbing with static
                 let lifetime =
-                    lifetime.as_ref().map_or_else(static_lifetime, |lr| self.lower_lifetime(lr));
+                    lifetime.as_ref().map_or_else(error_lifetime, |lr| self.lower_lifetime(lr));
                 TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty)
                     .intern(Interner)
             }
@@ -318,7 +316,7 @@
                         // place even if we encounter more opaque types while
                         // lowering the bounds
                         let idx = opaque_type_data.borrow_mut().alloc(ImplTrait {
-                            bounds: crate::make_single_type_binders(Vec::new()),
+                            bounds: crate::make_single_type_binders(Vec::default()),
                         });
                         // We don't want to lower the bounds inside the binders
                         // we're currently in, because they don't end up inside
@@ -349,8 +347,7 @@
                         let idx = counter.get();
                         // FIXME we're probably doing something wrong here
                         counter.set(idx + count_impl_traits(type_ref) as u16);
-                        if let Some(def) = self.resolver.generic_def() {
-                            let generics = generics(self.db.upcast(), def);
+                        if let Some(generics) = self.generics() {
                             let param = generics
                                 .iter()
                                 .filter(|(_, data)| {
@@ -385,8 +382,7 @@
                             const_params,
                             _impl_trait_params,
                             _lifetime_params,
-                        ) = if let Some(def) = self.resolver.generic_def() {
-                            let generics = generics(self.db.upcast(), def);
+                        ) = if let Some(generics) = self.generics() {
                             generics.provenance_split()
                         } else {
                             (0, 0, 0, 0, 0, 0)
@@ -574,44 +570,40 @@
                 // FIXME(trait_alias): Implement trait alias.
                 return (TyKind::Error.intern(Interner), None);
             }
-            TypeNs::GenericParam(param_id) => {
-                let generics = generics(
-                    self.db.upcast(),
-                    self.resolver.generic_def().expect("generics in scope"),
-                );
-                match self.type_param_mode {
-                    ParamLoweringMode::Placeholder => {
-                        TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
-                    }
-                    ParamLoweringMode::Variable => {
-                        let idx = match generics.param_idx(param_id.into()) {
-                            None => {
-                                never!("no matching generics");
-                                return (TyKind::Error.intern(Interner), None);
-                            }
-                            Some(idx) => idx,
-                        };
-
-                        TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
-                    }
+            TypeNs::GenericParam(param_id) => match self.type_param_mode {
+                ParamLoweringMode::Placeholder => {
+                    TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
                 }
-                .intern(Interner)
+                ParamLoweringMode::Variable => {
+                    let idx = match self
+                        .generics()
+                        .expect("generics in scope")
+                        .type_or_const_param_idx(param_id.into())
+                    {
+                        None => {
+                            never!("no matching generics");
+                            return (TyKind::Error.intern(Interner), None);
+                        }
+                        Some(idx) => idx,
+                    };
+
+                    TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
+                }
             }
+            .intern(Interner),
             TypeNs::SelfType(impl_id) => {
-                let def =
-                    self.resolver.generic_def().expect("impl should have generic param scope");
-                let generics = generics(self.db.upcast(), def);
+                let generics = self.generics().expect("impl should have generic param scope");
 
                 match self.type_param_mode {
                     ParamLoweringMode::Placeholder => {
                         // `def` can be either impl itself or item within, and we need impl itself
                         // now.
-                        let generics = generics.parent_generics().unwrap_or(&generics);
+                        let generics = generics.parent_or_self();
                         let subst = generics.placeholder_subst(self.db);
                         self.db.impl_self_ty(impl_id).substitute(Interner, &subst)
                     }
                     ParamLoweringMode::Variable => {
-                        let starting_from = match def {
+                        let starting_from = match generics.def() {
                             GenericDefId::ImplId(_) => 0,
                             // `def` is an item within impl. We need to substitute `BoundVar`s but
                             // remember that they are for parent (i.e. impl) generic params so they
@@ -679,12 +671,12 @@
     }
 
     fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
-        let Some((def, res)) = self.resolver.generic_def().zip(res) else {
+        let Some((generics, res)) = self.generics().zip(res) else {
             return TyKind::Error.intern(Interner);
         };
         let ty = named_associated_type_shorthand_candidates(
             self.db,
-            def,
+            generics.def(),
             res,
             Some(segment.name.clone()),
             move |name, t, associated_ty| {
@@ -696,7 +688,6 @@
                 let parent_subst = match self.type_param_mode {
                     ParamLoweringMode::Placeholder => {
                         // if we're lowering to placeholders, we have to put them in now.
-                        let generics = generics(self.db.upcast(), def);
                         let s = generics.placeholder_subst(self.db);
                         s.apply(parent_subst, Interner)
                     }
@@ -718,7 +709,7 @@
                     None,
                 );
 
-                let len_self = generics(self.db.upcast(), associated_ty.into()).len_self();
+                let len_self = utils::generics(self.db.upcast(), associated_ty.into()).len_self();
 
                 let substs = Substitution::from_iter(
                     Interner,
@@ -1016,40 +1007,43 @@
         self.substs_from_path_segment(segment, Some(resolved.into()), false, explicit_self_ty)
     }
 
-    pub(crate) fn lower_where_predicate(
-        &self,
-        where_predicate: &WherePredicate,
+    pub(crate) fn lower_where_predicate<'b>(
+        &'b self,
+        where_predicate: &'b WherePredicate,
         ignore_bindings: bool,
-    ) -> impl Iterator<Item = QuantifiedWhereClause> {
+    ) -> impl Iterator<Item = QuantifiedWhereClause> + 'b {
         match where_predicate {
             WherePredicate::ForLifetime { target, bound, .. }
             | WherePredicate::TypeBound { target, bound } => {
                 let self_ty = match target {
                     WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(type_ref),
-                    WherePredicateTypeTarget::TypeOrConstParam(param_id) => {
-                        let generic_def = self.resolver.generic_def().expect("generics in scope");
-                        let generics = generics(self.db.upcast(), generic_def);
-                        let param_id = hir_def::TypeOrConstParamId {
-                            parent: generic_def,
-                            local_id: *param_id,
-                        };
-                        let placeholder = to_placeholder_idx(self.db, param_id);
+                    &WherePredicateTypeTarget::TypeOrConstParam(local_id) => {
+                        let def = self.resolver.generic_def().expect("generics in scope");
+                        let param_id = hir_def::TypeOrConstParamId { parent: def, local_id };
                         match self.type_param_mode {
-                            ParamLoweringMode::Placeholder => TyKind::Placeholder(placeholder),
+                            ParamLoweringMode::Placeholder => {
+                                TyKind::Placeholder(to_placeholder_idx(self.db, param_id))
+                            }
                             ParamLoweringMode::Variable => {
-                                let idx = generics.param_idx(param_id).expect("matching generics");
+                                let idx = generics(self.db.upcast(), def)
+                                    .type_or_const_param_idx(param_id)
+                                    .expect("matching generics");
                                 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx))
                             }
                         }
                         .intern(Interner)
                     }
                 };
-                self.lower_type_bound(bound, self_ty, ignore_bindings)
-                    .collect::<Vec<_>>()
-                    .into_iter()
+                Either::Left(self.lower_type_bound(bound, self_ty, ignore_bindings))
             }
-            WherePredicate::Lifetime { .. } => vec![].into_iter(),
+            WherePredicate::Lifetime { bound, target } => Either::Right(iter::once(
+                crate::wrap_empty_binders(WhereClause::LifetimeOutlives(LifetimeOutlives {
+                    a: self.lower_lifetime(bound),
+                    b: self.lower_lifetime(target),
+                })),
+            )),
         }
+        .into_iter()
     }
 
     pub(crate) fn lower_type_bound(
@@ -1058,11 +1052,11 @@
         self_ty: Ty,
         ignore_bindings: bool,
     ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
-        let mut bindings = None;
-        let trait_ref = match bound.as_ref() {
+        let mut trait_ref = None;
+        let clause = match bound.as_ref() {
             TypeBound::Path(path, TraitBoundModifier::None) => {
-                bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
-                bindings
+                trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty));
+                trait_ref
                     .clone()
                     .filter(|tr| {
                         // ignore `T: Drop` or `T: Destruct` bounds.
@@ -1098,14 +1092,20 @@
             }
             TypeBound::ForLifetime(_, path) => {
                 // FIXME Don't silently drop the hrtb lifetimes here
-                bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
-                bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
+                trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty));
+                trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
             }
-            TypeBound::Lifetime(_) => None,
+            TypeBound::Lifetime(l) => {
+                let lifetime = self.lower_lifetime(l);
+                Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives {
+                    ty: self_ty,
+                    lifetime,
+                })))
+            }
             TypeBound::Error => None,
         };
-        trait_ref.into_iter().chain(
-            bindings
+        clause.into_iter().chain(
+            trait_ref
                 .into_iter()
                 .filter(move |_| !ignore_bindings)
                 .flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)),
@@ -1203,8 +1203,8 @@
                                     });
                                 if let Some(target_param_idx) = target_param_idx {
                                     let mut counter = 0;
-                                    for (idx, data) in self.generics().params.type_or_consts.iter()
-                                    {
+                                    let generics = self.generics().expect("generics in scope");
+                                    for (idx, data) in generics.params.type_or_consts.iter() {
                                         // Count the number of `impl Trait` things that appear before
                                         // the target of our `bound`.
                                         // Our counter within `impl_trait_mode` should be that number
@@ -1264,10 +1264,19 @@
         // bounds in the input.
         // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound.
         // These invariants are utilized by `TyExt::dyn_trait()` and chalk.
+        let mut lifetime = None;
         let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
             let mut bounds: Vec<_> = bounds
                 .iter()
                 .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
+                .filter(|b| match b.skip_binders() {
+                    WhereClause::Implemented(_) | WhereClause::AliasEq(_) => true,
+                    WhereClause::LifetimeOutlives(_) => false,
+                    WhereClause::TypeOutlives(t) => {
+                        lifetime = Some(t.lifetime.clone());
+                        false
+                    }
+                })
                 .collect();
 
             let mut multiple_regular_traits = false;
@@ -1305,7 +1314,7 @@
                             _ => unreachable!(),
                         }
                     }
-                    // We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet.
+                    // `WhereClause::{TypeOutlives, LifetimeOutlives}` have been filtered out
                     _ => unreachable!(),
                 }
             });
@@ -1325,7 +1334,21 @@
 
         if let Some(bounds) = bounds {
             let bounds = crate::make_single_type_binders(bounds);
-            TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
+            TyKind::Dyn(DynTy {
+                bounds,
+                lifetime: match lifetime {
+                    Some(it) => match it.bound_var(Interner) {
+                        Some(bound_var) => LifetimeData::BoundVar(BoundVar::new(
+                            DebruijnIndex::INNERMOST,
+                            bound_var.index,
+                        ))
+                        .intern(Interner),
+                        None => it,
+                    },
+                    None => static_lifetime(),
+                },
+            })
+            .intern(Interner)
         } else {
             // FIXME: report error
             // (additional non-auto traits, associated type rebound, or no resolved trait)
@@ -1355,8 +1378,8 @@
                     crate::wrap_empty_binders(clause)
                 });
                 predicates.extend(sized_clause);
-                predicates.shrink_to_fit();
             }
+            predicates.shrink_to_fit();
             predicates
         });
         ImplTrait { bounds: crate::make_single_type_binders(predicates) }
@@ -1371,10 +1394,7 @@
                         LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id))
                     }
                     ParamLoweringMode::Variable => {
-                        let generics = generics(
-                            self.db.upcast(),
-                            self.resolver.generic_def().expect("generics in scope"),
-                        );
+                        let generics = self.generics().expect("generics in scope");
                         let idx = match generics.lifetime_idx(id) {
                             None => return error_lifetime(),
                             Some(idx) => idx,
@@ -1485,7 +1505,7 @@
             // Handle `Self::Type` referring to own associated type in trait definitions
             if let GenericDefId::TraitId(trait_id) = param_id.parent() {
                 let trait_generics = generics(db.upcast(), trait_id.into());
-                if trait_generics.params.type_or_consts[param_id.local_id()].is_trait_self() {
+                if trait_generics.params[param_id.local_id()].is_trait_self() {
                     let def_generics = generics(db.upcast(), def);
                     let starting_idx = match def {
                         GenericDefId::TraitId(_) => 0,
@@ -1604,10 +1624,14 @@
 
     let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     let explicitly_unsized_tys = ctx.unsized_types.into_inner();
-    let implicitly_sized_predicates =
+    if let Some(implicitly_sized_predicates) =
         implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver)
-            .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p)));
-    predicates.extend(implicitly_sized_predicates);
+    {
+        predicates.extend(
+            implicitly_sized_predicates
+                .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))),
+        );
+    }
     predicates.into()
 }
 
@@ -1657,18 +1681,7 @@
         }
     }
 
-    let container: Option<ItemContainerId> = match def {
-        // FIXME: is there a function for this?
-        GenericDefId::FunctionId(f) => Some(f.lookup(db.upcast()).container),
-        GenericDefId::AdtId(_) => None,
-        GenericDefId::TraitId(_) => None,
-        GenericDefId::TraitAliasId(_) => None,
-        GenericDefId::TypeAliasId(t) => Some(t.lookup(db.upcast()).container),
-        GenericDefId::ImplId(_) => None,
-        GenericDefId::EnumVariantId(_) => None,
-        GenericDefId::ConstId(c) => Some(c.lookup(db.upcast()).container),
-    };
-    if let Some(ItemContainerId::TraitId(trait_id)) = container {
+    if let Some(trait_id) = def.assoc_trait_container(db.upcast()) {
         // add `Self: Trait<T1, T2, ...>` to the environment in trait
         // function default implementations (and speculative code
         // inside consts or type aliases)
@@ -1676,24 +1689,23 @@
         let substs = TyBuilder::placeholder_subst(db, trait_id);
         let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs };
         let pred = WhereClause::Implemented(trait_ref);
-        let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
-        clauses.push(program_clause.into_from_env_clause(Interner));
+        clauses.push(pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner));
     }
 
     let subst = generics(db.upcast(), def).placeholder_subst(db);
     let explicitly_unsized_tys = ctx.unsized_types.into_inner();
-    let implicitly_sized_clauses =
-        implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver).map(|pred| {
-            let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
-            program_clause.into_from_env_clause(Interner)
-        });
-    clauses.extend(implicitly_sized_clauses);
-
-    let krate = def.module(db.upcast()).krate();
+    if let Some(implicitly_sized_clauses) =
+        implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
+    {
+        clauses.extend(
+            implicitly_sized_clauses
+                .map(|pred| pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner)),
+        );
+    }
 
     let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
 
-    TraitEnvironment::new(krate, None, traits_in_scope.into_boxed_slice(), env)
+    TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env)
 }
 
 /// Resolve the where clause(s) of an item with generics.
@@ -1721,10 +1733,14 @@
 
     let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     let explicitly_unsized_tys = ctx.unsized_types.into_inner();
-    let implicitly_sized_predicates =
+    if let Some(implicitly_sized_predicates) =
         implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
-            .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p)));
-    predicates.extend(implicitly_sized_predicates);
+    {
+        predicates.extend(
+            implicitly_sized_predicates
+                .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))),
+        );
+    }
     predicates.into()
 }
 
@@ -1736,24 +1752,24 @@
     explicitly_unsized_tys: &'a FxHashSet<Ty>,
     substitution: &'a Substitution,
     resolver: &Resolver,
-) -> impl Iterator<Item = WhereClause> + 'a {
+) -> Option<impl Iterator<Item = WhereClause> + 'a> {
     let is_trait_def = matches!(def, GenericDefId::TraitId(..));
     let generic_args = &substitution.as_slice(Interner)[is_trait_def as usize..];
     let sized_trait = db
         .lang_item(resolver.krate(), LangItem::Sized)
         .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
 
-    sized_trait.into_iter().flat_map(move |sized_trait| {
-        let implicitly_sized_tys = generic_args
+    sized_trait.map(move |sized_trait| {
+        generic_args
             .iter()
             .filter_map(|generic_arg| generic_arg.ty(Interner))
-            .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty));
-        implicitly_sized_tys.map(move |self_ty| {
-            WhereClause::Implemented(TraitRef {
-                trait_id: sized_trait,
-                substitution: Substitution::from1(Interner, self_ty.clone()),
+            .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty))
+            .map(move |self_ty| {
+                WhereClause::Implemented(TraitRef {
+                    trait_id: sized_trait,
+                    substitution: Substitution::from1(Interner, self_ty.clone()),
+                })
             })
-        })
     })
 }
 
@@ -1796,8 +1812,7 @@
                 make_binders(db, &generic_params, val)
             }
             GenericParamDataRef::LifetimeParamData(_) => {
-                // using static because it requires defaults
-                make_binders(db, &generic_params, static_lifetime().cast(Interner))
+                make_binders(db, &generic_params, error_lifetime().cast(Interner))
             }
         }
     }));
@@ -1817,7 +1832,7 @@
         let val = match id {
             GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
             GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
-            GenericParamId::LifetimeParamId(_) => static_lifetime().cast(Interner),
+            GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
         };
         crate::make_binders(db, &generic_params, val)
     }));
@@ -2232,7 +2247,7 @@
     expected_ty: Ty,
     value: &ConstRef,
     mode: ParamLoweringMode,
-    args: impl FnOnce() -> Generics,
+    args: impl FnOnce() -> Option<Generics>,
     debruijn: DebruijnIndex,
 ) -> Const {
     match value {
@@ -2251,7 +2266,7 @@
             .unwrap_or_else(|| unknown_const(expected_ty))
         }
         &ConstRef::Complex(it) => {
-            let crate_data = &db.crate_graph()[owner.module(db.upcast()).krate()];
+            let crate_data = &db.crate_graph()[resolver.krate()];
             if crate_data.env.get("__ra_is_test_fixture").is_none() && crate_data.origin.is_local()
             {
                 // FIXME: current `InTypeConstId` is very unstable, so we only use it in non local crate
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 73b07df..cd72349 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -22,10 +22,10 @@
 use crate::{
     autoderef::{self, AutoderefKind},
     db::HirDatabase,
-    from_chalk_trait_id, from_foreign_def_id,
+    error_lifetime, from_chalk_trait_id, from_foreign_def_id,
     infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
     primitive::{FloatTy, IntTy, UintTy},
-    static_lifetime, to_chalk_trait_id,
+    to_chalk_trait_id,
     utils::all_super_traits,
     AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance,
     InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef,
@@ -1035,7 +1035,7 @@
     iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?;
 
     let refed = Canonical {
-        value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
+        value: TyKind::Ref(Mutability::Not, error_lifetime(), receiver_ty.value.clone())
             .intern(Interner),
         binders: receiver_ty.binders.clone(),
     };
@@ -1043,7 +1043,7 @@
     iterate_method_candidates_by_receiver(refed, first_adjustment.with_autoref(Mutability::Not))?;
 
     let ref_muted = Canonical {
-        value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone())
+        value: TyKind::Ref(Mutability::Mut, error_lifetime(), receiver_ty.value.clone())
             .intern(Interner),
         binders: receiver_ty.binders,
     };
@@ -1369,6 +1369,7 @@
     None
 }
 
+// FIXME: Replace this with a `Try` impl once stable
 macro_rules! check_that {
     ($cond:expr) => {
         if !$cond {
@@ -1377,6 +1378,7 @@
     };
 }
 
+#[derive(Debug)]
 enum IsValidCandidate {
     Yes,
     No,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
index d4d6691..fee3dd3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -9,11 +9,14 @@
     resolver::HasResolver,
 };
 
-use crate::mir::eval::{
-    name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId,
-    HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy,
-    IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan,
-    Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
+use crate::{
+    error_lifetime,
+    mir::eval::{
+        name, pad16, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule,
+        HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned,
+        ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability,
+        Result, Substitution, Ty, TyBuilder, TyExt,
+    },
 };
 
 mod simd;
@@ -247,7 +250,7 @@
             let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
             let arg = IntervalAndTy {
                 interval: Interval { addr: tmp, size: self.ptr_size() },
-                ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner),
+                ty: TyKind::Ref(Mutability::Not, error_lifetime(), ty.clone()).intern(Interner),
             };
             let offset = layout.fields.offset(i).bytes_usize();
             self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 7e582c0..151f65c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -27,6 +27,7 @@
     consteval::ConstEvalError,
     db::{HirDatabase, InternedClosure},
     display::HirDisplay,
+    error_lifetime,
     infer::{CaptureKind, CapturedItem, TypeMismatch},
     inhabitedness::is_ty_uninhabited_from,
     layout::LayoutError,
@@ -90,7 +91,7 @@
     UnresolvedField,
     UnsizedTemporary(Ty),
     MissingFunctionDefinition(DefWithBodyId, ExprId),
-    TypeMismatch(TypeMismatch),
+    TypeMismatch(Option<TypeMismatch>),
     /// This should never happen. Type mismatch should catch everything.
     TypeError(&'static str),
     NotSupported(String),
@@ -170,14 +171,15 @@
                     body.pretty_print_expr(db.upcast(), *owner, *it)
                 )?;
             }
-            MirLowerError::TypeMismatch(e) => {
-                writeln!(
+            MirLowerError::TypeMismatch(e) => match e {
+                Some(e) => writeln!(
                     f,
                     "Type mismatch: Expected {}, found {}",
                     e.expected.display(db),
                     e.actual.display(db),
-                )?;
-            }
+                )?,
+                None => writeln!(f, "Type mismatch: types mismatch with {{unknown}}",)?,
+            },
             MirLowerError::GenericArgNotProvided(id, subst) => {
                 let parent = id.parent;
                 let param = &db.generic_params(parent).type_or_consts[id.local_id];
@@ -493,9 +495,11 @@
                                     ty,
                                     value: chalk_ir::ConstValue::BoundVar(BoundVar::new(
                                         DebruijnIndex::INNERMOST,
-                                        gen.param_idx(p.into()).ok_or(MirLowerError::TypeError(
-                                            "fail to lower const generic param",
-                                        ))?,
+                                        gen.type_or_const_param_idx(p.into()).ok_or(
+                                            MirLowerError::TypeError(
+                                                "fail to lower const generic param",
+                                            ),
+                                        )?,
                                     )),
                                 }
                                 .intern(Interner),
@@ -1702,7 +1706,7 @@
     }
 
     fn is_uninhabited(&self, expr_id: ExprId) -> bool {
-        is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
+        is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db.upcast()))
     }
 
     /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
@@ -2032,10 +2036,12 @@
     let closure_local = ctx.result.locals.alloc(Local {
         ty: match kind {
             FnTrait::FnOnce => infer[expr].clone(),
-            FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone())
-                .intern(Interner),
-            FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone())
-                .intern(Interner),
+            FnTrait::FnMut => {
+                TyKind::Ref(Mutability::Mut, error_lifetime(), infer[expr].clone()).intern(Interner)
+            }
+            FnTrait::Fn => {
+                TyKind::Ref(Mutability::Not, error_lifetime(), infer[expr].clone()).intern(Interner)
+            }
         },
     });
     ctx.result.param_locals.push(closure_local);
@@ -2152,8 +2158,10 @@
     // need to take this input explicitly.
     root_expr: ExprId,
 ) -> Result<MirBody> {
-    if let Some((_, it)) = infer.type_mismatches().next() {
-        return Err(MirLowerError::TypeMismatch(it.clone()));
+    if infer.has_errors {
+        return Err(MirLowerError::TypeMismatch(
+            infer.type_mismatches().next().map(|(_, it)| it.clone()),
+        ));
     }
     let mut ctx = MirLowerCtx::new(db, owner, body, infer);
     // 0 is return local
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
index be81915..4ad0090 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
@@ -290,7 +290,7 @@
             Some((_, _, mutability)) => mutability,
             None => Mutability::Not,
         };
-        let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner);
+        let result_ref = TyKind::Ref(mutability, error_lifetime(), result_ty).intern(Interner);
         let mut result: Place = self.temp(result_ref, current, span)?.into();
         let index_fn_op = Operand::const_zst(
             TyKind::FnDef(
@@ -333,8 +333,8 @@
                 BorrowKind::Mut { kind: MutBorrowKind::Default },
             )
         };
-        let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner);
-        let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner);
+        let ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), source_ty.clone()).intern(Interner);
+        let target_ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), target_ty).intern(Interner);
         let ref_place: Place = self.temp(ty_ref, current, span)?.into();
         self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span);
         let deref_trait = self
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
index d6557c3..a384c93 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
@@ -101,7 +101,7 @@
         _outer_binder: DebruijnIndex,
     ) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> {
         let it = from_placeholder_idx(self.db, idx);
-        let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else {
+        let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else {
             not_supported!("missing idx in generics");
         };
         Ok(self
@@ -119,7 +119,7 @@
         _outer_binder: DebruijnIndex,
     ) -> std::result::Result<Ty, Self::Error> {
         let it = from_placeholder_idx(self.db, idx);
-        let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else {
+        let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else {
             not_supported!("missing idx in generics");
         };
         Ok(self
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
index 80f92ea..119de7f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs
@@ -136,3 +136,20 @@
 "#,
     );
 }
+
+#[test]
+fn no_mismatches_with_unresolved_projections() {
+    check_no_mismatches(
+        r#"
+// `Thing` is `{unknown}`
+fn create() -> Option<(i32, Thing)> {
+    Some((69420, Thing))
+}
+
+fn consume() -> Option<()> {
+    let (number, thing) = create()?;
+    Some(())
+}
+"#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
index 5069267..e8369ca 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
@@ -85,7 +85,7 @@
 trait Foo<'a> {}
 
 fn foo(foo: &dyn for<'a> Foo<'a>) {}
-    // ^^^ &dyn Foo<'static>
+    // ^^^ &dyn Foo<'_>
 "#,
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 80d5a0a..4355881 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -1109,7 +1109,7 @@
 #[lang = "va_list"]
 pub struct VaListImpl<'f>;
 fn my_fn(foo: ...) {}
-       //^^^ VaListImpl<'static>
+       //^^^ VaListImpl<'{error}>
 "#,
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index 8565b60..c2d2047 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -896,13 +896,13 @@
 "#,
         expect![[r#"
             123..127 'self': &Mutex<T>
-            150..152 '{}': MutexGuard<'static, T>
+            150..152 '{}': MutexGuard<'{error}, T>
             234..238 'self': &{unknown}
             240..290 '{     ...()); }': ()
             250..251 'w': &Mutex<BufWriter>
             276..287 '*(w.lock())': BufWriter
             278..279 'w': &Mutex<BufWriter>
-            278..286 'w.lock()': MutexGuard<'static, BufWriter>
+            278..286 'w.lock()': MutexGuard<'{error}, BufWriter>
         "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index f394045..a9d28eb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3092,7 +3092,7 @@
             389..394 'boxed': Box<Foo<i32>>
             389..406 'boxed....nner()': &i32
             416..421 'good1': &i32
-            424..438 'Foo::get_inner': fn get_inner<i32, 'static>(&Box<Foo<i32>>) -> &i32
+            424..438 'Foo::get_inner': fn get_inner<i32, '{error}>(&Box<Foo<i32>>) -> &i32
             424..446 'Foo::g...boxed)': &i32
             439..445 '&boxed': &Box<Foo<i32>>
             440..445 'boxed': Box<Foo<i32>>
@@ -3100,7 +3100,7 @@
             464..469 'boxed': Box<Foo<i32>>
             464..480 'boxed....self()': &Foo<i32>
             490..495 'good2': &Foo<i32>
-            498..511 'Foo::get_self': fn get_self<i32, 'static>(&Box<Foo<i32>>) -> &Foo<i32>
+            498..511 'Foo::get_self': fn get_self<i32, '{error}>(&Box<Foo<i32>>) -> &Foo<i32>
             498..519 'Foo::g...boxed)': &Foo<i32>
             512..518 '&boxed': &Box<Foo<i32>>
             513..518 'boxed': Box<Foo<i32>>
@@ -3659,7 +3659,7 @@
     let are = "are";
     let count = 10;
     builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'static>
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'{error}>
 }
 "#,
     );
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 759af18..dfcd322a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -1602,7 +1602,7 @@
         r#"
 //- minicore: sized
 trait Trait {}
-fn test(
+fn test<'lifetime>(
     a: impl Trait + 'lifetime,
     b: impl 'lifetime,
     c: impl (Trait),
@@ -1612,13 +1612,13 @@
 ) {}
 "#,
         expect![[r#"
-            28..29 'a': impl Trait
-            59..60 'b': impl Sized
-            82..83 'c': impl Trait
-            103..104 'd': impl Sized
-            128..129 'e': impl ?Sized
-            148..149 'f': impl Trait + ?Sized
-            173..175 '{}': ()
+            39..40 'a': impl Trait + 'lifetime
+            70..71 'b': impl 'lifetime
+            93..94 'c': impl Trait
+            114..115 'd': impl 'lifetime
+            139..140 'e': impl ?Sized
+            159..160 'f': impl Trait + ?Sized
+            184..186 '{}': ()
         "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index afd4d1f..42c7a84 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -262,7 +262,7 @@
     }
 }
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub(crate) struct Generics {
     def: GenericDefId,
     pub(crate) params: Interned<GenericParams>,
@@ -274,6 +274,10 @@
         self.iter().map(|(id, _)| id)
     }
 
+    pub(crate) fn def(&self) -> GenericDefId {
+        self.def
+    }
+
     /// Iterator over types and const params of self, then parent.
     pub(crate) fn iter<'a>(
         &'a self,
@@ -304,7 +308,11 @@
         };
 
         let lt_iter = self.params.iter_lt().map(from_lt_id(self));
-        self.params.iter().map(from_toc_id(self)).chain(lt_iter).chain(self.iter_parent())
+        self.params
+            .iter_type_or_consts()
+            .map(from_toc_id(self))
+            .chain(lt_iter)
+            .chain(self.iter_parent())
     }
 
     /// Iterate over types and const params without parent params.
@@ -336,16 +344,19 @@
             }
         };
 
-        self.params.iter().map(from_toc_id(self)).chain(self.params.iter_lt().map(from_lt_id(self)))
+        self.params
+            .iter_type_or_consts()
+            .map(from_toc_id(self))
+            .chain(self.params.iter_lt().map(from_lt_id(self)))
     }
 
     /// Iterator over types and const params of parent.
-    #[allow(clippy::needless_lifetimes)]
-    pub(crate) fn iter_parent<'a>(
-        &'a self,
-    ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
+    pub(crate) fn iter_parent(
+        &self,
+    ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
         self.parent_generics().into_iter().flat_map(|it| {
-            let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| {
+            let from_toc_id = move |(local_id, p)| {
+                let p: &_ = p;
                 let id = TypeOrConstParamId { parent: it.def, local_id };
                 match p {
                     TypeOrConstParamData::TypeParamData(p) => (
@@ -359,14 +370,14 @@
                 }
             };
 
-            let from_lt_id = move |(local_id, p): (_, &'a LifetimeParamData)| {
+            let from_lt_id = move |(local_id, p): (_, _)| {
                 (
                     GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
                     GenericParamDataRef::LifetimeParamData(p),
                 )
             };
             let lt_iter = it.params.iter_lt().map(from_lt_id);
-            it.params.iter().map(from_toc_id).chain(lt_iter)
+            it.params.iter_type_or_consts().map(from_toc_id).chain(lt_iter)
         })
     }
 
@@ -383,7 +394,7 @@
     }
 
     /// Returns number of generic parameter excluding those from parent
-    fn len_params(&self) -> usize {
+    fn len_type_and_const_params(&self) -> usize {
         self.params.type_or_consts.len()
     }
 
@@ -394,7 +405,7 @@
         let mut impl_trait_params = 0;
         let mut const_params = 0;
         let mut lifetime_params = 0;
-        self.params.iter().for_each(|(_, data)| match data {
+        self.params.iter_type_or_consts().for_each(|(_, data)| match data {
             TypeOrConstParamData::TypeParamData(p) => match p.provenance {
                 TypeParamProvenance::TypeParamList => type_params += 1,
                 TypeParamProvenance::TraitSelf => self_params += 1,
@@ -409,18 +420,23 @@
         (parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params)
     }
 
-    pub(crate) fn param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
-        Some(self.find_param(param)?.0)
+    pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
+        Some(self.find_type_or_const_param(param)?.0)
     }
 
-    fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> {
+    fn find_type_or_const_param(
+        &self,
+        param: TypeOrConstParamId,
+    ) -> Option<(usize, &TypeOrConstParamData)> {
         if param.parent == self.def {
-            let (idx, (_local_id, data)) =
-                self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
-            Some((idx, data))
+            let idx = param.local_id.into_raw().into_u32() as usize;
+            if idx >= self.params.type_or_consts.len() {
+                return None;
+            }
+            Some((idx, &self.params.type_or_consts[param.local_id]))
         } else {
             self.parent_generics()
-                .and_then(|g| g.find_param(param))
+                .and_then(|g| g.find_type_or_const_param(param))
                 // Remember that parent parameters come after parameters for self.
                 .map(|(idx, data)| (self.len_self() + idx, data))
         }
@@ -432,13 +448,14 @@
 
     fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> {
         if lifetime.parent == self.def {
-            let (idx, (_local_id, data)) = self
-                .params
-                .iter_lt()
-                .enumerate()
-                .find(|(_, (idx, _))| *idx == lifetime.local_id)?;
-
-            Some((self.len_params() + idx, data))
+            let idx = lifetime.local_id.into_raw().into_u32() as usize;
+            if idx >= self.params.lifetimes.len() {
+                return None;
+            }
+            Some((
+                self.len_type_and_const_params() + idx,
+                &self.params.lifetimes[lifetime.local_id],
+            ))
         } else {
             self.parent_generics()
                 .and_then(|g| g.find_lifetime(lifetime))
@@ -450,6 +467,10 @@
         self.parent_generics.as_deref()
     }
 
+    pub(crate) fn parent_or_self(&self) -> &Generics {
+        self.parent_generics.as_deref().unwrap_or(self)
+    }
+
     /// Returns a Substitution that replaces each parameter by a bound variable.
     pub(crate) fn bound_vars_subst(
         &self,
diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml
index 1907220..6d7ecd1 100644
--- a/src/tools/rust-analyzer/crates/hir/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml
@@ -33,7 +33,7 @@
 span.workspace = true
 
 [features]
-in-rust-tree = []
+in-rust-tree = ["hir-expand/in-rust-tree"]
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs
index 1d74f9a..cb5f5b0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/db.rs
@@ -4,24 +4,35 @@
 //!
 //! But we need this for at least LRU caching at the query level.
 pub use hir_def::db::{
-    AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery,
-    ConstVisibilityQuery, CrateLangItemsQuery, CrateSupportsNoStdQuery, DefDatabase,
-    DefDatabaseStorage, EnumDataQuery, EnumVariantDataWithDiagnosticsQuery, ExprScopesQuery,
-    ExternCrateDeclDataQuery, FieldVisibilitiesQuery, FieldsAttrsQuery, FieldsAttrsSourceMapQuery,
-    FileItemTreeQuery, FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery,
-    ImplDataWithDiagnosticsQuery, ImportMapQuery, InternAnonymousConstQuery, InternBlockQuery,
-    InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery,
-    InternExternBlockQuery, InternExternCrateQuery, InternFunctionQuery, InternImplQuery,
-    InternInTypeConstQuery, InternMacro2Query, InternMacroRulesQuery, InternProcMacroQuery,
-    InternStaticQuery, InternStructQuery, InternTraitAliasQuery, InternTraitQuery,
-    InternTypeAliasQuery, InternUnionQuery, InternUseQuery, LangItemQuery, Macro2DataQuery,
-    MacroRulesDataQuery, ProcMacroDataQuery, StaticDataQuery, StructDataWithDiagnosticsQuery,
-    TraitAliasDataQuery, TraitDataWithDiagnosticsQuery, TypeAliasDataQuery,
-    UnionDataWithDiagnosticsQuery,
+    AttrsQuery, BlockDefMapQuery, BlockItemTreeQuery, BodyQuery, BodyWithSourceMapQuery,
+    ConstDataQuery, ConstVisibilityQuery, CrateDefMapQuery, CrateLangItemsQuery,
+    CrateNotableTraitsQuery, CrateSupportsNoStdQuery, DefDatabase, DefDatabaseStorage,
+    EnumDataQuery, EnumVariantDataWithDiagnosticsQuery, ExprScopesQuery, ExternCrateDeclDataQuery,
+    FieldVisibilitiesQuery, FieldsAttrsQuery, FieldsAttrsSourceMapQuery, FileItemTreeQuery,
+    FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery, ImplDataWithDiagnosticsQuery,
+    ImportMapQuery, InternAnonymousConstQuery, InternBlockQuery, InternConstQuery, InternDatabase,
+    InternDatabaseStorage, InternEnumQuery, InternExternBlockQuery, InternExternCrateQuery,
+    InternFunctionQuery, InternImplQuery, InternInTypeConstQuery, InternMacro2Query,
+    InternMacroRulesQuery, InternProcMacroQuery, InternStaticQuery, InternStructQuery,
+    InternTraitAliasQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
+    InternUseQuery, LangItemQuery, Macro2DataQuery, MacroRulesDataQuery, ProcMacroDataQuery,
+    StaticDataQuery, StructDataWithDiagnosticsQuery, TraitAliasDataQuery,
+    TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, UnionDataWithDiagnosticsQuery,
 };
 pub use hir_expand::db::{
     AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage,
     ExpandProcMacroQuery, InternMacroCallQuery, InternSyntaxContextQuery, MacroArgQuery,
     ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, ProcMacrosQuery, RealSpanMapQuery,
 };
-pub use hir_ty::db::*;
+pub use hir_ty::db::{
+    AdtDatumQuery, AdtVarianceQuery, AssociatedTyDataQuery, AssociatedTyValueQuery, BorrowckQuery,
+    CallableItemSignatureQuery, ConstEvalDiscriminantQuery, ConstEvalQuery, ConstEvalStaticQuery,
+    ConstParamTyQuery, FieldTypesQuery, FnDefDatumQuery, FnDefVarianceQuery, GenericDefaultsQuery,
+    GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, HirDatabaseStorage,
+    ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, IncoherentInherentImplCratesQuery,
+    InherentImplsInBlockQuery, InherentImplsInCrateQuery, InternCallableDefQuery,
+    InternClosureQuery, InternCoroutineQuery, InternImplTraitIdQuery, InternLifetimeParamIdQuery,
+    InternTypeOrConstParamIdQuery, LayoutOfAdtQuery, MirBodyQuery, ProgramClausesForChalkEnvQuery,
+    ReturnTypeImplTraitsQuery, TargetDataLayoutQuery, TraitDatumQuery, TraitEnvironmentQuery,
+    TraitImplsInBlockQuery, TraitImplsInCrateQuery, TraitImplsInDepsQuery, TyQuery, ValueTyQuery,
+};
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 23c6b07..84f03d1 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -1,4 +1,5 @@
 //! HirDisplay implementations for various hir types.
+use either::Either;
 use hir_def::{
     data::adt::{StructKind, VariantData},
     generics::{
@@ -13,7 +14,7 @@
         write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
         HirFormatter, SizedByDefault,
     },
-    Interner, TraitRefExt, WhereClause,
+    AliasEq, AliasTy, Interner, ProjectionTyExt, TraitRefExt, TyKind, WhereClause,
 };
 
 use crate::{
@@ -363,16 +364,52 @@
 
 impl HirDisplay for TypeParam {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
+        let params = f.db.generic_params(self.id.parent());
+        let param_data = &params.type_or_consts[self.id.local_id()];
+        let substs = TyBuilder::placeholder_subst(f.db, self.id.parent());
+        let krate = self.id.parent().krate(f.db).id;
+        let ty =
+            TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into())).intern(Interner);
+        let predicates = f.db.generic_predicates(self.id.parent());
+        let predicates = predicates
+            .iter()
+            .cloned()
+            .map(|pred| pred.substitute(Interner, &substs))
+            .filter(|wc| match wc.skip_binders() {
+                WhereClause::Implemented(tr) => tr.self_type_parameter(Interner) == ty,
+                WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), ty: _ }) => {
+                    proj.self_type_parameter(f.db) == ty
+                }
+                WhereClause::AliasEq(_) => false,
+                WhereClause::TypeOutlives(to) => to.ty == ty,
+                WhereClause::LifetimeOutlives(_) => false,
+            })
+            .collect::<Vec<_>>();
+
+        match param_data {
+            TypeOrConstParamData::TypeParamData(p) => match p.provenance {
+                TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
+                    write!(f, "{}", p.name.clone().unwrap().display(f.db.upcast()))?
+                }
+                TypeParamProvenance::ArgumentImplTrait => {
+                    return write_bounds_like_dyn_trait_with_prefix(
+                        f,
+                        "impl",
+                        Either::Left(&ty),
+                        &predicates,
+                        SizedByDefault::Sized { anchor: krate },
+                    );
+                }
+            },
+            TypeOrConstParamData::ConstParamData(p) => {
+                write!(f, "{}", p.name.display(f.db.upcast()))?;
+            }
+        }
+
         if f.omit_verbose_types() {
             return Ok(());
         }
 
-        let bounds = f.db.generic_predicates_for_param(self.id.parent(), self.id.into(), None);
-        let substs = TyBuilder::placeholder_subst(f.db, self.id.parent());
-        let predicates: Vec<_> =
-            bounds.iter().cloned().map(|b| b.substitute(Interner, &substs)).collect();
-        let krate = self.id.parent().krate(f.db).id;
         let sized_trait =
             f.db.lang_item(krate, LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
         let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() {
@@ -382,7 +419,16 @@
         let has_only_not_sized_bound = predicates.is_empty();
         if !has_only_sized_bound || has_only_not_sized_bound {
             let default_sized = SizedByDefault::Sized { anchor: krate };
-            write_bounds_like_dyn_trait_with_prefix(f, ":", &predicates, default_sized)?;
+            write_bounds_like_dyn_trait_with_prefix(
+                f,
+                ":",
+                Either::Left(
+                    &hir_ty::TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into()))
+                        .intern(Interner),
+                ),
+                &predicates,
+                default_sized,
+            )?;
         }
         Ok(())
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 106056c..bcd94a6 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -239,7 +239,7 @@
         db: &dyn DefDatabase,
         query: import_map::Query,
     ) -> impl Iterator<Item = Either<ModuleDef, Macro>> {
-        let _p = tracing::span!(tracing::Level::INFO, "query_external_importables");
+        let _p = tracing::span!(tracing::Level::INFO, "query_external_importables").entered();
         import_map::search_dependencies(db, self.into(), &query).into_iter().map(|item| {
             match ItemInNs::from(item) {
                 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id),
@@ -260,11 +260,11 @@
         doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
     }
 
-    pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
+    pub fn cfg(&self, db: &dyn HirDatabase) -> Arc<CfgOptions> {
         db.crate_graph()[self.id].cfg_options.clone()
     }
 
-    pub fn potential_cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
+    pub fn potential_cfg(&self, db: &dyn HirDatabase) -> Arc<CfgOptions> {
         let data = &db.crate_graph()[self.id];
         data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone())
     }
@@ -548,8 +548,8 @@
         acc: &mut Vec<AnyDiagnostic>,
         style_lints: bool,
     ) {
-        let name = self.name(db);
-        let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", ?name);
+        let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", name = ?self.name(db))
+            .entered();
         let def_map = self.id.def_map(db.upcast());
         for diag in def_map.diagnostics() {
             if diag.in_module != self.id.local_id {
@@ -653,7 +653,7 @@
                     GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id })
                 });
                 let type_params = generic_params
-                    .iter()
+                    .iter_type_or_consts()
                     .filter(|(_, it)| it.type_param().is_some())
                     .map(|(local_id, _)| {
                         GenericParamId::TypeParamId(TypeParamId::from_unchecked(
@@ -684,7 +684,7 @@
                 let items = &db.trait_data(trait_.into()).items;
                 let required_items = items.iter().filter(|&(_, assoc)| match *assoc {
                     AssocItemId::FunctionId(it) => !db.function_data(it).has_body(),
-                    AssocItemId::ConstId(id) => Const::from(id).value(db).is_none(),
+                    AssocItemId::ConstId(id) => !db.const_data(id).has_body,
                     AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(),
                 });
                 impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map(
@@ -1418,16 +1418,14 @@
     }
 
     pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
-        if !db.generic_params(self.into()).is_empty() {
-            return Err(LayoutError::HasPlaceholder);
-        }
-        let krate = self.krate(db).id;
         db.layout_of_adt(
             self.into(),
-            Substitution::empty(Interner),
+            TyBuilder::adt(db, self.into())
+                .fill_with_defaults(db, || TyKind::Error.intern(Interner))
+                .build_into_subst(),
             db.trait_environment(self.into()),
         )
-        .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap()))
+        .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).id).unwrap()))
     }
 
     /// Turns this ADT into a type. Any type parameters of the ADT will be
@@ -1630,7 +1628,6 @@
         acc: &mut Vec<AnyDiagnostic>,
         style_lints: bool,
     ) {
-        db.unwind_if_cancelled();
         let krate = self.module(db).id.krate();
 
         let (body, source_map) = db.body_with_source_map(self.into());
@@ -1678,6 +1675,7 @@
         for d in &infer.diagnostics {
             acc.extend(AnyDiagnostic::inference_diagnostic(db, self.into(), d, &source_map));
         }
+
         for (pat_or_expr, mismatch) in infer.type_mismatches() {
             let expr_or_pat = match pat_or_expr {
                 ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),
@@ -1763,7 +1761,9 @@
                         need_mut = &mir::MutabilityReason::Not;
                     }
                     let local = Local { parent: self.into(), binding_id };
-                    match (need_mut, local.is_mut(db)) {
+                    let is_mut = body[binding_id].mode == BindingAnnotation::Mutable;
+
+                    match (need_mut, is_mut) {
                         (mir::MutabilityReason::Unused, _) => {
                             let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_'));
                             if !should_ignore {
@@ -2007,12 +2007,15 @@
 
     /// is this a `fn main` or a function with an `export_name` of `main`?
     pub fn is_main(self, db: &dyn HirDatabase) -> bool {
-        if !self.module(db).is_crate_root() {
-            return false;
-        }
         let data = db.function_data(self.id);
+        data.attrs.export_name() == Some("main")
+            || self.module(db).is_crate_root() && data.name.to_smol_str() == "main"
+    }
 
-        data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main")
+    /// Is this a function with an `export_name` of `main`?
+    pub fn exported_main(self, db: &dyn HirDatabase) -> bool {
+        let data = db.function_data(self.id);
+        data.attrs.export_name() == Some("main")
     }
 
     /// Does this function have the ignore attribute?
@@ -3909,7 +3912,7 @@
         inner.derived(
             TyKind::Ref(
                 if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not },
-                hir_ty::static_lifetime(),
+                hir_ty::error_lifetime(),
                 inner.ty.clone(),
             )
             .intern(Interner),
@@ -4492,7 +4495,7 @@
         name: Option<&Name>,
         mut callback: impl FnMut(Function) -> Option<T>,
     ) -> Option<T> {
-        let _p = tracing::span!(tracing::Level::INFO, "iterate_method_candidates");
+        let _p = tracing::span!(tracing::Level::INFO, "iterate_method_candidates").entered();
         let mut slot = None;
 
         self.iterate_method_candidates_dyn(
@@ -4580,7 +4583,7 @@
         name: Option<&Name>,
         mut callback: impl FnMut(AssocItem) -> Option<T>,
     ) -> Option<T> {
-        let _p = tracing::span!(tracing::Level::INFO, "iterate_path_candidates");
+        let _p = tracing::span!(tracing::Level::INFO, "iterate_path_candidates").entered();
         let mut slot = None;
         self.iterate_path_candidates_dyn(
             db,
@@ -4647,7 +4650,7 @@
         &'a self,
         db: &'a dyn HirDatabase,
     ) -> impl Iterator<Item = Trait> + 'a {
-        let _p = tracing::span!(tracing::Level::INFO, "applicable_inherent_traits");
+        let _p = tracing::span!(tracing::Level::INFO, "applicable_inherent_traits").entered();
         self.autoderef_(db)
             .filter_map(|ty| ty.dyn_trait())
             .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id))
@@ -4655,7 +4658,7 @@
     }
 
     pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Trait> + 'a {
-        let _p = tracing::span!(tracing::Level::INFO, "env_traits");
+        let _p = tracing::span!(tracing::Level::INFO, "env_traits").entered();
         self.autoderef_(db)
             .filter(|ty| matches!(ty.kind(Interner), TyKind::Placeholder(_)))
             .flat_map(|ty| {
@@ -4709,10 +4712,12 @@
                 if let WhereClause::Implemented(trait_ref) = pred.skip_binders() {
                     cb(type_.clone());
                     // skip the self type. it's likely the type we just got the bounds from
-                    for ty in
-                        trait_ref.substitution.iter(Interner).skip(1).filter_map(|a| a.ty(Interner))
-                    {
-                        walk_type(db, &type_.derived(ty.clone()), cb);
+                    if let [self_ty, params @ ..] = trait_ref.substitution.as_slice(Interner) {
+                        for ty in
+                            params.iter().filter(|&ty| ty != self_ty).filter_map(|a| a.ty(Interner))
+                        {
+                            walk_type(db, &type_.derived(ty.clone()), cb);
+                        }
                     }
                 }
             }
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 9796009..e792e15 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -722,7 +722,7 @@
         mut token: SyntaxToken,
         f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>,
     ) {
-        let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros");
+        let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros").entered();
         let (sa, span, file_id) =
             match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) {
                 Some(sa) => match sa.file_id.file_id() {
@@ -1246,6 +1246,17 @@
             .map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..)))
     }
 
+    pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option<u32> {
+        let sa = self.analyze(macro_call.syntax())?;
+        self.db
+            .parse_macro_expansion(
+                sa.expand(self.db, self.wrap_node_infile(macro_call.clone()).as_ref())?,
+            )
+            .value
+            .1
+            .matched_arm
+    }
+
     pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
         let sa = match self.analyze(macro_call.syntax()) {
             Some(it) => it,
@@ -1359,7 +1370,7 @@
         offset: Option<TextSize>,
         infer_body: bool,
     ) -> Option<SourceAnalyzer> {
-        let _p = tracing::span!(tracing::Level::INFO, "Semantics::analyze_impl");
+        let _p = tracing::span!(tracing::Level::INFO, "Semantics::analyze_impl").entered();
         let node = self.find_file(node);
 
         let container = self.with_ctx(|ctx| ctx.find_container(node))?;
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index d4d6f0b..434e4b5 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -118,7 +118,7 @@
 
 impl SourceToDefCtx<'_, '_> {
     pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> {
-        let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def");
+        let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def").entered();
         let mut mods = SmallVec::new();
         for &crate_id in self.db.relevant_crates(file).iter() {
             // FIXME: inner items
@@ -133,7 +133,7 @@
     }
 
     pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
-        let _p = tracing::span!(tracing::Level::INFO, "module_to_def");
+        let _p = tracing::span!(tracing::Level::INFO, "module_to_def").entered();
         let parent_declaration = src
             .syntax()
             .ancestors_with_macros_skip_attr_item(self.db.upcast())
@@ -158,7 +158,7 @@
     }
 
     pub(super) fn source_file_to_def(&self, src: InFile<ast::SourceFile>) -> Option<ModuleId> {
-        let _p = tracing::span!(tracing::Level::INFO, "source_file_to_def");
+        let _p = tracing::span!(tracing::Level::INFO, "source_file_to_def").entered();
         let file_id = src.file_id.original_file(self.db.upcast());
         self.file_to_def(file_id).first().copied()
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index c1b95bb..24a3208 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -105,7 +105,7 @@
     assist_id: &'static str,
     label: &'static str,
 ) -> Option<()> {
-    let _p = tracing::span!(tracing::Level::INFO, "add_missing_impl_members_inner");
+    let _p = tracing::span!(tracing::Level::INFO, "add_missing_impl_members_inner").entered();
     let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
     let impl_ = ctx.sema.to_def(&impl_def)?;
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index 62696d1..b90bccb 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -1588,4 +1588,82 @@
 "#,
         );
     }
+
+    #[test]
+    fn local_inline_import_has_alias() {
+        // FIXME
+        check_assist_not_applicable(
+            auto_import,
+            r#"
+struct S<T>(T);
+use S as IoResult;
+
+mod foo {
+    pub fn bar() -> S$0<()> {}
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn alias_local() {
+        // FIXME
+        check_assist_not_applicable(
+            auto_import,
+            r#"
+struct S<T>(T);
+use S as IoResult;
+
+mod foo {
+    pub fn bar() -> IoResult$0<()> {}
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn preserve_raw_identifiers_strict() {
+        check_assist(
+            auto_import,
+            r"
+            r#as$0
+
+            pub mod ffi_mod {
+                pub fn r#as() {};
+            }
+            ",
+            r"
+            use ffi_mod::r#as;
+
+            r#as
+
+            pub mod ffi_mod {
+                pub fn r#as() {};
+            }
+            ",
+        );
+    }
+
+    #[test]
+    fn preserve_raw_identifiers_reserved() {
+        check_assist(
+            auto_import,
+            r"
+            r#abstract$0
+
+            pub mod ffi_mod {
+                pub fn r#abstract() {};
+            }
+            ",
+            r"
+            use ffi_mod::r#abstract;
+
+            r#abstract
+
+            pub mod ffi_mod {
+                pub fn r#abstract() {};
+            }
+            ",
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
new file mode 100644
index 0000000..5459bd3
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -0,0 +1,250 @@
+use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
+use itertools::Itertools;
+use syntax::{
+    ast::{self, make, AstNode, HasName},
+    ted,
+};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: convert_from_to_tryfrom
+//
+// Converts a From impl to a TryFrom impl, wrapping returns in `Ok`.
+//
+// ```
+// # //- minicore: from
+// impl $0From<usize> for Thing {
+//     fn from(val: usize) -> Self {
+//         Thing {
+//             b: val.to_string(),
+//             a: val
+//         }
+//     }
+// }
+// ```
+// ->
+// ```
+// impl TryFrom<usize> for Thing {
+//     type Error = ${0:()};
+//
+//     fn try_from(val: usize) -> Result<Self, Self::Error> {
+//         Ok(Thing {
+//             b: val.to_string(),
+//             a: val
+//         })
+//     }
+// }
+// ```
+pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let impl_ = ctx.find_node_at_offset::<ast::Impl>()?;
+    let trait_ty = impl_.trait_()?;
+
+    let module = ctx.sema.scope(impl_.syntax())?.module();
+
+    let from_type = match &trait_ty {
+        ast::Type::PathType(path) => {
+            path.path()?.segment()?.generic_arg_list()?.generic_args().next()?
+        }
+        _ => return None,
+    };
+
+    let associated_items = impl_.assoc_item_list()?;
+    let from_fn = associated_items.assoc_items().find_map(|item| {
+        if let ast::AssocItem::Fn(f) = item {
+            if f.name()?.text() == "from" {
+                return Some(f);
+            }
+        };
+        None
+    })?;
+
+    let from_fn_name = from_fn.name()?;
+    let from_fn_return_type = from_fn.ret_type()?.ty()?;
+
+    let return_exprs = from_fn.body()?.syntax().descendants().filter_map(ast::ReturnExpr::cast);
+    let tail_expr = from_fn.body()?.tail_expr()?;
+
+    if resolve_target_trait(&ctx.sema, &impl_)?
+        != FamousDefs(&ctx.sema, module.krate()).core_convert_From()?
+    {
+        return None;
+    }
+
+    acc.add(
+        AssistId("convert_from_to_tryfrom", AssistKind::RefactorRewrite),
+        "Convert From to TryFrom",
+        impl_.syntax().text_range(),
+        |builder| {
+            let trait_ty = builder.make_mut(trait_ty);
+            let from_fn_return_type = builder.make_mut(from_fn_return_type);
+            let from_fn_name = builder.make_mut(from_fn_name);
+            let tail_expr = builder.make_mut(tail_expr);
+            let return_exprs = return_exprs.map(|r| builder.make_mut(r)).collect_vec();
+            let associated_items = builder.make_mut(associated_items).clone();
+
+            ted::replace(
+                trait_ty.syntax(),
+                make::ty(&format!("TryFrom<{from_type}>")).syntax().clone_for_update(),
+            );
+            ted::replace(
+                from_fn_return_type.syntax(),
+                make::ty("Result<Self, Self::Error>").syntax().clone_for_update(),
+            );
+            ted::replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update());
+            ted::replace(
+                tail_expr.syntax(),
+                wrap_ok(tail_expr.clone()).syntax().clone_for_update(),
+            );
+
+            for r in return_exprs {
+                let t = r.expr().unwrap_or_else(make::expr_unit);
+                ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update());
+            }
+
+            let error_type = ast::AssocItem::TypeAlias(make::ty_alias(
+                "Error",
+                None,
+                None,
+                None,
+                Some((make::ty_unit(), None)),
+            ))
+            .clone_for_update();
+
+            if let Some(cap) = ctx.config.snippet_cap {
+                if let ast::AssocItem::TypeAlias(type_alias) = &error_type {
+                    if let Some(ty) = type_alias.ty() {
+                        builder.add_placeholder_snippet(cap, ty);
+                    }
+                }
+            }
+
+            associated_items.add_item_at_start(error_type);
+        },
+    )
+}
+
+fn wrap_ok(expr: ast::Expr) -> ast::Expr {
+    make::expr_call(
+        make::expr_path(make::ext::ident_path("Ok")),
+        make::arg_list(std::iter::once(expr)),
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    #[test]
+    fn converts_from_to_tryfrom() {
+        check_assist(
+            convert_from_to_tryfrom,
+            r#"
+//- minicore: from
+struct Foo(String);
+
+impl $0From<String> for Foo {
+    fn from(val: String) -> Self {
+        if val == "bar" {
+            return Foo(val);
+        }
+        Self(val)
+    }
+}
+            "#,
+            r#"
+struct Foo(String);
+
+impl TryFrom<String> for Foo {
+    type Error = ${0:()};
+
+    fn try_from(val: String) -> Result<Self, Self::Error> {
+        if val == "bar" {
+            return Ok(Foo(val));
+        }
+        Ok(Self(val))
+    }
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn converts_from_to_tryfrom_nested_type() {
+        check_assist(
+            convert_from_to_tryfrom,
+            r#"
+//- minicore: from
+struct Foo(String);
+
+impl $0From<Option<String>> for Foo {
+    fn from(val: Option<String>) -> Self {
+        match val {
+            Some(val) => Foo(val),
+            None => Foo("".to_string())
+        }
+    }
+}
+            "#,
+            r#"
+struct Foo(String);
+
+impl TryFrom<Option<String>> for Foo {
+    type Error = ${0:()};
+
+    fn try_from(val: Option<String>) -> Result<Self, Self::Error> {
+        Ok(match val {
+            Some(val) => Foo(val),
+            None => Foo("".to_string())
+        })
+    }
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn converts_from_to_tryfrom_preserves_lifetimes() {
+        check_assist(
+            convert_from_to_tryfrom,
+            r#"
+//- minicore: from
+struct Foo<'a>(&'a str);
+
+impl<'a> $0From<&'a str> for Foo<'a> {
+    fn from(val: &'a str) -> Self {
+        Self(val)
+    }
+}
+            "#,
+            r#"
+struct Foo<'a>(&'a str);
+
+impl<'a> TryFrom<&'a str> for Foo<'a> {
+    type Error = ${0:()};
+
+    fn try_from(val: &'a str) -> Result<Self, Self::Error> {
+        Ok(Self(val))
+    }
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn other_trait_not_applicable() {
+        check_assist_not_applicable(
+            convert_from_to_tryfrom,
+            r#"
+struct Foo(String);
+
+impl $0TryFrom<String> for Foo {
+    fn try_from(val: String) -> Result<Self, Self::Error> {
+        Ok(Self(val))
+    }
+}
+            "#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 65ce3e8..3432629 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -1149,8 +1149,14 @@
     node: &dyn HasTokenAtOffset,
     ctx: &AssistContext<'_>,
 ) -> bool {
+    // FIXME: this quite an incorrect way to go about doing this :-)
+    // `FileReference` is an IDE-type --- it encapsulates data communicated to the human,
+    // but doesn't necessary fully reflect all the intricacies of the underlying language semantics
+    // The correct approach here would be to expose this entire analysis as a method on some hir
+    // type. Something like `body.free_variables(statement_range)`.
+
     // we directly modify variable with set: `n = 0`, `n += 1`
-    if reference.category == Some(ReferenceCategory::Write) {
+    if reference.category.contains(ReferenceCategory::WRITE) {
         return true;
     }
 
@@ -5617,7 +5623,7 @@
     fun_name(i);
 }
 
-fn $0fun_name(i: Struct<'static, T>) {
+fn $0fun_name(i: Struct<'_, T>) {
     foo(i);
 }
 "#,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index ff051fa..db94a21 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -175,8 +175,7 @@
         edit.edit_file(file);
 
         let target = function_builder.target.clone();
-        let function_template = function_builder.render();
-        let func = function_template.to_ast(ctx.config.snippet_cap, edit);
+        let func = function_builder.render(ctx.config.snippet_cap, edit);
 
         if let Some(name) = adt_name {
             let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db()))));
@@ -205,37 +204,6 @@
     find_struct_impl(ctx, &adt_source, &[fn_name.to_owned()]).map(|impl_| (impl_, range.file_id))
 }
 
-struct FunctionTemplate {
-    fn_def: ast::Fn,
-    ret_type: Option<ast::RetType>,
-    should_focus_return_type: bool,
-    tail_expr: ast::Expr,
-}
-
-impl FunctionTemplate {
-    fn to_ast(&self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
-        let Self { fn_def, ret_type, should_focus_return_type, tail_expr } = self;
-
-        if let Some(cap) = cap {
-            if *should_focus_return_type {
-                // Focus the return type if there is one
-                match ret_type {
-                    Some(ret_type) => {
-                        edit.add_placeholder_snippet(cap, ret_type.clone());
-                    }
-                    None => {
-                        edit.add_placeholder_snippet(cap, tail_expr.clone());
-                    }
-                }
-            } else {
-                edit.add_placeholder_snippet(cap, tail_expr.clone());
-            }
-        }
-
-        fn_def.clone()
-    }
-}
-
 struct FunctionBuilder {
     target: GeneratedFunctionTarget,
     fn_name: ast::Name,
@@ -339,7 +307,7 @@
         })
     }
 
-    fn render(self) -> FunctionTemplate {
+    fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
         let placeholder_expr = make::ext::expr_todo();
         let fn_body = make::block_expr(vec![], Some(placeholder_expr));
         let visibility = match self.visibility {
@@ -361,17 +329,31 @@
         )
         .clone_for_update();
 
-        FunctionTemplate {
-            ret_type: fn_def.ret_type(),
-            // PANIC: we guarantee we always create a function body with a tail expr
-            tail_expr: fn_def
-                .body()
-                .expect("generated function should have a body")
-                .tail_expr()
-                .expect("function body should have a tail expression"),
-            should_focus_return_type: self.should_focus_return_type,
-            fn_def,
+        let ret_type = fn_def.ret_type();
+        // PANIC: we guarantee we always create a function body with a tail expr
+        let tail_expr = fn_def
+            .body()
+            .expect("generated function should have a body")
+            .tail_expr()
+            .expect("function body should have a tail expression");
+
+        if let Some(cap) = cap {
+            if self.should_focus_return_type {
+                // Focus the return type if there is one
+                match ret_type {
+                    Some(ret_type) => {
+                        edit.add_placeholder_snippet(cap, ret_type.clone());
+                    }
+                    None => {
+                        edit.add_placeholder_snippet(cap, tail_expr.clone());
+                    }
+                }
+            } else {
+                edit.add_placeholder_snippet(cap, tail_expr.clone());
+            }
         }
+
+        fn_def
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index a90fe83..44307ff 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -49,13 +49,13 @@
 //
 // fn bar() {
 //     {
-//         let word = "안녕하세요";
+//         let word: &str = "안녕하세요";
 //         if !word.is_empty() {
 //             print(word);
 //         }
 //     };
 //     {
-//         let word = "여러분";
+//         let word: &str = "여러분";
 //         if !word.is_empty() {
 //             print(word);
 //         }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
index 67fea77..7c2dc0e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -59,10 +59,7 @@
 
     let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
         Ok(ty) => ty,
-        Err(_) => {
-            cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
-            return None;
-        }
+        Err(_) => return None,
     };
 
     let initializer = let_stmt.initializer()?;
@@ -315,14 +312,18 @@
 
     #[test]
     fn not_applicable_unknown_ty() {
-        cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred);
-        check_assist_not_applicable(
+        check_assist(
             promote_local_to_const,
             r"
 fn foo() {
     let x$0 = bar();
 }
 ",
+            r"
+fn foo() {
+    const $0X: _ = bar();
+}
+",
         );
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
index d67b259..0f0f13b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -145,7 +145,7 @@
     for scope in scopes {
         let mut search_non_import = |_, r: FileReference| {
             // The import itself is a use; we must skip that.
-            if r.category != Some(ReferenceCategory::Import) {
+            if !r.category.contains(ReferenceCategory::IMPORT) {
                 found = true;
                 true
             } else {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs
index f864ee5..264a2f0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs
@@ -3,7 +3,7 @@
     AstNode, AstToken,
 };
 
-use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
+use crate::{utils::test_related_attribute_syn, AssistContext, AssistId, AssistKind, Assists};
 
 // Assist: toggle_ignore
 //
@@ -26,7 +26,7 @@
 pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let attr: ast::Attr = ctx.find_node_at_offset()?;
     let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
-    let attr = test_related_attribute(&func)?;
+    let attr = test_related_attribute_syn(&func)?;
 
     match has_ignore_attribute(&func) {
         None => acc.add(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
new file mode 100644
index 0000000..0fa46ef
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
@@ -0,0 +1,581 @@
+use ide_db::source_change::SourceChangeBuilder;
+use itertools::Itertools;
+use syntax::{
+    algo,
+    ast::{self, make, AstNode},
+    ted::{self, Position},
+    NodeOrToken, SyntaxToken, TextRange, T,
+};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: wrap_unwrap_cfg_attr
+//
+// Wraps an attribute to a cfg_attr attribute or unwraps a cfg_attr attribute to the inner attributes.
+//
+// ```
+// #[derive$0(Debug)]
+// struct S {
+//    field: i32
+// }
+// ```
+// ->
+// ```
+// #[cfg_attr($0, derive(Debug))]
+// struct S {
+//    field: i32
+// }
+
+enum WrapUnwrapOption {
+    WrapDerive { derive: TextRange, attr: ast::Attr },
+    WrapAttr(ast::Attr),
+}
+
+/// Attempts to get the derive attribute from a derive attribute list
+///
+/// This will collect all the tokens in the "path" within the derive attribute list
+/// But a derive attribute list doesn't have paths. So we need to collect all the tokens before and after the ident
+///
+/// If this functions return None just map to WrapAttr
+fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption {
+    let attempt_attr = || {
+        {
+            let mut derive = ident.text_range();
+            // TokenTree is all the tokens between the `(` and `)`. They do not have paths. So a path `serde::Serialize` would be [Ident Colon Colon Ident]
+            // So lets say we have derive(Debug, serde::Serialize, Copy) ident would be on Serialize
+            // We need to grab all previous tokens until we find a `,` or `(` and all following tokens until we find a `,` or `)`
+            // We also want to consume the following comma if it exists
+
+            let mut prev = algo::skip_trivia_token(
+                ident.prev_sibling_or_token()?.into_token()?,
+                syntax::Direction::Prev,
+            )?;
+            let mut following = algo::skip_trivia_token(
+                ident.next_sibling_or_token()?.into_token()?,
+                syntax::Direction::Next,
+            )?;
+            if (prev.kind() == T![,] || prev.kind() == T!['('])
+                && (following.kind() == T![,] || following.kind() == T![')'])
+            {
+                // This would be a single ident such as Debug. As no path is present
+                if following.kind() == T![,] {
+                    derive = derive.cover(following.text_range());
+                } else if following.kind() == T![')'] && prev.kind() == T![,] {
+                    derive = derive.cover(prev.text_range());
+                }
+
+                Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() })
+            } else {
+                let mut consumed_comma = false;
+                // Collect the path
+                while let Some(prev_token) = algo::skip_trivia_token(prev, syntax::Direction::Prev)
+                {
+                    let kind = prev_token.kind();
+                    if kind == T![,] {
+                        consumed_comma = true;
+                        derive = derive.cover(prev_token.text_range());
+                        break;
+                    } else if kind == T!['('] {
+                        break;
+                    } else {
+                        derive = derive.cover(prev_token.text_range());
+                    }
+                    prev = prev_token.prev_sibling_or_token()?.into_token()?;
+                }
+                while let Some(next_token) =
+                    algo::skip_trivia_token(following.clone(), syntax::Direction::Next)
+                {
+                    let kind = next_token.kind();
+                    match kind {
+                        T![,] if !consumed_comma => {
+                            derive = derive.cover(next_token.text_range());
+                            break;
+                        }
+                        T![')'] | T![,] => break,
+                        _ => derive = derive.cover(next_token.text_range()),
+                    }
+                    following = next_token.next_sibling_or_token()?.into_token()?;
+                }
+                Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() })
+            }
+        }
+    };
+    if ident.parent().and_then(ast::TokenTree::cast).is_none()
+        || !attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default()
+    {
+        WrapUnwrapOption::WrapAttr(attr)
+    } else {
+        attempt_attr().unwrap_or(WrapUnwrapOption::WrapAttr(attr))
+    }
+}
+pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let option = if ctx.has_empty_selection() {
+        let ident = ctx.find_token_syntax_at_offset(T![ident]);
+        let attr = ctx.find_node_at_offset::<ast::Attr>();
+        match (attr, ident) {
+            (Some(attr), Some(ident))
+                if attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default() =>
+            {
+                Some(attempt_get_derive(attr.clone(), ident))
+            }
+
+            (Some(attr), _) => Some(WrapUnwrapOption::WrapAttr(attr)),
+            _ => None,
+        }
+    } else {
+        let covering_element = ctx.covering_element();
+        match covering_element {
+            NodeOrToken::Node(node) => ast::Attr::cast(node).map(WrapUnwrapOption::WrapAttr),
+            NodeOrToken::Token(ident) if ident.kind() == syntax::T![ident] => {
+                let attr = ident.parent_ancestors().find_map(ast::Attr::cast)?;
+                Some(attempt_get_derive(attr.clone(), ident))
+            }
+            _ => None,
+        }
+    }?;
+    match option {
+        WrapUnwrapOption::WrapAttr(attr) if attr.simple_name().as_deref() == Some("cfg_attr") => {
+            unwrap_cfg_attr(acc, attr)
+        }
+        WrapUnwrapOption::WrapAttr(attr) => wrap_cfg_attr(acc, ctx, attr),
+        WrapUnwrapOption::WrapDerive { derive, attr } => wrap_derive(acc, ctx, attr, derive),
+    }
+}
+
+fn wrap_derive(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+    attr: ast::Attr,
+    derive_element: TextRange,
+) -> Option<()> {
+    let range = attr.syntax().text_range();
+    let token_tree = attr.token_tree()?;
+    let mut path_text = String::new();
+
+    let mut cfg_derive_tokens = Vec::new();
+    let mut new_derive = Vec::new();
+
+    for tt in token_tree.token_trees_and_tokens() {
+        let NodeOrToken::Token(token) = tt else {
+            continue;
+        };
+        if token.kind() == T!['('] || token.kind() == T![')'] {
+            continue;
+        }
+
+        if derive_element.contains_range(token.text_range()) {
+            if token.kind() != T![,] && token.kind() != syntax::SyntaxKind::WHITESPACE {
+                path_text.push_str(token.text());
+                cfg_derive_tokens.push(NodeOrToken::Token(token));
+            }
+        } else {
+            new_derive.push(NodeOrToken::Token(token));
+        }
+    }
+    let handle_source_change = |edit: &mut SourceChangeBuilder| {
+        let new_derive = make::attr_outer(make::meta_token_tree(
+            make::ext::ident_path("derive"),
+            make::token_tree(T!['('], new_derive),
+        ))
+        .clone_for_update();
+        let meta = make::meta_token_tree(
+            make::ext::ident_path("cfg_attr"),
+            make::token_tree(
+                T!['('],
+                vec![
+                    NodeOrToken::Token(make::token(T![,])),
+                    NodeOrToken::Token(make::tokens::whitespace(" ")),
+                    NodeOrToken::Token(make::tokens::ident("derive")),
+                    NodeOrToken::Node(make::token_tree(T!['('], cfg_derive_tokens)),
+                ],
+            ),
+        );
+        // Remove the derive attribute
+        let edit_attr = edit.make_syntax_mut(attr.syntax().clone());
+
+        ted::replace(edit_attr, new_derive.syntax().clone());
+        let cfg_attr = make::attr_outer(meta).clone_for_update();
+
+        ted::insert_all_raw(
+            Position::after(new_derive.syntax().clone()),
+            vec![make::tokens::whitespace("\n").into(), cfg_attr.syntax().clone().into()],
+        );
+        if let Some(snippet_cap) = ctx.config.snippet_cap {
+            if let Some(first_meta) =
+                cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
+            {
+                edit.add_tabstop_after_token(snippet_cap, first_meta)
+            }
+        }
+    };
+
+    acc.add(
+        AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor),
+        format!("Wrap #[derive({path_text})] in `cfg_attr`",),
+        range,
+        handle_source_change,
+    );
+    Some(())
+}
+fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) -> Option<()> {
+    let range = attr.syntax().text_range();
+    let path = attr.path()?;
+    let handle_source_change = |edit: &mut SourceChangeBuilder| {
+        let mut raw_tokens = vec![
+            NodeOrToken::Token(make::token(T![,])),
+            NodeOrToken::Token(make::tokens::whitespace(" ")),
+        ];
+        path.syntax().descendants_with_tokens().for_each(|it| {
+            if let NodeOrToken::Token(token) = it {
+                raw_tokens.push(NodeOrToken::Token(token));
+            }
+        });
+        if let Some(meta) = attr.meta() {
+            if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) {
+                raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
+                raw_tokens.push(NodeOrToken::Token(eq.clone()));
+                raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
+
+                expr.syntax().descendants_with_tokens().for_each(|it| {
+                    if let NodeOrToken::Token(token) = it {
+                        raw_tokens.push(NodeOrToken::Token(token));
+                    }
+                });
+            } else if let Some(tt) = meta.token_tree() {
+                raw_tokens.extend(tt.token_trees_and_tokens());
+            }
+        }
+        let meta = make::meta_token_tree(
+            make::ext::ident_path("cfg_attr"),
+            make::token_tree(T!['('], raw_tokens),
+        );
+        let cfg_attr = if attr.excl_token().is_some() {
+            make::attr_inner(meta)
+        } else {
+            make::attr_outer(meta)
+        }
+        .clone_for_update();
+        let attr_syntax = edit.make_syntax_mut(attr.syntax().clone());
+        ted::replace(attr_syntax, cfg_attr.syntax());
+
+        if let Some(snippet_cap) = ctx.config.snippet_cap {
+            if let Some(first_meta) =
+                cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
+            {
+                edit.add_tabstop_after_token(snippet_cap, first_meta)
+            }
+        }
+    };
+    acc.add(
+        AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor),
+        "Convert to `cfg_attr`",
+        range,
+        handle_source_change,
+    );
+    Some(())
+}
+fn unwrap_cfg_attr(acc: &mut Assists, attr: ast::Attr) -> Option<()> {
+    let range = attr.syntax().text_range();
+    let meta = attr.meta()?;
+    let meta_tt = meta.token_tree()?;
+    let mut inner_attrs = Vec::with_capacity(1);
+    let mut found_comma = false;
+    let mut iter = meta_tt.token_trees_and_tokens().skip(1).peekable();
+    while let Some(tt) = iter.next() {
+        if let NodeOrToken::Token(token) = &tt {
+            if token.kind() == T![')'] {
+                break;
+            }
+            if token.kind() == T![,] {
+                found_comma = true;
+                continue;
+            }
+        }
+        if !found_comma {
+            continue;
+        }
+        let Some(attr_name) = tt.into_token().and_then(|token| {
+            if token.kind() == T![ident] {
+                Some(make::ext::ident_path(token.text()))
+            } else {
+                None
+            }
+        }) else {
+            continue;
+        };
+        let next_tt = iter.next()?;
+        let meta = match next_tt {
+            NodeOrToken::Node(tt) => make::meta_token_tree(attr_name, tt),
+            NodeOrToken::Token(token) if token.kind() == T![,] || token.kind() == T![')'] => {
+                make::meta_path(attr_name)
+            }
+            NodeOrToken::Token(token) => {
+                let equals = algo::skip_trivia_token(token, syntax::Direction::Next)?;
+                if equals.kind() != T![=] {
+                    return None;
+                }
+                let expr_token =
+                    algo::skip_trivia_token(equals.next_token()?, syntax::Direction::Next)
+                        .and_then(|it| {
+                            if it.kind().is_literal() {
+                                Some(make::expr_literal(it.text()))
+                            } else {
+                                None
+                            }
+                        })?;
+                make::meta_expr(attr_name, ast::Expr::Literal(expr_token))
+            }
+        };
+        if attr.excl_token().is_some() {
+            inner_attrs.push(make::attr_inner(meta));
+        } else {
+            inner_attrs.push(make::attr_outer(meta));
+        }
+    }
+    if inner_attrs.is_empty() {
+        return None;
+    }
+    let handle_source_change = |f: &mut SourceChangeBuilder| {
+        let inner_attrs = inner_attrs.iter().map(|it| it.to_string()).join("\n");
+        f.replace(range, inner_attrs);
+    };
+    acc.add(
+        AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor),
+        "Extract Inner Attributes from `cfg_attr`",
+        range,
+        handle_source_change,
+    );
+    Some(())
+}
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_assist;
+
+    use super::*;
+
+    #[test]
+    fn test_basic_to_from_cfg_attr() {
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #[derive$0(Debug)]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+            r#"
+            #[cfg_attr($0, derive(Debug))]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+        );
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #[cfg_attr(debug_assertions, $0 derive(Debug))]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+            r#"
+            #[derive(Debug)]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+        );
+    }
+    #[test]
+    fn to_from_path_attr() {
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            pub struct Test {
+                #[foo$0]
+                test: u32,
+            }
+            "#,
+            r#"
+            pub struct Test {
+                #[cfg_attr($0, foo)]
+                test: u32,
+            }
+            "#,
+        );
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            pub struct Test {
+                #[cfg_attr(debug_assertions$0, foo)]
+                test: u32,
+            }
+            "#,
+            r#"
+            pub struct Test {
+                #[foo]
+                test: u32,
+            }
+            "#,
+        );
+    }
+    #[test]
+    fn to_from_eq_attr() {
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            pub struct Test {
+                #[foo = "bar"$0]
+                test: u32,
+            }
+            "#,
+            r#"
+            pub struct Test {
+                #[cfg_attr($0, foo = "bar")]
+                test: u32,
+            }
+            "#,
+        );
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            pub struct Test {
+                #[cfg_attr(debug_assertions$0, foo = "bar")]
+                test: u32,
+            }
+            "#,
+            r#"
+            pub struct Test {
+                #[foo = "bar"]
+                test: u32,
+            }
+            "#,
+        );
+    }
+    #[test]
+    fn inner_attrs() {
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #![no_std$0]
+            "#,
+            r#"
+            #![cfg_attr($0, no_std)]
+            "#,
+        );
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #![cfg_attr(not(feature = "std")$0, no_std)]
+            "#,
+            r#"
+            #![no_std]
+            "#,
+        );
+    }
+    #[test]
+    fn test_derive_wrap() {
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #[derive(Debug$0, Clone, Copy)]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+            r#"
+            #[derive( Clone, Copy)]
+            #[cfg_attr($0, derive(Debug))]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+        );
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #[derive(Clone, Debug$0, Copy)]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+            r#"
+            #[derive(Clone,  Copy)]
+            #[cfg_attr($0, derive(Debug))]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+        );
+    }
+    #[test]
+    fn test_derive_wrap_with_path() {
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #[derive(std::fmt::Debug$0, Clone, Copy)]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+            r#"
+            #[derive( Clone, Copy)]
+            #[cfg_attr($0, derive(std::fmt::Debug))]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+        );
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #[derive(Clone, std::fmt::Debug$0, Copy)]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+            r#"
+            #[derive(Clone, Copy)]
+            #[cfg_attr($0, derive(std::fmt::Debug))]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+        );
+    }
+    #[test]
+    fn test_derive_wrap_at_end() {
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #[derive(std::fmt::Debug, Clone, Cop$0y)]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+            r#"
+            #[derive(std::fmt::Debug, Clone)]
+            #[cfg_attr($0, derive(Copy))]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+        );
+        check_assist(
+            wrap_unwrap_cfg_attr,
+            r#"
+            #[derive(Clone, Copy, std::fmt::D$0ebug)]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+            r#"
+            #[derive(Clone, Copy)]
+            #[cfg_attr($0, derive(std::fmt::Debug))]
+            pub struct Test {
+                test: u32,
+            }
+            "#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index 8f0b8f8..0df5e91 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -116,6 +116,7 @@
     mod change_visibility;
     mod convert_bool_then;
     mod convert_comment_block;
+    mod convert_from_to_tryfrom;
     mod convert_integer_literal;
     mod convert_into_to_from;
     mod convert_iter_for_each_to_for;
@@ -217,6 +218,7 @@
     mod unwrap_result_return_type;
     mod unwrap_tuple;
     mod wrap_return_type_in_result;
+    mod wrap_unwrap_cfg_attr;
 
     pub(crate) fn all() -> &'static [Handler] {
         &[
@@ -237,6 +239,7 @@
             convert_bool_then::convert_bool_then_to_if,
             convert_bool_then::convert_if_to_bool_then,
             convert_comment_block::convert_comment_block,
+            convert_from_to_tryfrom::convert_from_to_tryfrom,
             convert_integer_literal::convert_integer_literal,
             convert_into_to_from::convert_into_to_from,
             convert_iter_for_each_to_for::convert_iter_for_each_to_for,
@@ -342,6 +345,8 @@
             unwrap_tuple::unwrap_tuple,
             unqualify_method_call::unqualify_method_call,
             wrap_return_type_in_result::wrap_return_type_in_result,
+            wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr,
+
             // These are manually sorted for better priorities. By default,
             // priority is determined by the size of the target range (smaller
             // target wins). If the ranges are equal, position in this list is
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index a66e199..937e78f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -391,6 +391,36 @@
 }
 
 #[test]
+fn doctest_convert_from_to_tryfrom() {
+    check_doc_test(
+        "convert_from_to_tryfrom",
+        r#####"
+//- minicore: from
+impl $0From<usize> for Thing {
+    fn from(val: usize) -> Self {
+        Thing {
+            b: val.to_string(),
+            a: val
+        }
+    }
+}
+"#####,
+        r#####"
+impl TryFrom<usize> for Thing {
+    type Error = ${0:()};
+
+    fn try_from(val: usize) -> Result<Self, Self::Error> {
+        Ok(Thing {
+            b: val.to_string(),
+            a: val
+        })
+    }
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_convert_if_to_bool_then() {
     check_doc_test(
         "convert_if_to_bool_then",
@@ -1820,13 +1850,13 @@
 
 fn bar() {
     {
-        let word = "안녕하세요";
+        let word: &str = "안녕하세요";
         if !word.is_empty() {
             print(word);
         }
     };
     {
-        let word = "여러분";
+        let word: &str = "여러분";
         if !word.is_empty() {
             print(word);
         }
@@ -3151,3 +3181,22 @@
 "#####,
     )
 }
+
+#[test]
+fn doctest_wrap_unwrap_cfg_attr() {
+    check_doc_test(
+        "wrap_unwrap_cfg_attr",
+        r#####"
+#[derive$0(Debug)]
+struct S {
+   field: i32
+}
+"#####,
+        r#####"
+#[cfg_attr($0, derive(Debug))]
+struct S {
+   field: i32
+}
+"#####,
+    )
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 8bd5d17..bc0c9b7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -71,7 +71,7 @@
 ///
 /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
 /// but it's better than not to have the runnables for the tests at all.
-pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
+pub fn test_related_attribute_syn(fn_def: &ast::Fn) -> Option<ast::Attr> {
     fn_def.attrs().find_map(|attr| {
         let path = attr.path()?;
         let text = path.syntax().text().to_string();
@@ -83,6 +83,19 @@
     })
 }
 
+pub fn has_test_related_attribute(attrs: &hir::AttrsWithOwner) -> bool {
+    attrs.iter().any(|attr| {
+        let path = attr.path();
+        (|| {
+            Some(
+                path.segments().first()?.as_text()?.starts_with("test")
+                    || path.segments().last()?.as_text()?.ends_with("test"),
+            )
+        })()
+        .unwrap_or_default()
+    })
+}
+
 #[derive(Clone, Copy, PartialEq)]
 pub enum IgnoreAssocItems {
     DocHiddenAttrPresent,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 361ad82..c6e243b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -9,6 +9,7 @@
     ty_filter::TryEnum,
     SnippetCap,
 };
+use stdx::never;
 use syntax::{
     ast::{self, make, AstNode, AstToken},
     SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
@@ -319,7 +320,9 @@
 ) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> {
     let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range;
     if ctx.source_range().end() < receiver_range.start() {
-        // This shouldn't happen, yet it does. I assume this might be due to an incorrect token mapping.
+        // This shouldn't happen, yet it does. I assume this might be due to an incorrect token
+        // mapping.
+        never!();
         return None;
     }
     let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
index e467808..2361d14 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
@@ -226,7 +226,7 @@
     if !path_ctx.is_trivial_path() {
         return None;
     }
-    let x = match ascription {
+    let ty = match ascription {
         TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
             ctx.sema.type_of_pat(pat.as_ref()?)
         }
@@ -235,7 +235,9 @@
         }
     }?
     .adjusted();
-    let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
-    acc.add(render_type_inference(ty_string, ctx));
+    if !ty.is_unknown() {
+        let ty_string = ty.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
+        acc.add(render_type_inference(ty_string, ctx));
+    }
     None
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 995e3f4..8b435f4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -17,7 +17,7 @@
 };
 use syntax::{
     ast::{self, AttrKind, NameOrNameRef},
-    AstNode, SmolStr,
+    AstNode, Edition, SmolStr,
     SyntaxKind::{self, *},
     SyntaxToken, TextRange, TextSize, T,
 };
@@ -667,7 +667,8 @@
         let file_with_fake_ident = {
             let parse = db.parse(file_id);
             let edit = Indel::insert(offset, COMPLETION_MARKER.to_owned());
-            parse.reparse(&edit).tree()
+            // FIXME: Edition
+            parse.reparse(&edit, Edition::CURRENT).tree()
         };
 
         // always pick the token to the immediate left of the cursor, as that is what we are actually
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
index e667e2e..7d710f1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
@@ -200,7 +200,7 @@
 ) -> Option<(Box<[GreenNode]>, String, Option<Box<str>>)> {
     let mut imports = Vec::with_capacity(requires.len());
     for path in requires.iter() {
-        let use_path = ast::SourceFile::parse(&format!("use {path};"))
+        let use_path = ast::SourceFile::parse(&format!("use {path};"), syntax::Edition::CURRENT)
             .syntax_node()
             .descendants()
             .find_map(ast::Path::cast)?;
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
index 3718dff..64a32de 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
@@ -19,7 +19,7 @@
             en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Foo<…>    Foo<'static, {unknown}, _>
+            st Foo<…>    Foo<'{error}, {unknown}, _>
             st Record    Record
             st Tuple     Tuple
             st Unit      Unit
@@ -92,7 +92,7 @@
             en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Foo<…>    Foo<'static, {unknown}, _>
+            st Foo<…>    Foo<'{error}, {unknown}, _>
             st Record    Record
             st Tuple     Tuple
             st Unit      Unit
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index 9770972..66f1bff 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -20,8 +20,8 @@
             en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            sp Self      Foo<'static, {unknown}, _>
-            st Foo<…>    Foo<'static, {unknown}, _>
+            sp Self      Foo<'{error}, {unknown}, _>
+            st Foo<…>    Foo<'{error}, {unknown}, _>
             st Record    Record
             st Tuple     Tuple
             st Unit      Unit
@@ -45,8 +45,8 @@
             en Enum       Enum
             ma makro!(…)  macro_rules! makro
             md module
-            sp Self       Foo<'static, {unknown}, _>
-            st Foo<…>     Foo<'static, {unknown}, _>
+            sp Self       Foo<'{error}, {unknown}, _>
+            st Foo<…>     Foo<'{error}, {unknown}, _>
             st Record     Record
             st Tuple      Tuple
             st Unit       Unit
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
index 071e1b4..9a6826a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
@@ -26,6 +26,7 @@
 memchr = "2.6.4"
 triomphe.workspace = true
 nohash-hasher.workspace = true
+bitflags.workspace = true
 
 # local deps
 base-db.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
index ec05f6d..ce9a5f0 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
@@ -91,82 +91,101 @@
             crate::symbol_index::LocalRootsQuery
             crate::symbol_index::LibraryRootsQuery
             // HirDatabase
-            hir::db::MirBodyQuery
-            hir::db::BorrowckQuery
-            hir::db::TyQuery
-            hir::db::ValueTyQuery
-            hir::db::ImplSelfTyQuery
-            hir::db::ConstParamTyQuery
-            hir::db::ConstEvalQuery
-            hir::db::ConstEvalDiscriminantQuery
-            hir::db::ImplTraitQuery
-            hir::db::FieldTypesQuery
-            hir::db::LayoutOfAdtQuery
-            hir::db::TargetDataLayoutQuery
-            hir::db::CallableItemSignatureQuery
-            hir::db::ReturnTypeImplTraitsQuery
-            hir::db::GenericPredicatesForParamQuery
-            hir::db::GenericPredicatesQuery
-            hir::db::TraitEnvironmentQuery
-            hir::db::GenericDefaultsQuery
-            hir::db::InherentImplsInCrateQuery
-            hir::db::InherentImplsInBlockQuery
-            hir::db::IncoherentInherentImplCratesQuery
-            hir::db::TraitImplsInCrateQuery
-            hir::db::TraitImplsInBlockQuery
-            hir::db::TraitImplsInDepsQuery
-            hir::db::InternCallableDefQuery
-            hir::db::InternLifetimeParamIdQuery
-            hir::db::InternImplTraitIdQuery
-            hir::db::InternTypeOrConstParamIdQuery
-            hir::db::InternClosureQuery
-            hir::db::InternCoroutineQuery
-            hir::db::AssociatedTyDataQuery
-            hir::db::TraitDatumQuery
             hir::db::AdtDatumQuery
-            hir::db::ImplDatumQuery
+            hir::db::AdtVarianceQuery
+            hir::db::AssociatedTyDataQuery
+            hir::db::AssociatedTyValueQuery
+            hir::db::BorrowckQuery
+            hir::db::CallableItemSignatureQuery
+            hir::db::ConstEvalDiscriminantQuery
+            hir::db::ConstEvalQuery
+            hir::db::ConstEvalStaticQuery
+            hir::db::ConstParamTyQuery
+            hir::db::FieldTypesQuery
             hir::db::FnDefDatumQuery
             hir::db::FnDefVarianceQuery
-            hir::db::AdtVarianceQuery
-            hir::db::AssociatedTyValueQuery
+            hir::db::GenericDefaultsQuery
+            hir::db::GenericPredicatesForParamQuery
+            hir::db::GenericPredicatesQuery
+            hir::db::ImplDatumQuery
+            hir::db::ImplSelfTyQuery
+            hir::db::ImplTraitQuery
+            hir::db::IncoherentInherentImplCratesQuery
+            hir::db::InherentImplsInBlockQuery
+            hir::db::InherentImplsInCrateQuery
+            hir::db::InternCallableDefQuery
+            hir::db::InternClosureQuery
+            hir::db::InternCoroutineQuery
+            hir::db::InternImplTraitIdQuery
+            hir::db::InternLifetimeParamIdQuery
+            hir::db::InternTypeOrConstParamIdQuery
+            hir::db::LayoutOfAdtQuery
+            hir::db::MirBodyQuery
             hir::db::ProgramClausesForChalkEnvQuery
+            hir::db::ReturnTypeImplTraitsQuery
+            hir::db::TargetDataLayoutQuery
+            hir::db::TraitDatumQuery
+            hir::db::TraitEnvironmentQuery
+            hir::db::TraitImplsInBlockQuery
+            hir::db::TraitImplsInCrateQuery
+            hir::db::TraitImplsInDepsQuery
+            hir::db::TyQuery
+            hir::db::ValueTyQuery
 
             // DefDatabase
-            hir::db::FileItemTreeQuery
+            hir::db::AttrsQuery
             hir::db::BlockDefMapQuery
-            hir::db::StructDataWithDiagnosticsQuery
-            hir::db::UnionDataWithDiagnosticsQuery
+            hir::db::BlockItemTreeQuery
+            hir::db::BodyQuery
+            hir::db::BodyWithSourceMapQuery
+            hir::db::ConstDataQuery
+            hir::db::ConstVisibilityQuery
+            hir::db::CrateDefMapQuery
+            hir::db::CrateLangItemsQuery
+            hir::db::CrateNotableTraitsQuery
+            hir::db::CrateSupportsNoStdQuery
             hir::db::EnumDataQuery
             hir::db::EnumVariantDataWithDiagnosticsQuery
-            hir::db::ImplDataWithDiagnosticsQuery
-            hir::db::TraitDataWithDiagnosticsQuery
-            hir::db::TraitAliasDataQuery
-            hir::db::TypeAliasDataQuery
+            hir::db::ExprScopesQuery
+            hir::db::ExternCrateDeclDataQuery
+            hir::db::FieldVisibilitiesQuery
+            hir::db::FieldsAttrsQuery
+            hir::db::FieldsAttrsSourceMapQuery
+            hir::db::FileItemTreeQuery
             hir::db::FunctionDataQuery
-            hir::db::ConstDataQuery
-            hir::db::StaticDataQuery
+            hir::db::FunctionVisibilityQuery
+            hir::db::GenericParamsQuery
+            hir::db::ImplDataWithDiagnosticsQuery
+            hir::db::ImportMapQuery
+            hir::db::InternAnonymousConstQuery
+            hir::db::InternBlockQuery
+            hir::db::InternConstQuery
+            hir::db::InternEnumQuery
+            hir::db::InternExternBlockQuery
+            hir::db::InternExternCrateQuery
+            hir::db::InternFunctionQuery
+            hir::db::InternImplQuery
+            hir::db::InternInTypeConstQuery
+            hir::db::InternMacro2Query
+            hir::db::InternMacroRulesQuery
+            hir::db::InternProcMacroQuery
+            hir::db::InternStaticQuery
+            hir::db::InternStructQuery
+            hir::db::InternTraitAliasQuery
+            hir::db::InternTraitQuery
+            hir::db::InternTypeAliasQuery
+            hir::db::InternUnionQuery
+            hir::db::InternUseQuery
+            hir::db::LangItemQuery
             hir::db::Macro2DataQuery
             hir::db::MacroRulesDataQuery
             hir::db::ProcMacroDataQuery
-            hir::db::BodyWithSourceMapQuery
-            hir::db::BodyQuery
-            hir::db::ExprScopesQuery
-            hir::db::GenericParamsQuery
-            hir::db::FieldsAttrsQuery
-            hir::db::FieldsAttrsSourceMapQuery
-            hir::db::AttrsQuery
-            hir::db::CrateLangItemsQuery
-            hir::db::LangItemQuery
-            hir::db::ImportMapQuery
-            hir::db::FieldVisibilitiesQuery
-            hir::db::FunctionVisibilityQuery
-            hir::db::ConstVisibilityQuery
-            hir::db::CrateSupportsNoStdQuery
-            hir::db::ExternCrateDeclDataQuery
-            hir::db::InternAnonymousConstQuery
-            hir::db::InternExternCrateQuery
-            hir::db::InternInTypeConstQuery
-            hir::db::InternUseQuery
+            hir::db::StaticDataQuery
+            hir::db::StructDataWithDiagnosticsQuery
+            hir::db::TraitAliasDataQuery
+            hir::db::TraitDataWithDiagnosticsQuery
+            hir::db::TypeAliasDataQuery
+            hir::db::UnionDataWithDiagnosticsQuery
 
             // InternDatabase
             hir::db::InternFunctionQuery
@@ -192,9 +211,10 @@
             hir::db::InternMacroCallQuery
             hir::db::InternSyntaxContextQuery
             hir::db::MacroArgQuery
+            hir::db::ParseMacroExpansionErrorQuery
             hir::db::ParseMacroExpansionQuery
-            hir::db::RealSpanMapQuery
             hir::db::ProcMacrosQuery
+            hir::db::RealSpanMapQuery
 
             // LineIndexDatabase
             crate::LineIndexQuery
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
index 72ca354..58e77b9 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
@@ -91,8 +91,10 @@
     db: &dyn DefDatabase,
     attrs: &AttrsWithOwner,
 ) -> Option<(Documentation, DocsRangeMap)> {
-    let docs =
-        attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
+    let docs = attrs
+        .by_key("doc")
+        .attrs()
+        .filter_map(|attr| attr.string_value_unescape().map(|s| (s, attr.id)));
     let indent = doc_indent(attrs);
     let mut buf = String::new();
     let mut mapping = Vec::new();
@@ -132,7 +134,7 @@
 }
 
 pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> {
-    let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value());
+    let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value_unescape());
     let indent = doc_indent(attrs);
     let mut buf = String::new();
     for doc in docs {
@@ -270,10 +272,9 @@
     attrs
         .by_key("doc")
         .attrs()
-        .filter_map(|attr| attr.string_value())
+        .filter_map(|attr| attr.string_value()) // no need to use unescape version here
         .flat_map(|s| s.lines())
-        .filter(|line| !line.chars().all(|c| c.is_whitespace()))
-        .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
+        .filter_map(|line| line.chars().position(|c| !c.is_whitespace()))
         .min()
         .unwrap_or(0)
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs
index bd5c464..e97f1b8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs
@@ -176,7 +176,7 @@
 
 pub fn insert_use_as_alias(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
     let text: &str = "use foo as _";
-    let parse = syntax::SourceFile::parse(text);
+    let parse = syntax::SourceFile::parse(text, span::Edition::CURRENT);
     let node = parse
         .tree()
         .syntax()
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs
index 10c285a..9d1f1cc 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs
@@ -1243,7 +1243,7 @@
         .and_then(|it| ImportScope::find_insert_use_container(&it, sema))
         .or_else(|| ImportScope::from(syntax))
         .unwrap();
-    let path = ast::SourceFile::parse(&format!("use {path};"))
+    let path = ast::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT)
         .tree()
         .syntax()
         .descendants()
@@ -1292,14 +1292,14 @@
 }
 
 fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) {
-    let use0 = ast::SourceFile::parse(ra_fixture0)
+    let use0 = ast::SourceFile::parse(ra_fixture0, span::Edition::CURRENT)
         .tree()
         .syntax()
         .descendants()
         .find_map(ast::Use::cast)
         .unwrap();
 
-    let use1 = ast::SourceFile::parse(ra_fixture1)
+    let use1 = ast::SourceFile::parse(ra_fixture1, span::Edition::CURRENT)
         .tree()
         .syntax()
         .descendants()
@@ -1311,7 +1311,7 @@
 }
 
 fn check_guess(ra_fixture: &str, expected: ImportGranularityGuess) {
-    let syntax = ast::SourceFile::parse(ra_fixture).tree().syntax().clone();
+    let syntax = ast::SourceFile::parse(ra_fixture, span::Edition::CURRENT).tree().syntax().clone();
     let file = ImportScope::from(syntax).unwrap();
     assert_eq!(super::guess_granularity_from_scope(&file), expected);
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/label.rs b/src/tools/rust-analyzer/crates/ide-db/src/label.rs
index 4b6d54b..919c127 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/label.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/label.rs
@@ -1,6 +1,8 @@
 //! See [`Label`]
 use std::fmt;
 
+use stdx::always;
+
 /// A type to specify UI label, like an entry in the list of assists. Enforces
 /// proper casing:
 ///
@@ -30,7 +32,7 @@
 
 impl Label {
     pub fn new(label: String) -> Label {
-        assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
+        always!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
         Label(label)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index a3ecc10..cb10331 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -64,7 +64,7 @@
     pub range: TextRange,
     /// The node of the reference in the (macro-)file
     pub name: FileReferenceNode,
-    pub category: Option<ReferenceCategory>,
+    pub category: ReferenceCategory,
 }
 
 #[derive(Debug, Clone)]
@@ -124,17 +124,16 @@
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub enum ReferenceCategory {
-    // FIXME: Add this variant and delete the `retain_adt_literal_usages` function.
-    // Create
-    Write,
-    Read,
-    Import,
-    // FIXME: Some day should be able to search in doc comments. Would probably
-    // need to switch from enum to bitflags then?
-    // DocComment
-    Test,
+bitflags::bitflags! {
+    #[derive(Copy, Clone, Default, PartialEq, Eq, Hash, Debug)]
+    pub struct ReferenceCategory: u8 {
+        // FIXME: Add this variant and delete the `retain_adt_literal_usages` function.
+        // const CREATE = 1 << 0;
+        const WRITE = 1 << 0;
+        const READ = 1 << 1;
+        const IMPORT = 1 << 2;
+        const TEST = 1 << 3;
+    }
 }
 
 /// Generally, `search_scope` returns files that might contain references for the element.
@@ -660,7 +659,7 @@
                 let reference = FileReference {
                     range,
                     name: FileReferenceNode::NameRef(name_ref.clone()),
-                    category: None,
+                    category: ReferenceCategory::empty(),
                 };
                 sink(file_id, reference)
             }
@@ -676,10 +675,15 @@
         match NameRefClass::classify(self.sema, name_ref) {
             Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => {
                 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
+                let category = if is_name_ref_in_import(name_ref) {
+                    ReferenceCategory::IMPORT
+                } else {
+                    ReferenceCategory::empty()
+                };
                 let reference = FileReference {
                     range,
                     name: FileReferenceNode::NameRef(name_ref.clone()),
-                    category: is_name_ref_in_import(name_ref).then_some(ReferenceCategory::Import),
+                    category,
                 };
                 sink(file_id, reference)
             }
@@ -700,7 +704,7 @@
                 let reference = FileReference {
                     range,
                     name: FileReferenceNode::FormatStringEntry(token, range),
-                    category: Some(ReferenceCategory::Read),
+                    category: ReferenceCategory::READ,
                 };
                 sink(file_id, reference)
             }
@@ -719,7 +723,7 @@
                 let reference = FileReference {
                     range,
                     name: FileReferenceNode::Lifetime(lifetime.clone()),
-                    category: None,
+                    category: ReferenceCategory::empty(),
                 };
                 sink(file_id, reference)
             }
@@ -817,7 +821,7 @@
                     range,
                     name: FileReferenceNode::Name(name.clone()),
                     // FIXME: mutable patterns should have `Write` access
-                    category: Some(ReferenceCategory::Read),
+                    category: ReferenceCategory::READ,
                 };
                 sink(file_id, reference)
             }
@@ -826,7 +830,7 @@
                 let reference = FileReference {
                     range,
                     name: FileReferenceNode::Name(name.clone()),
-                    category: None,
+                    category: ReferenceCategory::empty(),
                 };
                 sink(file_id, reference)
             }
@@ -851,7 +855,7 @@
                 let reference = FileReference {
                     range,
                     name: FileReferenceNode::Name(name.clone()),
-                    category: None,
+                    category: ReferenceCategory::empty(),
                 };
                 sink(file_id, reference)
             }
@@ -875,38 +879,41 @@
         sema: &Semantics<'_, RootDatabase>,
         def: &Definition,
         r: &ast::NameRef,
-    ) -> Option<ReferenceCategory> {
+    ) -> ReferenceCategory {
+        let mut result = ReferenceCategory::empty();
         if is_name_ref_in_test(sema, r) {
-            return Some(ReferenceCategory::Test);
+            result |= ReferenceCategory::TEST;
         }
 
         // Only Locals and Fields have accesses for now.
         if !matches!(def, Definition::Local(_) | Definition::Field(_)) {
-            return is_name_ref_in_import(r).then_some(ReferenceCategory::Import);
+            if is_name_ref_in_import(r) {
+                result |= ReferenceCategory::IMPORT;
+            }
+            return result;
         }
 
         let mode = r.syntax().ancestors().find_map(|node| {
-        match_ast! {
-            match node {
-                ast::BinExpr(expr) => {
-                    if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
-                        // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals).
-                        // FIXME: This is not terribly accurate.
-                        if let Some(lhs) = expr.lhs() {
-                            if lhs.syntax().text_range().end() == r.syntax().text_range().end() {
-                                return Some(ReferenceCategory::Write);
+            match_ast! {
+                match node {
+                    ast::BinExpr(expr) => {
+                        if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
+                            // If the variable or field ends on the LHS's end then it's a Write
+                            // (covers fields and locals). FIXME: This is not terribly accurate.
+                            if let Some(lhs) = expr.lhs() {
+                                if lhs.syntax().text_range().end() == r.syntax().text_range().end() {
+                                    return Some(ReferenceCategory::WRITE)
+                                }
                             }
                         }
-                    }
-                    Some(ReferenceCategory::Read)
-                },
-                _ => None
+                        Some(ReferenceCategory::READ)
+                    },
+                    _ => None,
+                }
             }
-        }
-    });
+        }).unwrap_or(ReferenceCategory::READ);
 
-        // Default Locals and Fields to read
-        mode.or(Some(ReferenceCategory::Read))
+        result | mode
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 0451546..6d0119f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -317,7 +317,8 @@
     #[test]
     fn mismatched_types_issue_15883() {
         // Check we don't panic.
-        check_diagnostics_no_bails(
+        cov_mark::check!(validate_match_bailed_out);
+        check_diagnostics(
             r#"
 //- minicore: option
 fn main() {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 7a03f17..41357b5 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -81,14 +81,15 @@
     let adt = d.receiver.strip_references().as_adt()?;
     let target_module = adt.module(ctx.sema.db);
 
-    let suggested_type =
-        if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) {
-            let display =
-                new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok();
-            make::ty(display.as_deref().unwrap_or("()"))
-        } else {
-            make::ty("()")
-        };
+    let suggested_type = if let Some(new_field_type) =
+        ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()).filter(|it| !it.is_unknown())
+    {
+        let display =
+            new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok();
+        make::ty(display.as_deref().unwrap_or("()"))
+    } else {
+        make::ty("()")
+    };
 
     if !is_editable_crate(target_module.krate(), ctx.sema.db) {
         return None;
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
index cd251fa..fdd4e86 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -1,9 +1,12 @@
+use hir::Name;
 use ide_db::{
     assists::{Assist, AssistId, AssistKind},
     base_db::FileRange,
     label::Label,
     source_change::SourceChange,
+    RootDatabase,
 };
+use syntax::TextRange;
 use text_edit::TextEdit;
 
 use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -21,7 +24,17 @@
         return None;
     }
     let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
-    let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string();
+    // The range for the Actual Name. We don't want to replace the entire declarition. Using the diagnostic range causes issues within in Array Destructuring.
+    let name_range = d
+        .local
+        .primary_source(ctx.sema.db)
+        .name()
+        .map(|v| v.syntax().original_file_range_rooted(ctx.sema.db))
+        .filter(|it| {
+            Some(it.file_id) == ast.file_id.file_id()
+                && diagnostic_range.range.contains_range(it.range)
+        });
+    let var_name = d.local.name(ctx.sema.db);
     Some(
         Diagnostic::new_with_syntax_node_ptr(
             ctx,
@@ -29,23 +42,36 @@
             "unused variable",
             ast,
         )
-        .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro()))
+        .with_fixes(name_range.and_then(|it| {
+            fixes(ctx.sema.db, var_name, it.range, diagnostic_range, ast.file_id.is_macro())
+        }))
         .experimental(),
     )
 }
 
-fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option<Vec<Assist>> {
+fn fixes(
+    db: &RootDatabase,
+    var_name: Name,
+    name_range: TextRange,
+    diagnostic_range: FileRange,
+    is_in_marco: bool,
+) -> Option<Vec<Assist>> {
     if is_in_marco {
         return None;
     }
+
     Some(vec![Assist {
         id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix),
-        label: Label::new(format!("Rename unused {} to _{}", var_name, var_name)),
+        label: Label::new(format!(
+            "Rename unused {} to _{}",
+            var_name.display(db),
+            var_name.display(db)
+        )),
         group: None,
         target: diagnostic_range.range,
         source_change: Some(SourceChange::from_text_edit(
             diagnostic_range.file_id,
-            TextEdit::replace(diagnostic_range.range, format!("_{}", var_name)),
+            TextEdit::replace(name_range, format!("_{}", var_name.display(db))),
         )),
         trigger_signature_help: false,
     }])
@@ -213,4 +239,21 @@
 "#,
         );
     }
+    #[test]
+    fn unused_variable_in_array_destructure() {
+        check_fix(
+            r#"
+fn main() {
+    let arr = [1, 2, 3, 4, 5];
+    let [_x, y$0 @ ..] = arr;
+}
+"#,
+            r#"
+fn main() {
+    let arr = [1, 2, 3, 4, 5];
+    let [_x, _y @ ..] = arr;
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 270cf84..c3ced36 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -320,13 +320,11 @@
     let module = sema.file_to_module_def(file_id);
 
     let ctx = DiagnosticsContext { config, sema, resolve };
-    if module.is_none() {
-        handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id);
-    }
 
     let mut diags = Vec::new();
-    if let Some(m) = module {
-        m.diagnostics(db, &mut diags, config.style_lints);
+    match module {
+        Some(m) => m.diagnostics(db, &mut diags, config.style_lints),
+        None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id),
     }
 
     for diag in diags {
@@ -409,6 +407,11 @@
         res.push(d)
     }
 
+    res.retain(|d| {
+        !(ctx.config.disabled.contains(d.code.as_str())
+            || ctx.config.disable_experimental && d.experimental)
+    });
+
     let mut diagnostics_of_range = res
         .iter_mut()
         .filter_map(|it| {
@@ -421,9 +424,14 @@
         })
         .collect::<FxHashMap<_, _>>();
 
+    if diagnostics_of_range.is_empty() {
+        return res;
+    }
+
     let mut rustc_stack: FxHashMap<String, Vec<Severity>> = FxHashMap::default();
     let mut clippy_stack: FxHashMap<String, Vec<Severity>> = FxHashMap::default();
 
+    // FIXME: This becomes quite expensive for big files
     handle_lint_attributes(
         &ctx.sema,
         parse.syntax(),
@@ -432,11 +440,7 @@
         &mut diagnostics_of_range,
     );
 
-    res.retain(|d| {
-        d.severity != Severity::Allow
-            && !ctx.config.disabled.contains(d.code.as_str())
-            && !(ctx.config.disable_experimental && d.experimental)
-    });
+    res.retain(|d| d.severity != Severity::Allow);
 
     res
 }
@@ -476,6 +480,7 @@
     clippy_stack: &mut FxHashMap<String, Vec<Severity>>,
     diagnostics_of_range: &mut FxHashMap<InFile<SyntaxNode>, &mut Diagnostic>,
 ) {
+    let _g = tracing::span!(tracing::Level::INFO, "handle_lint_attributes").entered();
     let file_id = sema.hir_file_for(root);
     let preorder = root.preorder();
     for ev in preorder {
@@ -486,24 +491,24 @@
                         stack.push(severity);
                     });
                 }
-                if let Some(x) =
+                if let Some(it) =
                     diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() })
                 {
                     const EMPTY_LINTS: &[&str] = &[];
-                    let (names, stack) = match x.code {
+                    let (names, stack) = match it.code {
                         DiagnosticCode::RustcLint(name) => (
-                            RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x),
+                            RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it),
                             &mut *rustc_stack,
                         ),
                         DiagnosticCode::Clippy(name) => (
-                            CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x),
+                            CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it),
                             &mut *clippy_stack,
                         ),
                         _ => continue,
                     };
                     for &name in names {
-                        if let Some(s) = stack.get(name).and_then(|x| x.last()) {
-                            x.severity = *s;
+                        if let Some(s) = stack.get(name).and_then(|it| it.last()) {
+                            it.severity = *s;
                         }
                     }
                 }
@@ -571,8 +576,8 @@
         if let Some(lint) = lint.as_single_name_ref() {
             job(rustc_stack.entry(lint.to_string()).or_default(), severity);
         }
-        if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) {
-            if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) {
+        if let Some(tool) = lint.qualifier().and_then(|it| it.as_single_name_ref()) {
+            if let Some(name_ref) = &lint.segment().and_then(|it| it.name_ref()) {
                 if tool.to_string() == "clippy" {
                     job(clippy_stack.entry(name_ref.to_string()).or_default(), severity);
                 }
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/fragments.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/fragments.rs
index 4d6809e..ca937a0 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/fragments.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/fragments.rs
@@ -27,7 +27,7 @@
 pub(crate) fn stmt(s: &str) -> Result<SyntaxNode, ()> {
     let template = "const _: () = { {}; };";
     let input = template.replace("{}", s);
-    let parse = syntax::SourceFile::parse(&input);
+    let parse = syntax::SourceFile::parse(&input, syntax::Edition::CURRENT);
     if !parse.errors().is_empty() {
         return Err(());
     }
@@ -48,7 +48,7 @@
 fn fragment<T: AstNode>(template: &str, s: &str) -> Result<SyntaxNode, ()> {
     let s = s.trim();
     let input = template.replace("{}", s);
-    let parse = syntax::SourceFile::parse(&input);
+    let parse = syntax::SourceFile::parse(&input, syntax::Edition::CURRENT);
     if !parse.errors().is_empty() {
         return Err(());
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs
index a090b60..2e7e230 100644
--- a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs
@@ -2,7 +2,7 @@
 //! We have to skip tests, so cannot reuse file_structure module.
 
 use hir::Semantics;
-use ide_assists::utils::test_related_attribute;
+use ide_assists::utils::test_related_attribute_syn;
 use ide_db::RootDatabase;
 use syntax::{ast, ast::HasName, AstNode, SyntaxNode, TextRange};
 
@@ -19,7 +19,7 @@
 
 fn method_range(item: SyntaxNode) -> Option<(TextRange, Option<TextRange>)> {
     ast::Fn::cast(item).and_then(|fn_def| {
-        if test_related_attribute(&fn_def).is_some() {
+        if test_related_attribute_syn(&fn_def).is_some() {
             None
         } else {
             Some((
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index 4b0961c..e2d629a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -177,7 +177,9 @@
     use ide_db::base_db::{FileLoader, SourceDatabase};
     // hack until we get hygiene working (same character amount to preserve formatting as much as possible)
     const DOLLAR_CRATE_REPLACE: &str = "__r_a_";
-    let expansion = expansion.replace("$crate", DOLLAR_CRATE_REPLACE);
+    const BUILTIN_REPLACE: &str = "builtin__POUND";
+    let expansion =
+        expansion.replace("$crate", DOLLAR_CRATE_REPLACE).replace("builtin #", BUILTIN_REPLACE);
     let (prefix, suffix) = match kind {
         SyntaxKind::MACRO_PAT => ("fn __(", ": u32);"),
         SyntaxKind::MACRO_EXPR | SyntaxKind::MACRO_STMTS => ("fn __() {", "}"),
@@ -206,7 +208,9 @@
     let captured_stdout = String::from_utf8(output.stdout).ok()?;
 
     if output.status.success() && !captured_stdout.trim().is_empty() {
-        let output = captured_stdout.replace(DOLLAR_CRATE_REPLACE, "$crate");
+        let output = captured_stdout
+            .replace(DOLLAR_CRATE_REPLACE, "$crate")
+            .replace(BUILTIN_REPLACE, "builtin #");
         let output = output.trim().strip_prefix(prefix)?;
         let output = match kind {
             SyntaxKind::MACRO_PAT => {
diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index 8136915..568906a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -220,7 +220,7 @@
     use super::*;
 
     fn check(ra_fixture: &str, expect: Expect) {
-        let file = SourceFile::parse(ra_fixture).ok().unwrap();
+        let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap();
         let structure = file_structure(&file);
         expect.assert_debug_eq(&structure)
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
index 2bc0721..c1b7693 100755
--- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
@@ -289,7 +289,7 @@
     fn check(ra_fixture: &str) {
         let (ranges, text) = extract_tags(ra_fixture, "fold");
 
-        let parse = SourceFile::parse(&text);
+        let parse = SourceFile::parse(&text, span::Edition::CURRENT);
         let mut folds = folding_ranges(&parse.tree());
         folds.sort_by_key(|fold| (fold.range.start(), fold.range.end()));
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index e20e0b6..6f32ce7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -26,7 +26,7 @@
     // FIXME: This needs to be more precise. Reference category makes sense only
     // for references, but we also have defs. And things like exit points are
     // neither.
-    pub category: Option<ReferenceCategory>,
+    pub category: ReferenceCategory,
 }
 
 #[derive(Default, Clone)]
@@ -113,7 +113,11 @@
                         range,
                         category,
                     });
-                let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
+                let category = if local.is_mut(sema.db) {
+                    ReferenceCategory::WRITE
+                } else {
+                    ReferenceCategory::empty()
+                };
                 local
                     .sources(sema.db)
                     .into_iter()
@@ -137,7 +141,9 @@
     {
         match resolution.map(Definition::from) {
             Some(def) => iter::once(def).collect(),
-            None => return Some(vec![HighlightedRange { range, category: None }]),
+            None => {
+                return Some(vec![HighlightedRange { range, category: ReferenceCategory::empty() }])
+            }
         }
     } else {
         find_defs(sema, token.clone())
@@ -211,7 +217,11 @@
         // highlight the defs themselves
         match def {
             Definition::Local(local) => {
-                let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
+                let category = if local.is_mut(sema.db) {
+                    ReferenceCategory::WRITE
+                } else {
+                    ReferenceCategory::empty()
+                };
                 local
                     .sources(sema.db)
                     .into_iter()
@@ -238,8 +248,11 @@
                         continue;
                     }
                     let hl_range = nav.focus_range.map(|range| {
-                        let category = matches!(def, Definition::Local(l) if l.is_mut(sema.db))
-                            .then_some(ReferenceCategory::Write);
+                        let category = if matches!(def, Definition::Local(l) if l.is_mut(sema.db)) {
+                            ReferenceCategory::WRITE
+                        } else {
+                            ReferenceCategory::empty()
+                        };
                         HighlightedRange { range, category }
                     });
                     if let Some(hl_range) = hl_range {
@@ -272,24 +285,30 @@
             def_ranges
                 .into_iter()
                 .flatten()
-                .map(|range| HighlightedRange { category: None, range }),
+                .map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }),
         );
         let body = body?;
         walk_expr(&body, &mut |expr| match expr {
             ast::Expr::ReturnExpr(expr) => {
                 if let Some(token) = expr.return_token() {
-                    highlights.push(HighlightedRange { category: None, range: token.text_range() });
+                    highlights.push(HighlightedRange {
+                        category: ReferenceCategory::empty(),
+                        range: token.text_range(),
+                    });
                 }
             }
             ast::Expr::TryExpr(try_) => {
                 if let Some(token) = try_.question_mark_token() {
-                    highlights.push(HighlightedRange { category: None, range: token.text_range() });
+                    highlights.push(HighlightedRange {
+                        category: ReferenceCategory::empty(),
+                        range: token.text_range(),
+                    });
                 }
             }
             ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) => {
                 if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) {
                     highlights.push(HighlightedRange {
-                        category: None,
+                        category: ReferenceCategory::empty(),
                         range: expr.syntax().text_range(),
                     });
                 }
@@ -309,7 +328,7 @@
                         .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()),
                     _ => tail.syntax().text_range(),
                 };
-                highlights.push(HighlightedRange { category: None, range })
+                highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range })
             });
         }
         Some(highlights)
@@ -354,7 +373,9 @@
             token.map(|tok| tok.text_range()),
             label.as_ref().map(|it| it.syntax().text_range()),
         );
-        highlights.extend(range.map(|range| HighlightedRange { category: None, range }));
+        highlights.extend(
+            range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }),
+        );
         for_each_break_and_continue_expr(label, body, &mut |expr| {
             let range: Option<TextRange> = match (cursor_token_kind, expr) {
                 (T![for] | T![while] | T![loop] | T![break], ast::Expr::BreakExpr(break_)) => {
@@ -372,7 +393,9 @@
                 ),
                 _ => None,
             };
-            highlights.extend(range.map(|range| HighlightedRange { category: None, range }));
+            highlights.extend(
+                range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }),
+            );
         });
         Some(highlights)
     }
@@ -430,14 +453,18 @@
         async_token: Option<SyntaxToken>,
         body: Option<ast::Expr>,
     ) -> Option<Vec<HighlightedRange>> {
-        let mut highlights =
-            vec![HighlightedRange { category: None, range: async_token?.text_range() }];
+        let mut highlights = vec![HighlightedRange {
+            category: ReferenceCategory::empty(),
+            range: async_token?.text_range(),
+        }];
         if let Some(body) = body {
             walk_expr(&body, &mut |expr| {
                 if let ast::Expr::AwaitExpr(expr) = expr {
                     if let Some(token) = expr.await_token() {
-                        highlights
-                            .push(HighlightedRange { category: None, range: token.text_range() });
+                        highlights.push(HighlightedRange {
+                            category: ReferenceCategory::empty(),
+                            range: token.text_range(),
+                        });
                     }
                 }
             });
@@ -481,6 +508,8 @@
 
 #[cfg(test)]
 mod tests {
+    use itertools::Itertools;
+
     use crate::fixture;
 
     use super::*;
@@ -504,28 +533,18 @@
 
         let hls = analysis.highlight_related(config, pos).unwrap().unwrap_or_default();
 
-        let mut expected = annotations
-            .into_iter()
-            .map(|(r, access)| (r.range, (!access.is_empty()).then_some(access)))
-            .collect::<Vec<_>>();
+        let mut expected =
+            annotations.into_iter().map(|(r, access)| (r.range, access)).collect::<Vec<_>>();
 
-        let mut actual = hls
+        let mut actual: Vec<(TextRange, String)> = hls
             .into_iter()
             .map(|hl| {
                 (
                     hl.range,
-                    hl.category.map(|it| {
-                        match it {
-                            ReferenceCategory::Read => "read",
-                            ReferenceCategory::Write => "write",
-                            ReferenceCategory::Import => "import",
-                            ReferenceCategory::Test => "test",
-                        }
-                        .to_owned()
-                    }),
+                    hl.category.iter_names().map(|(name, _flag)| name.to_lowercase()).join(","),
                 )
             })
-            .collect::<Vec<_>>();
+            .collect();
         actual.sort_by_key(|(range, _)| range.start());
         expected.sort_by_key(|(range, _)| range.start());
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 822751c..95de3c8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -14,7 +14,7 @@
     helpers::pick_best_token,
     FxIndexSet, RootDatabase,
 };
-use itertools::Itertools;
+use itertools::{multizip, Itertools};
 use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, T};
 
 use crate::{
@@ -149,7 +149,7 @@
     if let Some(doc_comment) = token_as_doc_comment(&original_token) {
         cov_mark::hit!(no_highlight_on_comment_hover);
         return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
-            let res = hover_for_definition(sema, file_id, def, &node, config);
+            let res = hover_for_definition(sema, file_id, def, &node, None, config);
             Some(RangeInfo::new(range, res))
         });
     }
@@ -162,6 +162,7 @@
             file_id,
             Definition::from(resolution?),
             &original_token.parent()?,
+            None,
             config,
         );
         return Some(RangeInfo::new(range, res));
@@ -196,6 +197,29 @@
             descended()
                 .filter_map(|token| {
                     let node = token.parent()?;
+
+                    // special case macro calls, we wanna render the invoked arm index
+                    if let Some(name) = ast::NameRef::cast(node.clone()) {
+                        if let Some(path_seg) =
+                            name.syntax().parent().and_then(ast::PathSegment::cast)
+                        {
+                            if let Some(macro_call) = path_seg
+                                .parent_path()
+                                .syntax()
+                                .parent()
+                                .and_then(ast::MacroCall::cast)
+                            {
+                                if let Some(macro_) = sema.resolve_macro_call(&macro_call) {
+                                    return Some(vec![(
+                                        Definition::Macro(macro_),
+                                        sema.resolve_macro_call_arm(&macro_call),
+                                        node,
+                                    )]);
+                                }
+                            }
+                        }
+                    }
+
                     match IdentClass::classify_node(sema, &node)? {
                         // It's better for us to fall back to the keyword hover here,
                         // rendering poll is very confusing
@@ -204,20 +228,19 @@
                         IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand {
                             decl,
                             ..
-                        }) => Some(vec![(Definition::ExternCrateDecl(decl), node)]),
+                        }) => Some(vec![(Definition::ExternCrateDecl(decl), None, node)]),
 
                         class => Some(
-                            class
-                                .definitions()
-                                .into_iter()
-                                .zip(iter::repeat(node))
+                            multizip((class.definitions(), iter::repeat(None), iter::repeat(node)))
                                 .collect::<Vec<_>>(),
                         ),
                     }
                 })
                 .flatten()
-                .unique_by(|&(def, _)| def)
-                .map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
+                .unique_by(|&(def, _, _)| def)
+                .map(|(def, macro_arm, node)| {
+                    hover_for_definition(sema, file_id, def, &node, macro_arm, config)
+                })
                 .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
                     acc.actions.extend(actions);
                     acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup));
@@ -374,6 +397,7 @@
     file_id: FileId,
     def: Definition,
     scope_node: &SyntaxNode,
+    macro_arm: Option<u32>,
     config: &HoverConfig,
 ) -> HoverResult {
     let famous_defs = match &def {
@@ -398,7 +422,8 @@
     };
     let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
 
-    let markup = render::definition(sema.db, def, famous_defs.as_ref(), &notable_traits, config);
+    let markup =
+        render::definition(sema.db, def, famous_defs.as_ref(), &notable_traits, macro_arm, config);
     HoverResult {
         markup: render::process_markup(sema.db, def, &markup, config),
         actions: [
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index abedbff..3f0fc85 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -101,7 +101,7 @@
                 if let Some((inner, body)) = error_type_args {
                     inner_ty = inner;
                     body_ty = body;
-                    s = "Try Error".to_owned();
+                    "Try Error".clone_into(&mut s);
                 }
             }
         }
@@ -403,6 +403,7 @@
     def: Definition,
     famous_defs: Option<&FamousDefs<'_, '_>>,
     notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
+    macro_arm: Option<u32>,
     config: &HoverConfig,
 ) -> Markup {
     let mod_path = definition_mod_path(db, &def);
@@ -413,6 +414,13 @@
         Definition::Adt(Adt::Struct(struct_)) => {
             struct_.display_limited(db, config.max_struct_field_count).to_string()
         }
+        Definition::Macro(it) => {
+            let mut label = it.display(db).to_string();
+            if let Some(macro_arm) = macro_arm {
+                format_to!(label, " // matched arm #{}", macro_arm);
+            }
+            label
+        }
         _ => def.label(db),
     };
     let docs = def.docs(db, famous_defs);
@@ -637,7 +645,7 @@
         })
         .join("\n");
     if captures_rendered.trim().is_empty() {
-        captures_rendered = "This closure captures nothing".to_owned();
+        "This closure captures nothing".clone_into(&mut captures_rendered);
     }
     let mut targets: Vec<hir::ModuleDef> = Vec::new();
     let mut push_new_def = |item: hir::ModuleDef| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 08925fc..6bbc8b3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -1560,21 +1560,21 @@
 fn test_hover_macro_invocation() {
     check(
         r#"
-macro_rules! foo { () => {} }
+macro_rules! foo { (a) => {}; () => {} }
 
 fn f() { fo$0o!(); }
 "#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                macro_rules! foo
-                ```
-            "#]],
+            ```rust
+            macro_rules! foo // matched arm #1
+            ```
+        "#]],
     )
 }
 
@@ -1590,22 +1590,22 @@
 fn f() { fo$0o!(); }
 "#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                test
-                ```
+            ```rust
+            test
+            ```
 
-                ```rust
-                macro foo
-                ```
+            ```rust
+            macro foo // matched arm #0
+            ```
 
-                ---
+            ---
 
-                foo bar
+            foo bar
 
-                foo bar baz
-            "#]],
+            foo bar baz
+        "#]],
     )
 }
 
@@ -2323,6 +2323,49 @@
 }
 
 #[test]
+fn test_hover_layout_of_variant_generic() {
+    check(
+        r#"enum Option<T> {
+    Some(T),
+    None$0
+}"#,
+        expect![[r#"
+            *None*
+
+            ```rust
+            test::Option
+            ```
+
+            ```rust
+            None
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn test_hover_layout_generic_unused() {
+    check(
+        r#"
+//- minicore: phantom_data
+struct S$0<T>(core::marker::PhantomData<T>);
+"#,
+        expect![[r#"
+            *S*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 0, align = 1
+            struct S<T>(PhantomData<T>)
+            ```
+        "#]],
+    );
+}
+
+#[test]
 fn test_hover_layout_of_enum() {
     check(
         r#"enum $0Foo {
@@ -3258,12 +3301,12 @@
 fn test_hover_dyn_return_has_goto_type_action() {
     check_actions(
         r#"
-trait Foo {}
+trait Foo<T> {}
 struct S;
-impl Foo for S {}
+impl Foo<S> for S {}
 
 struct B<T>{}
-fn foo() -> B<dyn Foo> {}
+fn foo() -> B<dyn Foo<S>> {}
 
 fn main() { let s$0t = foo(); }
 "#,
@@ -3277,8 +3320,8 @@
                                 file_id: FileId(
                                     0,
                                 ),
-                                full_range: 42..55,
-                                focus_range: 49..50,
+                                full_range: 48..61,
+                                focus_range: 55..56,
                                 name: "B",
                                 kind: Struct,
                                 description: "struct B<T>",
@@ -3290,11 +3333,24 @@
                                 file_id: FileId(
                                     0,
                                 ),
-                                full_range: 0..12,
+                                full_range: 0..15,
                                 focus_range: 6..9,
                                 name: "Foo",
                                 kind: Trait,
-                                description: "trait Foo",
+                                description: "trait Foo<T>",
+                            },
+                        },
+                        HoverGotoTypeData {
+                            mod_path: "test::S",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 16..25,
+                                focus_range: 23..24,
+                                name: "S",
+                                kind: Struct,
+                                description: "struct S",
                             },
                         },
                     ],
@@ -3673,6 +3729,7 @@
             ```
 
             ```rust
+            // size = 0, align = 1
             struct ST<const C: usize = 1, T = Foo>(T)
             ```
         "#]],
@@ -3694,6 +3751,7 @@
             ```
 
             ```rust
+            // size = 0, align = 1
             struct ST<const C: usize = {const}, T = Foo>(T)
             ```
         "#]],
@@ -3716,6 +3774,7 @@
             ```
 
             ```rust
+            // size = 0, align = 1
             struct ST<const C: usize = VAL, T = Foo>(T)
             ```
         "#]],
@@ -4040,7 +4099,6 @@
             ```
         "#]],
     );
-    // lifetimes bounds arent being tracked yet
     check(
         r#"
 //- minicore: sized
@@ -4051,7 +4109,7 @@
             *T*
 
             ```rust
-            T
+            T: 'static
             ```
         "#]],
     );
@@ -4215,6 +4273,10 @@
                 ```
             "#]],
         );
+    }
+
+    #[test]
+    fn mixed2() {
         check(
             r#"
 //- minicore: sized
@@ -7873,8 +7935,44 @@
             ```
 
             ```rust
+            // size = 16 (0x10), align = 8, niches = 1
             struct Pedro<'a>
             ```
         "#]],
     )
 }
+
+#[test]
+fn hover_impl_trait_arg_self() {
+    check(
+        r#"
+trait T<Rhs = Self> {}
+fn main(a$0: impl T) {}
+"#,
+        expect![[r#"
+            *a*
+
+            ```rust
+            a: impl T + ?Sized
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_struct_default_arg_self() {
+    check(
+        r#"
+struct T<Rhs = Self> {}
+fn main(a$0: T) {}
+"#,
+        expect![[r#"
+            *a*
+
+            ```rust
+            // size = 0, align = 1
+            a: T
+            ```
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index dda38ce..15eecd1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -1,6 +1,5 @@
 use std::{
     fmt::{self, Write},
-    hash::{BuildHasher, BuildHasherDefault},
     mem::take,
 };
 
@@ -9,7 +8,7 @@
     known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
     ModuleDefId, Semantics,
 };
-use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase};
+use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
 use itertools::Itertools;
 use smallvec::{smallvec, SmallVec};
 use stdx::never;
@@ -495,6 +494,7 @@
     position: TextSize,
     hash: u64,
     config: &InlayHintsConfig,
+    hasher: impl Fn(&InlayHint) -> u64,
 ) -> Option<InlayHint> {
     let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered();
     let sema = Semantics::new(db);
@@ -506,20 +506,16 @@
     let mut acc = Vec::new();
 
     let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
-    match file.token_at_offset(position).left_biased() {
-        Some(token) => {
-            if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) {
-                parent_block.syntax().descendants().for_each(hints)
-            } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) {
-                parent_item.syntax().descendants().for_each(hints)
-            } else {
-                return None;
-            }
-        }
-        None => return None,
+    let token = file.token_at_offset(position).left_biased()?;
+    if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) {
+        parent_block.syntax().descendants().for_each(hints)
+    } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) {
+        parent_item.syntax().descendants().for_each(hints)
+    } else {
+        return None;
     }
 
-    acc.into_iter().find(|hint| BuildHasherDefault::<FxHasher>::default().hash_one(hint) == hash)
+    acc.into_iter().find(|hint| hasher(hint) == hash)
 }
 
 fn hints(
diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
index 815a4ba..9d8ba90 100644
--- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
@@ -316,7 +316,7 @@
         };
 
         let (before_cursor_pos, before) = extract_offset(ra_fixture_before);
-        let file = SourceFile::parse(&before).ok().unwrap();
+        let file = SourceFile::parse(&before, span::Edition::CURRENT).ok().unwrap();
 
         let range = TextRange::empty(before_cursor_pos);
         let result = join_lines(&config, &file, range);
@@ -342,7 +342,7 @@
         };
 
         let (sel, before) = extract_range(ra_fixture_before);
-        let parse = SourceFile::parse(&before);
+        let parse = SourceFile::parse(&before, span::Edition::CURRENT);
         let result = join_lines(&config, &parse.tree(), sel);
         let actual = {
             let mut actual = before;
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index ad48d80..431aa30 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -58,13 +58,15 @@
 mod view_memory_layout;
 mod view_mir;
 
+use std::panic::UnwindSafe;
+
 use cfg::CfgOptions;
 use fetch_crates::CrateInfo;
 use hir::ChangeWithProcMacros;
 use ide_db::{
     base_db::{
         salsa::{self, ParallelDatabase},
-        CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
+        CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, SourceDatabaseExt, VfsPath,
     },
     prime_caches, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase,
 };
@@ -252,7 +254,7 @@
             Edition::CURRENT,
             None,
             None,
-            cfg_options.clone(),
+            Arc::new(cfg_options),
             None,
             Env::default(),
             false,
@@ -271,6 +273,10 @@
         self.with_db(|db| status::status(db, file_id))
     }
 
+    pub fn source_root(&self, file_id: FileId) -> Cancellable<SourceRootId> {
+        self.with_db(|db| db.file_source_root(file_id))
+    }
+
     pub fn parallel_prime_caches<F>(&self, num_worker_threads: u8, cb: F) -> Cancellable<()>
     where
         F: Fn(ParallelPrimeCachesProgress) + Sync + std::panic::UnwindSafe,
@@ -280,7 +286,7 @@
 
     /// Gets the text of the source file.
     pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
-        self.with_db(|db| db.file_text(file_id))
+        self.with_db(|db| SourceDatabaseExt::file_text(db, file_id))
     }
 
     /// Gets the syntax tree of the file.
@@ -290,7 +296,6 @@
 
     /// Returns true if this file belongs to an immutable library.
     pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> {
-        use ide_db::base_db::SourceDatabaseExt;
         self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
     }
 
@@ -428,8 +433,11 @@
         file_id: FileId,
         position: TextSize,
         hash: u64,
+        hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe,
     ) -> Cancellable<Option<InlayHint>> {
-        self.with_db(|db| inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config))
+        self.with_db(|db| {
+            inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config, hasher)
+        })
     }
 
     /// Returns the set of folding ranges.
diff --git a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs
index 6e8a6d0..5735615 100644
--- a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs
@@ -50,7 +50,7 @@
     fn test_matching_brace() {
         fn do_check(before: &str, after: &str) {
             let (pos, before) = extract_offset(before);
-            let parse = SourceFile::parse(&before);
+            let parse = SourceFile::parse(&before, span::Edition::CURRENT);
             let new_pos = match matching_brace(&parse.tree(), pos) {
                 None => pos,
                 Some(pos) => pos,
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index fef2aba..01af864 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -30,7 +30,7 @@
 #[derive(Debug, Clone)]
 pub struct ReferenceSearchResult {
     pub declaration: Option<Declaration>,
-    pub references: IntMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
+    pub references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>>,
 }
 
 #[derive(Debug, Clone)]
@@ -66,7 +66,7 @@
                 retain_adt_literal_usages(&mut usages, def, sema);
             }
 
-            let mut references = usages
+            let mut references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>> = usages
                 .into_iter()
                 .map(|(file_id, refs)| {
                     (
@@ -77,7 +77,7 @@
                             .collect(),
                     )
                 })
-                .collect::<IntMap<_, Vec<_>>>();
+                .collect();
             let declaration = match def {
                 Definition::Module(module) => {
                     Some(NavigationTarget::from_module_to_decl(sema.db, module))
@@ -93,7 +93,7 @@
                     references
                         .entry(extra_ref.file_id)
                         .or_default()
-                        .push((extra_ref.focus_or_full_range(), None));
+                        .push((extra_ref.focus_or_full_range(), ReferenceCategory::empty()));
                 }
                 Declaration {
                     is_mut: matches!(def, Definition::Local(l) if l.is_mut(sema.db)),
@@ -300,7 +300,7 @@
 #[cfg(test)]
 mod tests {
     use expect_test::{expect, Expect};
-    use ide_db::{base_db::FileId, search::ReferenceCategory};
+    use ide_db::base_db::FileId;
     use stdx::format_to;
 
     use crate::{fixture, SearchScope};
@@ -324,7 +324,7 @@
                 test_func Function FileId(0) 0..17 3..12
 
                 FileId(0) 35..44
-                FileId(0) 75..84 Test
+                FileId(0) 75..84 test
             "#]],
         );
 
@@ -345,7 +345,28 @@
                 test_func Function FileId(0) 0..17 3..12
 
                 FileId(0) 35..44
-                FileId(0) 96..105 Test
+                FileId(0) 96..105 test
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_access() {
+        check(
+            r#"
+struct S { f$0: u32 }
+
+#[test]
+fn test() {
+    let mut x = S { f: 92 };
+    x.f = 92;
+}
+"#,
+            expect![[r#"
+                f Field FileId(0) 11..17 11..12
+
+                FileId(0) 61..62 read test
+                FileId(0) 76..77 write test
             "#]],
         );
     }
@@ -600,12 +621,12 @@
     i = 5;
 }"#,
             expect![[r#"
-                i Local FileId(0) 20..25 24..25 Write
+                i Local FileId(0) 20..25 24..25 write
 
-                FileId(0) 50..51 Write
-                FileId(0) 54..55 Read
-                FileId(0) 76..77 Write
-                FileId(0) 94..95 Write
+                FileId(0) 50..51 write
+                FileId(0) 54..55 read
+                FileId(0) 76..77 write
+                FileId(0) 94..95 write
             "#]],
         );
     }
@@ -626,8 +647,8 @@
             expect![[r#"
                 spam Local FileId(0) 19..23 19..23
 
-                FileId(0) 34..38 Read
-                FileId(0) 41..45 Read
+                FileId(0) 34..38 read
+                FileId(0) 41..45 read
             "#]],
         );
     }
@@ -641,7 +662,7 @@
             expect![[r#"
                 i ValueParam FileId(0) 7..8 7..8
 
-                FileId(0) 25..26 Read
+                FileId(0) 25..26 read
             "#]],
         );
     }
@@ -655,7 +676,7 @@
             expect![[r#"
                 i ValueParam FileId(0) 7..8 7..8
 
-                FileId(0) 25..26 Read
+                FileId(0) 25..26 read
             "#]],
         );
     }
@@ -676,7 +697,7 @@
             expect![[r#"
                 spam Field FileId(0) 17..30 21..25
 
-                FileId(0) 67..71 Read
+                FileId(0) 67..71 read
             "#]],
         );
     }
@@ -824,7 +845,7 @@
             expect![[r#"
                 foo Module FileId(0) 0..8 4..7
 
-                FileId(0) 14..17 Import
+                FileId(0) 14..17 import
             "#]],
         );
     }
@@ -842,7 +863,7 @@
             expect![[r#"
                 foo Module FileId(0) 0..8 4..7
 
-                FileId(1) 4..8 Import
+                FileId(1) 4..8 import
             "#]],
         );
     }
@@ -857,7 +878,7 @@
             expect![[r#"
                 Module FileId(0) 0..10
 
-                FileId(0) 4..8 Import
+                FileId(0) 4..8 import
             "#]],
         );
     }
@@ -885,7 +906,7 @@
             expect![[r#"
                 Foo Struct FileId(2) 0..41 18..21 some
 
-                FileId(1) 20..23 Import
+                FileId(1) 20..23 import
                 FileId(1) 47..50
             "#]],
         );
@@ -960,10 +981,10 @@
 }
 "#,
             expect![[r#"
-                i Local FileId(0) 19..24 23..24 Write
+                i Local FileId(0) 19..24 23..24 write
 
-                FileId(0) 34..35 Write
-                FileId(0) 38..39 Read
+                FileId(0) 34..35 write
+                FileId(0) 38..39 read
             "#]],
         );
     }
@@ -984,8 +1005,8 @@
             expect![[r#"
                 f Field FileId(0) 15..21 15..16
 
-                FileId(0) 55..56 Read
-                FileId(0) 68..69 Write
+                FileId(0) 55..56 read
+                FileId(0) 68..69 write
             "#]],
         );
     }
@@ -1002,7 +1023,7 @@
             expect![[r#"
                 i Local FileId(0) 19..20 19..20
 
-                FileId(0) 26..27 Write
+                FileId(0) 26..27 write
             "#]],
         );
     }
@@ -1048,7 +1069,7 @@
             expect![[r#"
                 f Function FileId(0) 22..31 25..26
 
-                FileId(1) 11..12 Import
+                FileId(1) 11..12 import
                 FileId(1) 24..25
             "#]],
         );
@@ -1071,7 +1092,7 @@
             expect![[r#"
                 field Field FileId(0) 15..24 15..20
 
-                FileId(0) 68..73 Read
+                FileId(0) 68..73 read
             "#]],
         );
     }
@@ -1095,7 +1116,7 @@
             expect![[r#"
                 field Field FileId(0) 32..41 32..37
 
-                FileId(0) 102..107 Read
+                FileId(0) 102..107 read
             "#]],
         );
     }
@@ -1119,7 +1140,7 @@
             expect![[r#"
                 field Field FileId(0) 56..65 56..61
 
-                FileId(0) 125..130 Read
+                FileId(0) 125..130 read
             "#]],
         );
     }
@@ -1144,8 +1165,8 @@
             expect![[r#"
                 self SelfParam FileId(0) 47..51 47..51
 
-                FileId(0) 71..75 Read
-                FileId(0) 152..156 Read
+                FileId(0) 71..75 read
+                FileId(0) 152..156 read
             "#]],
         );
     }
@@ -1165,7 +1186,7 @@
             expect![[r#"
                 self SelfParam FileId(0) 47..51 47..51
 
-                FileId(0) 63..67 Read
+                FileId(0) 63..67 read
             "#]],
         );
     }
@@ -1185,16 +1206,16 @@
             if let Some(decl) = refs.declaration {
                 format_to!(actual, "{}", decl.nav.debug_render());
                 if decl.is_mut {
-                    format_to!(actual, " {:?}", ReferenceCategory::Write)
+                    format_to!(actual, " write",)
                 }
                 actual += "\n\n";
             }
 
             for (file_id, references) in &refs.references {
-                for (range, access) in references {
+                for (range, category) in references {
                     format_to!(actual, "{:?} {:?}", file_id, range);
-                    if let Some(access) = access {
-                        format_to!(actual, " {:?}", access);
+                    for (name, _flag) in category.iter_names() {
+                        format_to!(actual, " {}", name.to_lowercase());
                     }
                     actual += "\n";
                 }
@@ -1281,7 +1302,7 @@
             expect![[r#"
                 a Local FileId(0) 59..60 59..60
 
-                FileId(0) 80..81 Read
+                FileId(0) 80..81 read
             "#]],
         );
     }
@@ -1299,7 +1320,7 @@
             expect![[r#"
                 a Local FileId(0) 59..60 59..60
 
-                FileId(0) 80..81 Read
+                FileId(0) 80..81 read
             "#]],
         );
     }
@@ -1479,7 +1500,7 @@
             expect![[r#"
                 test Function FileId(0) 0..33 11..15
 
-                FileId(0) 24..28 Test
+                FileId(0) 24..28 test
             "#]],
         );
     }
@@ -1538,9 +1559,9 @@
             expect![[r#"
                 Foo Struct FileId(0) 0..15 11..14
 
-                FileId(1) 16..19 Import
-                FileId(2) 16..19 Import
-                FileId(3) 16..19 Import
+                FileId(1) 16..19 import
+                FileId(2) 16..19 import
+                FileId(3) 16..19 import
             "#]],
         );
     }
@@ -1568,7 +1589,7 @@
             expect![[r#"
                 foo Macro FileId(1) 0..61 29..32
 
-                FileId(0) 46..49 Import
+                FileId(0) 46..49 import
                 FileId(2) 0..3
                 FileId(3) 5..8
             "#]],
@@ -1731,7 +1752,7 @@
             expect![[r#"
                 derive_identity Derive FileId(2) 1..107 45..60
 
-                FileId(0) 17..31 Import
+                FileId(0) 17..31 import
                 FileId(0) 56..70
             "#]],
         );
@@ -2055,7 +2076,7 @@
             expect![[r#"
                 method Field FileId(0) 60..70 60..66
 
-                FileId(0) 136..142 Read
+                FileId(0) 136..142 read
             "#]],
         );
         check(
@@ -2101,7 +2122,7 @@
             expect![[r#"
                 method Field FileId(0) 60..70 60..66
 
-                FileId(0) 136..142 Read
+                FileId(0) 136..142 read
             "#]],
         );
         check(
@@ -2160,9 +2181,9 @@
             expect![[r#"
                 a Local FileId(0) 20..21 20..21
 
-                FileId(0) 56..57 Read
-                FileId(0) 60..61 Read
-                FileId(0) 68..69 Read
+                FileId(0) 56..57 read
+                FileId(0) 60..61 read
+                FileId(0) 68..69 read
             "#]],
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 79324bf..b6c6753 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -1,9 +1,11 @@
 use std::fmt;
 
 use ast::HasName;
-use cfg::CfgExpr;
-use hir::{db::HirDatabase, AsAssocItem, HasAttrs, HasSource, HirFileIdExt, Semantics};
-use ide_assists::utils::test_related_attribute;
+use cfg::{CfgAtom, CfgExpr};
+use hir::{
+    db::HirDatabase, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics,
+};
+use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
 use ide_db::{
     base_db::{FilePosition, FileRange},
     defs::Definition,
@@ -280,7 +282,7 @@
 }
 
 fn as_test_runnable(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> {
-    if test_related_attribute(fn_def).is_some() {
+    if test_related_attribute_syn(fn_def).is_some() {
         let function = sema.to_def(fn_def)?;
         runnable_fn(sema, function)
     } else {
@@ -293,7 +295,7 @@
         let module = ast::Module::cast(node)?;
         let module = sema.to_def(&module)?;
 
-        if has_test_function_or_multiple_test_submodules(sema, &module) {
+        if has_test_function_or_multiple_test_submodules(sema, &module, false) {
             Some(module)
         } else {
             None
@@ -305,7 +307,8 @@
     sema: &Semantics<'_, RootDatabase>,
     def: hir::Function,
 ) -> Option<Runnable> {
-    let kind = if def.is_main(sema.db) {
+    let under_cfg_test = has_cfg_test(def.module(sema.db).attrs(sema.db));
+    let kind = if !under_cfg_test && def.is_main(sema.db) {
         RunnableKind::Bin
     } else {
         let test_id = || {
@@ -342,7 +345,8 @@
     sema: &Semantics<'_, RootDatabase>,
     def: hir::Module,
 ) -> Option<Runnable> {
-    if !has_test_function_or_multiple_test_submodules(sema, &def) {
+    if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db)))
+    {
         return None;
     }
     let path = def
@@ -384,12 +388,17 @@
     Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg })
 }
 
+fn has_cfg_test(attrs: AttrsWithOwner) -> bool {
+    attrs.cfgs().any(|cfg| matches!(cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if s == "test"))
+}
+
 /// Creates a test mod runnable for outline modules at the top of their definition.
 fn runnable_mod_outline_definition(
     sema: &Semantics<'_, RootDatabase>,
     def: hir::Module,
 ) -> Option<Runnable> {
-    if !has_test_function_or_multiple_test_submodules(sema, &def) {
+    if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db)))
+    {
         return None;
     }
     let path = def
@@ -522,20 +531,28 @@
 fn has_test_function_or_multiple_test_submodules(
     sema: &Semantics<'_, RootDatabase>,
     module: &hir::Module,
+    consider_exported_main: bool,
 ) -> bool {
     let mut number_of_test_submodules = 0;
 
     for item in module.declarations(sema.db) {
         match item {
             hir::ModuleDef::Function(f) => {
-                if let Some(it) = f.source(sema.db) {
-                    if test_related_attribute(&it.value).is_some() {
-                        return true;
-                    }
+                if has_test_related_attribute(&f.attrs(sema.db)) {
+                    return true;
+                }
+                if consider_exported_main && f.exported_main(sema.db) {
+                    // an exported main in a test module can be considered a test wrt to custom test
+                    // runners
+                    return true;
                 }
             }
             hir::ModuleDef::Module(submodule) => {
-                if has_test_function_or_multiple_test_submodules(sema, &submodule) {
+                if has_test_function_or_multiple_test_submodules(
+                    sema,
+                    &submodule,
+                    consider_exported_main,
+                ) {
                     number_of_test_submodules += 1;
                 }
             }
@@ -1484,4 +1501,39 @@
             "#]],
         )
     }
+
+    #[test]
+    fn exported_main_is_test_in_cfg_test_mod() {
+        check(
+            r#"
+//- /lib.rs crate:foo cfg:test
+$0
+mod not_a_test_module_inline {
+    #[export_name = "main"]
+    fn exp_main() {}
+}
+#[cfg(test)]
+mod test_mod_inline {
+    #[export_name = "main"]
+    fn exp_main() {}
+}
+mod not_a_test_module;
+#[cfg(test)]
+mod test_mod;
+//- /not_a_test_module.rs
+#[export_name = "main"]
+fn exp_main() {}
+//- /test_mod.rs
+#[export_name = "main"]
+fn exp_main() {}
+"#,
+            expect![[r#"
+                [
+                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 36..80, focus_range: 67..75, name: \"exp_main\", kind: Function })",
+                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 83..168, focus_range: 100..115, name: \"test_mod_inline\", kind: Module, description: \"mod test_mod_inline\" }, Atom(Flag(\"test\")))",
+                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 192..218, focus_range: 209..217, name: \"test_mod\", kind: Module, description: \"mod test_mod\" }, Atom(Flag(\"test\")))",
+                ]
+            "#]],
+        )
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index 3fef16d..ca013da 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -188,7 +188,14 @@
             } else {
                 let it = self.tokens.insert(TokenStaticData {
                     documentation: documentation_for_definition(&sema, def, &node),
-                    hover: Some(hover_for_definition(&sema, file_id, def, &node, &hover_config)),
+                    hover: Some(hover_for_definition(
+                        &sema,
+                        file_id,
+                        def,
+                        &node,
+                        None,
+                        &hover_config,
+                    )),
                     definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| {
                         FileRange { file_id: it.file_id, range: it.focus_or_full_range() }
                     }),
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index e7346cb..a72f505 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -444,7 +444,6 @@
         Definition::Variant(_) => Highlight::new(HlTag::Symbol(SymbolKind::Variant)),
         Definition::Const(konst) => {
             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)) | HlMod::Const;
-
             if let Some(item) = konst.as_assoc_item(db) {
                 h |= HlMod::Associated;
                 h |= HlMod::Static;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
index 5c7a463..e329023 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
@@ -77,6 +77,7 @@
     Library,
     /// Used to differentiate individual elements within macro calls.
     Macro,
+    /// Used to differentiate individual elements within proc-macro calls.
     ProcMacro,
     /// Mutable binding.
     Mutable,
@@ -113,7 +114,7 @@
     Semi,
     /// ! (only for macro calls)
     MacroBang,
-    ///
+    /// Other punctutations
     Other,
 }
 
@@ -127,7 +128,7 @@
     Logical,
     /// >, <, ==, >=, <=, !=
     Comparison,
-    ///
+    /// Other operators
     Other,
 }
 
@@ -225,8 +226,8 @@
         HlMod::IntraDocLink,
         HlMod::Library,
         HlMod::Macro,
-        HlMod::ProcMacro,
         HlMod::Mutable,
+        HlMod::ProcMacro,
         HlMod::Public,
         HlMod::Reference,
         HlMod::Static,
@@ -262,6 +263,7 @@
     }
 
     fn mask(self) -> u32 {
+        debug_assert!(Self::ALL.len() <= 32, "HlMod::mask is not enough to cover all variants");
         1 << (self as u32)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 7ba1194..5234d36 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -218,7 +218,7 @@
         <span class="bool_literal">true</span>
     <span class="brace">}</span>
 <span class="brace">}</span>
-<span class="keyword const">const</span> <span class="constant const declaration">USAGE_OF_BOOL</span><span class="colon">:</span><span class="builtin_type">bool</span> <span class="operator">=</span> <span class="enum public">Bool</span><span class="operator">::</span><span class="enum_variant public">True</span><span class="operator">.</span><span class="method consuming public">to_primitive</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="keyword const">const</span> <span class="constant const declaration">USAGE_OF_BOOL</span><span class="colon">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="enum public">Bool</span><span class="operator">::</span><span class="enum_variant public">True</span><span class="operator">.</span><span class="method consuming public">to_primitive</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">Baz</span> <span class="brace">{</span>
     <span class="keyword">type</span> <span class="type_alias associated declaration static trait">Qux</span><span class="semicolon">;</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index c2990fd..901e41d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -300,7 +300,7 @@
         true
     }
 }
-const USAGE_OF_BOOL:bool = Bool::True.to_primitive();
+const USAGE_OF_BOOL: bool = Bool::True.to_primitive();
 
 trait Baz {
     type Qux;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
index 1065d58..05cdf43 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
@@ -88,7 +88,7 @@
         // Remove custom markers
         .replace("$0", "");
 
-    let parsed = SourceFile::parse(&text);
+    let parsed = SourceFile::parse(&text, span::Edition::CURRENT);
 
     // If the "file" parsed without errors,
     // return its syntax
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index d3eee0e..b899304 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -127,7 +127,8 @@
     if !stdx::always!(range.len() == TextSize::of(opening_bracket)) {
         return None;
     }
-    let file = file.reparse(&Indel::delete(range));
+    // FIXME: Edition
+    let file = file.reparse(&Indel::delete(range), span::Edition::CURRENT);
 
     if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) {
         return Some(edit);
@@ -411,7 +412,7 @@
         let (offset, mut before) = extract_offset(before);
         let edit = TextEdit::insert(offset, char_typed.to_string());
         edit.apply(&mut before);
-        let parse = SourceFile::parse(&before);
+        let parse = SourceFile::parse(&before, span::Edition::CURRENT);
         on_char_typed_inner(&parse, offset, char_typed).map(|it| {
             it.apply(&mut before);
             before.to_string()
diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs
index d784321..40d18b1 100644
--- a/src/tools/rust-analyzer/crates/intern/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs
@@ -174,6 +174,7 @@
     map: OnceLock<InternMap<T>>,
 }
 
+#[allow(clippy::new_without_default)] // this a const fn, so it can't be default
 impl<T: ?Sized> InternStorage<T> {
     pub const fn new() -> Self {
         Self { map: OnceLock::new() }
diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml
index c89722c..c1a7688 100644
--- a/src/tools/rust-analyzer/crates/limit/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/limit/Cargo.toml
@@ -10,7 +10,6 @@
 
 [features]
 tracking = []
-default = ["tracking"]
 
 [lints]
-workspace = true
\ No newline at end of file
+workspace = true
diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml
index 48e84a7..b6f90ec 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml
@@ -28,5 +28,8 @@
 vfs-notify.workspace = true
 vfs.workspace = true
 
+[features]
+in-rust-tree = ["hir-expand/in-rust-tree"]
+
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index 79d6fe3..31b0c8c 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -335,7 +335,7 @@
 ) -> RootDatabase {
     let (ProjectWorkspace::Cargo { toolchain, target_layout, .. }
     | ProjectWorkspace::Json { toolchain, target_layout, .. }
-    | ProjectWorkspace::DetachedFiles { toolchain, target_layout, .. }) = ws;
+    | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws;
 
     let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
     let mut db = RootDatabase::new(lru_cap);
@@ -407,8 +407,7 @@
         call_site: Span,
         mixed_site: Span,
     ) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
-        let env = env.iter().map(|(k, v)| (k.to_owned(), v.to_owned())).collect();
-        match self.0.expand(subtree, attrs, env, def_site, call_site, mixed_site) {
+        match self.0.expand(subtree, attrs, env.clone(), def_site, call_site, mixed_site) {
             Ok(Ok(subtree)) => Ok(subtree),
             Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)),
             Err(err) => Err(ProcMacroExpansionError::System(err.to_string())),
diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
index 4d5531a..f4bbaef 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
@@ -1,7 +1,7 @@
 //! This module add real world mbe example for benchmark tests
 
 use rustc_hash::FxHashMap;
-use span::Span;
+use span::{Edition, Span};
 use syntax::{
     ast::{self, HasName},
     AstNode, SmolStr,
@@ -46,9 +46,9 @@
         invocations
             .into_iter()
             .map(|(id, tt)| {
-                let res = rules[&id].expand(&tt, |_| (), true, DUMMY);
+                let res = rules[&id].expand(&tt, |_| (), true, DUMMY, Edition::CURRENT);
                 assert!(res.err.is_none());
-                res.value.token_trees.len()
+                res.value.0.token_trees.len()
             })
             .sum()
     };
@@ -66,7 +66,7 @@
 
 fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree<Span>> {
     let fixture = bench_fixture::numerous_macro_rules();
-    let source_file = ast::SourceFile::parse(&fixture).ok().unwrap();
+    let source_file = ast::SourceFile::parse(&fixture, span::Edition::CURRENT).ok().unwrap();
 
     source_file
         .syntax()
@@ -120,7 +120,7 @@
                         },
                         token_trees: token_trees.into_boxed_slice(),
                     };
-                    if it.expand(&subtree, |_| (), true, DUMMY).err.is_none() {
+                    if it.expand(&subtree, |_| (), true, DUMMY, Edition::CURRENT).err.is_none() {
                         res.push((name.clone(), subtree));
                         break;
                     }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
index 2f2c0aa..cfad8bc 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
@@ -6,10 +6,10 @@
 mod transcriber;
 
 use rustc_hash::FxHashMap;
-use span::Span;
+use span::{Edition, Span};
 use syntax::SmolStr;
 
-use crate::{parser::MetaVarKind, ExpandError, ExpandResult};
+use crate::{parser::MetaVarKind, ExpandError, ExpandResult, MatchedArmIndex};
 
 pub(crate) fn expand_rules(
     rules: &[crate::Rule],
@@ -17,10 +17,11 @@
     marker: impl Fn(&mut Span) + Copy,
     new_meta_vars: bool,
     call_site: Span,
-) -> ExpandResult<tt::Subtree<Span>> {
-    let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
-    for rule in rules {
-        let new_match = matcher::match_(&rule.lhs, input);
+    def_site_edition: Edition,
+) -> ExpandResult<(tt::Subtree<Span>, MatchedArmIndex)> {
+    let mut match_: Option<(matcher::Match, &crate::Rule, usize)> = None;
+    for (idx, rule) in rules.iter().enumerate() {
+        let new_match = matcher::match_(&rule.lhs, input, def_site_edition);
 
         if new_match.err.is_none() {
             // If we find a rule that applies without errors, we're done.
@@ -34,31 +35,34 @@
                 call_site,
             );
             if transcribe_err.is_none() {
-                return ExpandResult::ok(value);
+                return ExpandResult::ok((value, Some(idx as u32)));
             }
         }
         // Use the rule if we matched more tokens, or bound variables count
-        if let Some((prev_match, _)) = &match_ {
+        if let Some((prev_match, _, _)) = &match_ {
             if (new_match.unmatched_tts, -(new_match.bound_count as i32))
                 < (prev_match.unmatched_tts, -(prev_match.bound_count as i32))
             {
-                match_ = Some((new_match, rule));
+                match_ = Some((new_match, rule, idx));
             }
         } else {
-            match_ = Some((new_match, rule));
+            match_ = Some((new_match, rule, idx));
         }
     }
-    if let Some((match_, rule)) = match_ {
+    if let Some((match_, rule, idx)) = match_ {
         // if we got here, there was no match without errors
         let ExpandResult { value, err: transcribe_err } =
             transcriber::transcribe(&rule.rhs, &match_.bindings, marker, new_meta_vars, call_site);
-        ExpandResult { value, err: match_.err.or(transcribe_err) }
+        ExpandResult { value: (value, idx.try_into().ok()), err: match_.err.or(transcribe_err) }
     } else {
         ExpandResult::new(
-            tt::Subtree {
-                delimiter: tt::Delimiter::invisible_spanned(call_site),
-                token_trees: Box::new([]),
-            },
+            (
+                tt::Subtree {
+                    delimiter: tt::Delimiter::invisible_spanned(call_site),
+                    token_trees: Box::default(),
+                },
+                None,
+            ),
             ExpandError::NoMatchingRule,
         )
     }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
index 3170834d5..78d4bfe 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
@@ -62,7 +62,7 @@
 use std::rc::Rc;
 
 use smallvec::{smallvec, SmallVec};
-use span::Span;
+use span::{Edition, Span};
 use syntax::SmolStr;
 use tt::DelimSpan;
 
@@ -108,8 +108,8 @@
 }
 
 /// Matching errors are added to the `Match`.
-pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree<Span>) -> Match {
-    let mut res = match_loop(pattern, input);
+pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree<Span>, edition: Edition) -> Match {
+    let mut res = match_loop(pattern, input, edition);
     res.bound_count = count(res.bindings.bindings());
     return res;
 
@@ -363,6 +363,7 @@
     eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
     error_items: &mut SmallVec<[MatchState<'t>; 1]>,
     delim_span: tt::DelimSpan<Span>,
+    edition: Edition,
 ) {
     macro_rules! try_push {
         ($items: expr, $it:expr) => {
@@ -473,7 +474,7 @@
             OpDelimited::Op(Op::Var { kind, name, .. }) => {
                 if let &Some(kind) = kind {
                     let mut fork = src.clone();
-                    let match_res = match_meta_var(kind, &mut fork, delim_span);
+                    let match_res = match_meta_var(kind, &mut fork, delim_span, edition);
                     match match_res.err {
                         None => {
                             // Some meta variables are optional (e.g. vis)
@@ -586,7 +587,7 @@
     }
 }
 
-fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>) -> Match {
+fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, edition: Edition) -> Match {
     let span = src.delimiter.delim_span();
     let mut src = TtIter::new(src);
     let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new();
@@ -627,6 +628,7 @@
             &mut eof_items,
             &mut error_items,
             span,
+            edition,
         );
         stdx::always!(cur_items.is_empty());
 
@@ -740,21 +742,14 @@
     kind: MetaVarKind,
     input: &mut TtIter<'_, Span>,
     delim_span: DelimSpan<Span>,
+    edition: Edition,
 ) -> ExpandResult<Option<Fragment>> {
     let fragment = match kind {
         MetaVarKind::Path => {
-            return input.expect_fragment(parser::PrefixEntryPoint::Path).map(|it| {
+            return input.expect_fragment(parser::PrefixEntryPoint::Path, edition).map(|it| {
                 it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path)
             });
         }
-        MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
-        MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop,
-        MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
-        MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
-        MetaVarKind::Block => parser::PrefixEntryPoint::Block,
-        MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
-        MetaVarKind::Item => parser::PrefixEntryPoint::Item,
-        MetaVarKind::Vis => parser::PrefixEntryPoint::Vis,
         MetaVarKind::Expr => {
             // `expr` should not match underscores, let expressions, or inline const. The latter
             // two are for [backwards compatibility][0].
@@ -770,7 +765,7 @@
                 }
                 _ => {}
             };
-            return input.expect_fragment(parser::PrefixEntryPoint::Expr).map(|tt| {
+            return input.expect_fragment(parser::PrefixEntryPoint::Expr, edition).map(|tt| {
                 tt.map(|tt| match tt {
                     tt::TokenTree::Leaf(leaf) => tt::Subtree {
                         delimiter: tt::Delimiter::invisible_spanned(*leaf.span()),
@@ -818,8 +813,16 @@
             };
             return tt_result.map(|it| Some(Fragment::Tokens(it))).into();
         }
+        MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
+        MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop,
+        MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
+        MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
+        MetaVarKind::Block => parser::PrefixEntryPoint::Block,
+        MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
+        MetaVarKind::Item => parser::PrefixEntryPoint::Item,
+        MetaVarKind::Vis => parser::PrefixEntryPoint::Vis,
     };
-    input.expect_fragment(fragment).map(|it| it.map(Fragment::Tokens))
+    input.expect_fragment(fragment, edition).map(|it| it.map(Fragment::Tokens))
 }
 
 fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) {
diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
index 3a85351..d5de563 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
@@ -122,6 +122,9 @@
     }
 }
 
+/// Index of the matched macro arm on successful expansion.
+pub type MatchedArmIndex = Option<u32>;
+
 /// This struct contains AST for a single `macro_rules` definition. What might
 /// be very confusing is that AST has almost exactly the same shape as
 /// `tt::TokenTree`, but there's a crucial difference: in macro rules, `$ident`
@@ -250,8 +253,9 @@
         marker: impl Fn(&mut Span) + Copy,
         new_meta_vars: bool,
         call_site: Span,
-    ) -> ExpandResult<tt::Subtree<Span>> {
-        expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site)
+        def_site_edition: Edition,
+    ) -> ExpandResult<(tt::Subtree<Span>, MatchedArmIndex)> {
+        expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site, def_site_edition)
     }
 }
 
@@ -329,6 +333,10 @@
         Self { value: Default::default(), err: Some(err) }
     }
 
+    pub fn zip_val<U>(self, other: U) -> ValueResult<(T, U), E> {
+        ValueResult { value: (self.value, other), err: self.err }
+    }
+
     pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ValueResult<U, E> {
         ValueResult { value: f(self.value), err: self.err }
     }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
index c934db6..3230eeb 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
@@ -3,7 +3,7 @@
 use std::fmt;
 
 use rustc_hash::{FxHashMap, FxHashSet};
-use span::{SpanAnchor, SpanData, SpanMap};
+use span::{Edition, SpanAnchor, SpanData, SpanMap};
 use stdx::{never, non_empty_vec::NonEmptyVec};
 use syntax::{
     ast::{self, make::tokens::doc_comment},
@@ -119,6 +119,7 @@
 pub fn token_tree_to_syntax_node<Ctx>(
     tt: &tt::Subtree<SpanData<Ctx>>,
     entry_point: parser::TopEntryPoint,
+    edition: parser::Edition,
 ) -> (Parse<SyntaxNode>, SpanMap<Ctx>)
 where
     SpanData<Ctx>: Copy + fmt::Debug,
@@ -131,7 +132,7 @@
         _ => TokenBuffer::from_subtree(tt),
     };
     let parser_input = to_parser_input(&buffer);
-    let parser_output = entry_point.parse(&parser_input);
+    let parser_output = entry_point.parse(&parser_input, edition);
     let mut tree_sink = TtTreeSink::new(buffer.begin());
     for event in parser_output.iter() {
         match event {
@@ -182,7 +183,12 @@
 }
 
 /// Split token tree with separate expr: $($e:expr)SEP*
-pub fn parse_exprs_with_sep<S>(tt: &tt::Subtree<S>, sep: char, span: S) -> Vec<tt::Subtree<S>>
+pub fn parse_exprs_with_sep<S>(
+    tt: &tt::Subtree<S>,
+    sep: char,
+    span: S,
+    edition: Edition,
+) -> Vec<tt::Subtree<S>>
 where
     S: Copy + fmt::Debug,
 {
@@ -194,7 +200,7 @@
     let mut res = Vec::new();
 
     while iter.peek_n(0).is_some() {
-        let expanded = iter.expect_fragment(parser::PrefixEntryPoint::Expr);
+        let expanded = iter.expect_fragment(parser::PrefixEntryPoint::Expr, edition);
 
         res.push(match expanded.value {
             None => break,
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
index a261b1d..bbfe378 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
@@ -10,7 +10,7 @@
 use crate::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY};
 
 fn check_punct_spacing(fixture: &str) {
-    let source_file = ast::SourceFile::parse(fixture).ok().unwrap();
+    let source_file = ast::SourceFile::parse(fixture, span::Edition::CURRENT).ok().unwrap();
     let subtree = syntax_node_to_token_tree(source_file.syntax(), DummyTestSpanMap, DUMMY);
     let mut annotations: FxHashMap<_, _> = extract_annotations(fixture)
         .into_iter()
diff --git a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
index e3d12d8..9c7d7af 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
@@ -140,10 +140,11 @@
     pub(crate) fn expect_fragment(
         &mut self,
         entry_point: parser::PrefixEntryPoint,
+        edition: parser::Edition,
     ) -> ExpandResult<Option<tt::TokenTree<S>>> {
         let buffer = tt::buffer::TokenBuffer::from_tokens(self.inner.as_slice());
         let parser_input = to_parser_input(&buffer);
-        let tree_traversal = entry_point.parse(&parser_input);
+        let tree_traversal = entry_point.parse(&parser_input, edition);
         let mut cursor = buffer.begin();
         let mut error = false;
         for step in tree_traversal.iter() {
diff --git a/src/tools/rust-analyzer/crates/parser/src/edition.rs b/src/tools/rust-analyzer/crates/parser/src/edition.rs
new file mode 100644
index 0000000..2617854
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/src/edition.rs
@@ -0,0 +1,55 @@
+//! The edition of the Rust language used in a crate.
+// Ideally this would be defined in the span crate, but the dependency chain is all over the place
+// wrt to span, parser and syntax.
+use std::fmt;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Edition {
+    Edition2015,
+    Edition2018,
+    Edition2021,
+    Edition2024,
+}
+
+impl Edition {
+    pub const CURRENT: Edition = Edition::Edition2021;
+    pub const DEFAULT: Edition = Edition::Edition2015;
+}
+
+#[derive(Debug)]
+pub struct ParseEditionError {
+    invalid_input: String,
+}
+
+impl std::error::Error for ParseEditionError {}
+impl fmt::Display for ParseEditionError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "invalid edition: {:?}", self.invalid_input)
+    }
+}
+
+impl std::str::FromStr for Edition {
+    type Err = ParseEditionError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let res = match s {
+            "2015" => Edition::Edition2015,
+            "2018" => Edition::Edition2018,
+            "2021" => Edition::Edition2021,
+            "2024" => Edition::Edition2024,
+            _ => return Err(ParseEditionError { invalid_input: s.to_owned() }),
+        };
+        Ok(res)
+    }
+}
+
+impl fmt::Display for Edition {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(match self {
+            Edition::Edition2015 => "2015",
+            Edition::Edition2018 => "2018",
+            Edition::Edition2021 => "2021",
+            Edition::Edition2024 => "2024",
+        })
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index 48e4c8a..e5fec67 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -178,7 +178,7 @@
                 rustc_lexer::TokenKind::Ident => {
                     SyntaxKind::from_keyword(token_text).unwrap_or(IDENT)
                 }
-                rustc_lexer::TokenKind::InvalidIdent => {
+                rustc_lexer::TokenKind::InvalidPrefix | rustc_lexer::TokenKind::InvalidIdent => {
                     err = "Ident contains invalid characters";
                     IDENT
                 }
diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs
index 86c771c..c7ad025 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs
@@ -26,6 +26,7 @@
 #[cfg(feature = "in-rust-tree")]
 extern crate rustc_lexer;
 
+mod edition;
 mod event;
 mod grammar;
 mod input;
@@ -42,6 +43,7 @@
 pub(crate) use token_set::TokenSet;
 
 pub use crate::{
+    edition::Edition,
     input::Input,
     lexed_str::LexedStr,
     output::{Output, Step},
@@ -86,7 +88,7 @@
 }
 
 impl TopEntryPoint {
-    pub fn parse(&self, input: &Input) -> Output {
+    pub fn parse(&self, input: &Input, edition: Edition) -> Output {
         let _p = tracing::span!(tracing::Level::INFO, "TopEntryPoint::parse", ?self).entered();
         let entry_point: fn(&'_ mut parser::Parser<'_>) = match self {
             TopEntryPoint::SourceFile => grammar::entry::top::source_file,
@@ -98,7 +100,7 @@
             TopEntryPoint::MetaItem => grammar::entry::top::meta_item,
             TopEntryPoint::MacroEagerInput => grammar::entry::top::eager_macro_input,
         };
-        let mut p = parser::Parser::new(input);
+        let mut p = parser::Parser::new(input, edition);
         entry_point(&mut p);
         let events = p.finish();
         let res = event::process(events);
@@ -150,7 +152,7 @@
 }
 
 impl PrefixEntryPoint {
-    pub fn parse(&self, input: &Input) -> Output {
+    pub fn parse(&self, input: &Input, edition: Edition) -> Output {
         let entry_point: fn(&'_ mut parser::Parser<'_>) = match self {
             PrefixEntryPoint::Vis => grammar::entry::prefix::vis,
             PrefixEntryPoint::Block => grammar::entry::prefix::block,
@@ -163,7 +165,7 @@
             PrefixEntryPoint::Item => grammar::entry::prefix::item,
             PrefixEntryPoint::MetaItem => grammar::entry::prefix::meta_item,
         };
-        let mut p = parser::Parser::new(input);
+        let mut p = parser::Parser::new(input, edition);
         entry_point(&mut p);
         let events = p.finish();
         event::process(events)
@@ -187,9 +189,9 @@
     ///
     /// Tokens must start with `{`, end with `}` and form a valid brace
     /// sequence.
-    pub fn parse(self, tokens: &Input) -> Output {
+    pub fn parse(self, tokens: &Input, edition: Edition) -> Output {
         let Reparser(r) = self;
-        let mut p = parser::Parser::new(tokens);
+        let mut p = parser::Parser::new(tokens, edition);
         r(&mut p);
         let events = p.finish();
         event::process(events)
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 0514612..5b901f9 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -8,6 +8,7 @@
 use crate::{
     event::Event,
     input::Input,
+    Edition,
     SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
     TokenSet, T,
 };
@@ -26,13 +27,14 @@
     pos: usize,
     events: Vec<Event>,
     steps: Cell<u32>,
+    _edition: Edition,
 }
 
 static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000);
 
 impl<'t> Parser<'t> {
-    pub(super) fn new(inp: &'t Input) -> Parser<'t> {
-        Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0) }
+    pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> {
+        Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), _edition: edition }
     }
 
     pub(crate) fn finish(self) -> Vec<Event> {
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index 6ecfdc9..ef83420 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -61,9 +61,11 @@
     SHR,
     SHLEQ,
     SHREQ,
+    ABSTRACT_KW,
     AS_KW,
     ASYNC_KW,
     AWAIT_KW,
+    BECOME_KW,
     BOX_KW,
     BREAK_KW,
     CONST_KW,
@@ -75,6 +77,7 @@
     ENUM_KW,
     EXTERN_KW,
     FALSE_KW,
+    FINAL_KW,
     FN_KW,
     FOR_KW,
     IF_KW,
@@ -87,10 +90,11 @@
     MOD_KW,
     MOVE_KW,
     MUT_KW,
+    OVERRIDE_KW,
+    PRIV_KW,
     PUB_KW,
     REF_KW,
     RETURN_KW,
-    BECOME_KW,
     SELF_KW,
     SELF_TYPE_KW,
     STATIC_KW,
@@ -100,8 +104,11 @@
     TRUE_KW,
     TRY_KW,
     TYPE_KW,
+    TYPEOF_KW,
     UNSAFE_KW,
+    UNSIZED_KW,
     USE_KW,
+    VIRTUAL_KW,
     WHERE_KW,
     WHILE_KW,
     YIELD_KW,
@@ -280,9 +287,11 @@
     pub fn is_keyword(self) -> bool {
         matches!(
             self,
-            AS_KW
+            ABSTRACT_KW
+                | AS_KW
                 | ASYNC_KW
                 | AWAIT_KW
+                | BECOME_KW
                 | BOX_KW
                 | BREAK_KW
                 | CONST_KW
@@ -294,6 +303,7 @@
                 | ENUM_KW
                 | EXTERN_KW
                 | FALSE_KW
+                | FINAL_KW
                 | FN_KW
                 | FOR_KW
                 | IF_KW
@@ -306,10 +316,11 @@
                 | MOD_KW
                 | MOVE_KW
                 | MUT_KW
+                | OVERRIDE_KW
+                | PRIV_KW
                 | PUB_KW
                 | REF_KW
                 | RETURN_KW
-                | BECOME_KW
                 | SELF_KW
                 | SELF_TYPE_KW
                 | STATIC_KW
@@ -319,8 +330,11 @@
                 | TRUE_KW
                 | TRY_KW
                 | TYPE_KW
+                | TYPEOF_KW
                 | UNSAFE_KW
+                | UNSIZED_KW
                 | USE_KW
+                | VIRTUAL_KW
                 | WHERE_KW
                 | WHILE_KW
                 | YIELD_KW
@@ -399,9 +413,11 @@
     }
     pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
         let kw = match ident {
+            "abstract" => ABSTRACT_KW,
             "as" => AS_KW,
             "async" => ASYNC_KW,
             "await" => AWAIT_KW,
+            "become" => BECOME_KW,
             "box" => BOX_KW,
             "break" => BREAK_KW,
             "const" => CONST_KW,
@@ -413,6 +429,7 @@
             "enum" => ENUM_KW,
             "extern" => EXTERN_KW,
             "false" => FALSE_KW,
+            "final" => FINAL_KW,
             "fn" => FN_KW,
             "for" => FOR_KW,
             "if" => IF_KW,
@@ -425,10 +442,11 @@
             "mod" => MOD_KW,
             "move" => MOVE_KW,
             "mut" => MUT_KW,
+            "override" => OVERRIDE_KW,
+            "priv" => PRIV_KW,
             "pub" => PUB_KW,
             "ref" => REF_KW,
             "return" => RETURN_KW,
-            "become" => BECOME_KW,
             "self" => SELF_KW,
             "Self" => SELF_TYPE_KW,
             "static" => STATIC_KW,
@@ -438,8 +456,11 @@
             "true" => TRUE_KW,
             "try" => TRY_KW,
             "type" => TYPE_KW,
+            "typeof" => TYPEOF_KW,
             "unsafe" => UNSAFE_KW,
+            "unsized" => UNSIZED_KW,
             "use" => USE_KW,
+            "virtual" => VIRTUAL_KW,
             "where" => WHERE_KW,
             "while" => WHILE_KW,
             "yield" => YIELD_KW,
@@ -500,4 +521,4 @@
     }
 }
 #[macro_export]
-macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests.rs b/src/tools/rust-analyzer/crates/parser/src/tests.rs
index c65219b..0e04096 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests.rs
@@ -88,7 +88,7 @@
 fn parse(entry: TopEntryPoint, text: &str) -> (String, bool) {
     let lexed = LexedStr::new(text);
     let input = lexed.to_input();
-    let output = entry.parse(&input);
+    let output = entry.parse(&input, crate::Edition::CURRENT);
 
     let mut buf = String::new();
     let mut errors = Vec::new();
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
index 2f3c7fe..f92b39e 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
@@ -86,7 +86,7 @@
     let input = lexed.to_input();
 
     let mut n_tokens = 0;
-    for step in entry.parse(&input).iter() {
+    for step in entry.parse(&input, crate::Edition::CURRENT).iter() {
         match step {
             Step::Token { n_input_tokens, .. } => n_tokens += n_input_tokens as usize,
             Step::FloatSplit { .. } => n_tokens += 1,
diff --git a/src/tools/rust-analyzer/crates/parser/src/token_set.rs b/src/tools/rust-analyzer/crates/parser/src/token_set.rs
index cd4894c..88a89a5 100644
--- a/src/tools/rust-analyzer/crates/parser/src/token_set.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/token_set.rs
@@ -4,34 +4,48 @@
 
 /// A bit-set of `SyntaxKind`s
 #[derive(Clone, Copy)]
-pub(crate) struct TokenSet(u128);
+pub(crate) struct TokenSet([u64; 3]);
+
+/// `TokenSet`s should only include token `SyntaxKind`s, so the discriminant of any passed/included
+/// `SyntaxKind` must *not* be greater than that of the last token `SyntaxKind`.
+/// See #17037.
+const LAST_TOKEN_KIND_DISCRIMINANT: usize = SyntaxKind::SHEBANG as usize;
 
 impl TokenSet {
-    pub(crate) const EMPTY: TokenSet = TokenSet(0);
+    pub(crate) const EMPTY: TokenSet = TokenSet([0; 3]);
 
     pub(crate) const fn new(kinds: &[SyntaxKind]) -> TokenSet {
-        let mut res = 0u128;
+        let mut res = [0; 3];
         let mut i = 0;
         while i < kinds.len() {
-            res |= mask(kinds[i]);
+            let discriminant = kinds[i] as usize;
+            debug_assert!(
+                discriminant <= LAST_TOKEN_KIND_DISCRIMINANT,
+                "Expected a token `SyntaxKind`"
+            );
+            let idx = discriminant / 64;
+            res[idx] |= 1 << (discriminant % 64);
             i += 1;
         }
         TokenSet(res)
     }
 
     pub(crate) const fn union(self, other: TokenSet) -> TokenSet {
-        TokenSet(self.0 | other.0)
+        TokenSet([self.0[0] | other.0[0], self.0[1] | other.0[1], self.0[2] | other.0[2]])
     }
 
     pub(crate) const fn contains(&self, kind: SyntaxKind) -> bool {
-        self.0 & mask(kind) != 0
+        let discriminant = kind as usize;
+        debug_assert!(
+            discriminant <= LAST_TOKEN_KIND_DISCRIMINANT,
+            "Expected a token `SyntaxKind`"
+        );
+        let idx = discriminant / 64;
+        let mask = 1 << (discriminant % 64);
+        self.0[idx] & mask != 0
     }
 }
 
-const fn mask(kind: SyntaxKind) -> u128 {
-    1u128 << (kind as usize)
-}
-
 #[test]
 fn token_set_works_for_tokens() {
     use crate::SyntaxKind::*;
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index fd49164..0ab16c3 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -11,6 +11,7 @@
 mod process;
 mod version;
 
+use base_db::Env;
 use indexmap::IndexSet;
 use paths::AbsPathBuf;
 use rustc_hash::FxHashMap;
@@ -37,7 +38,7 @@
     CustomDerive,
     Attr,
     // This used to be called FuncLike, so that's what the server expects currently.
-    #[serde(alias = "bang")]
+    #[serde(alias = "Bang")]
     #[serde(rename(serialize = "FuncLike", deserialize = "FuncLike"))]
     Bang,
 }
@@ -152,16 +153,13 @@
         &self,
         subtree: &tt::Subtree<Span>,
         attr: Option<&tt::Subtree<Span>>,
-        env: Vec<(String, String)>,
+        env: Env,
         def_site: Span,
         call_site: Span,
         mixed_site: Span,
     ) -> Result<Result<tt::Subtree<Span>, PanicMessage>, ServerError> {
         let version = self.process.lock().unwrap_or_else(|e| e.into_inner()).version();
-        let current_dir = env
-            .iter()
-            .find(|(name, _)| name == "CARGO_MANIFEST_DIR")
-            .map(|(_, value)| value.clone());
+        let current_dir = env.get("CARGO_MANIFEST_DIR");
 
         let mut span_data_table = IndexSet::default();
         let def_site = span_data_table.insert_full(def_site).0;
@@ -172,7 +170,7 @@
             macro_name: self.name.to_string(),
             attributes: attr.map(|subtree| FlatTree::new(subtree, version, &mut span_data_table)),
             lib: self.dylib_path.to_path_buf().into(),
-            env,
+            env: env.into(),
             current_dir,
             has_global_spans: ExpnGlobals {
                 serialize: version >= HAS_GLOBAL_SPANS,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs
new file mode 100644
index 0000000..07f914f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs
@@ -0,0 +1,5 @@
+//! This teaches cargo about our cfg(rust_analyzer)
+
+fn main() {
+    println!("cargo:rustc-check-cfg=cfg(rust_analyzer)");
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs
index a8c732f..874d1c6 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs
@@ -4,6 +4,8 @@
 use std::{env, fs::File, io::Write, path::PathBuf, process::Command};
 
 fn main() {
+    println!("cargo:rustc-check-cfg=cfg(rust_analyzer)");
+
     let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
     path.push("rustc_version.rs");
     let mut f = File::create(&path).unwrap();
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs
index c76c201..6a0ae36 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs
@@ -53,7 +53,7 @@
     println!("Creating {}", src_dir.display());
     std::fs::create_dir_all(src_dir).unwrap();
 
-    for item_els in [&["Cargo.toml"][..], &["src", "lib.rs"]] {
+    for item_els in [&["Cargo.toml"][..], &["build.rs"][..], &["src", "lib.rs"]] {
         let mut src = imp_dir.clone();
         let mut dst = staging_dir.clone();
         for el in item_els {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs
new file mode 100644
index 0000000..07f914f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs
@@ -0,0 +1,5 @@
+//! This teaches cargo about our cfg(rust_analyzer)
+
+fn main() {
+    println!("cargo:rustc-check-cfg=cfg(rust_analyzer)");
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
index d40eb26..fbd423c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
@@ -23,16 +23,18 @@
 use toolchain::Tool;
 
 use crate::{
-    cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
+    cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
     InvocationStrategy, Package, Sysroot, TargetKind,
 };
 
+/// Output of the build script and proc-macro building steps for a workspace.
 #[derive(Debug, Default, Clone, PartialEq, Eq)]
 pub struct WorkspaceBuildScripts {
     outputs: ArenaMap<Package, BuildScriptOutput>,
     error: Option<String>,
 }
 
+/// Output of the build script and proc-macro building step for a concrete package.
 #[derive(Debug, Clone, Default, PartialEq, Eq)]
 pub(crate) struct BuildScriptOutput {
     /// List of config flags defined by this package's build script.
@@ -86,7 +88,9 @@
                 // --all-targets includes tests, benches and examples in addition to the
                 // default lib and bins. This is an independent concept from the --target
                 // flag below.
-                cmd.arg("--all-targets");
+                if config.all_targets {
+                    cmd.arg("--all-targets");
+                }
 
                 if let Some(target) = &config.target {
                     cmd.args(["--target", target]);
@@ -235,7 +239,7 @@
             },
             progress,
         )?;
-        res.iter_mut().for_each(|it| it.error = errors.clone());
+        res.iter_mut().for_each(|it| it.error.clone_from(&errors));
         collisions.into_iter().for_each(|(id, workspace, package)| {
             if let Some(&(p, w)) = by_id.get(id) {
                 res[workspace].outputs[package] = res[w].outputs[p].clone();
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 51c1b09..ff7cf14 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -76,6 +76,8 @@
 
 #[derive(Default, Clone, Debug, PartialEq, Eq)]
 pub struct CargoConfig {
+    /// Whether to pass `--all-targets` to cargo invocations.
+    pub all_targets: bool,
     /// List of features to activate.
     pub features: CargoFeatures,
     /// rustc target
@@ -133,6 +135,20 @@
     pub active_features: Vec<String>,
     /// String representation of package id
     pub id: String,
+    /// Authors as given in the `Cargo.toml`
+    pub authors: Vec<String>,
+    /// Description as given in the `Cargo.toml`
+    pub description: Option<String>,
+    /// Homepage as given in the `Cargo.toml`
+    pub homepage: Option<String>,
+    /// License as given in the `Cargo.toml`
+    pub license: Option<String>,
+    /// License file as given in the `Cargo.toml`
+    pub license_file: Option<Utf8PathBuf>,
+    /// Readme file as given in the `Cargo.toml`
+    pub readme: Option<Utf8PathBuf>,
+    /// Rust version as given in the `Cargo.toml`
+    pub rust_version: Option<semver::Version>,
     /// The contents of [package.metadata.rust-analyzer]
     pub metadata: RustAnalyzerPackageMetaData,
 }
@@ -223,6 +239,10 @@
         }
         TargetKind::Other
     }
+
+    pub fn is_executable(self) -> bool {
+        matches!(self, TargetKind::Bin | TargetKind::Example)
+    }
 }
 
 // Deserialize helper for the cargo metadata
@@ -285,6 +305,12 @@
                     .collect(),
             );
         }
+        // The manifest is a rust file, so this means its a script manifest
+        if cargo_toml.extension().is_some_and(|ext| ext == "rs") {
+            // Deliberately don't set up RUSTC_BOOTSTRAP or a nightly override here, the user should
+            // opt into it themselves.
+            other_options.push("-Zscript".to_owned());
+        }
         meta.other_options(other_options);
 
         // FIXME: Fetching metadata is a slow process, as it might require
@@ -328,6 +354,13 @@
                 repository,
                 edition,
                 metadata,
+                authors,
+                description,
+                homepage,
+                license,
+                license_file,
+                readme,
+                rust_version,
                 ..
             } = meta_pkg;
             let meta = from_value::<PackageMetadata>(metadata).unwrap_or_default();
@@ -346,16 +379,24 @@
             let is_local = source.is_none();
             let is_member = ws_members.contains(&id);
 
+            let manifest = AbsPathBuf::assert(manifest_path);
             let pkg = packages.alloc(PackageData {
                 id: id.repr.clone(),
                 name,
                 version,
-                manifest: AbsPathBuf::assert(manifest_path).try_into().unwrap(),
+                manifest: manifest.clone().try_into().unwrap(),
                 targets: Vec::new(),
                 is_local,
                 is_member,
                 edition,
                 repository,
+                authors,
+                description,
+                homepage,
+                license,
+                license_file,
+                readme,
+                rust_version,
                 dependencies: Vec::new(),
                 features: features.into_iter().collect(),
                 active_features: Vec::new(),
@@ -366,11 +407,22 @@
             for meta_tgt in meta_targets {
                 let cargo_metadata::Target { name, kind, required_features, src_path, .. } =
                     meta_tgt;
+                let kind = TargetKind::new(&kind);
                 let tgt = targets.alloc(TargetData {
                     package: pkg,
                     name,
-                    root: AbsPathBuf::assert(src_path),
-                    kind: TargetKind::new(&kind),
+                    root: if kind == TargetKind::Bin
+                        && manifest.extension().is_some_and(|ext| ext == "rs")
+                    {
+                        // cargo strips the script part of a cargo script away and places the
+                        // modified manifest file into a special target dir which is then used as
+                        // the source path. We don't want that, we want the original here so map it
+                        // back
+                        manifest.clone()
+                    } else {
+                        AbsPathBuf::assert(src_path)
+                    },
+                    kind,
                     required_features,
                 });
                 pkg_data.targets.push(tgt);
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs
similarity index 70%
rename from src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs
rename to src/tools/rust-analyzer/crates/project-model/src/cfg.rs
index af68290..b409bc1 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs
@@ -3,9 +3,11 @@
 //! rustc main.rs --cfg foo --cfg 'feature="bar"'
 use std::{fmt, str::FromStr};
 
-use cfg::CfgOptions;
+use cfg::{CfgDiff, CfgOptions};
+use rustc_hash::FxHashMap;
+use serde::Serialize;
 
-#[derive(Clone, Eq, PartialEq, Debug)]
+#[derive(Clone, Eq, PartialEq, Debug, Serialize)]
 pub enum CfgFlag {
     Atom(String),
     KeyValue { key: String, value: String },
@@ -69,3 +71,27 @@
         }
     }
 }
+
+/// A set of cfg-overrides per crate.
+#[derive(Default, Debug, Clone, Eq, PartialEq)]
+pub struct CfgOverrides {
+    /// A global set of overrides matching all crates.
+    pub global: CfgDiff,
+    /// A set of overrides matching specific crates.
+    pub selective: FxHashMap<String, CfgDiff>,
+}
+
+impl CfgOverrides {
+    pub fn len(&self) -> usize {
+        self.global.len() + self.selective.values().map(|it| it.len()).sum::<usize>()
+    }
+
+    pub fn apply(&self, cfg_options: &mut CfgOptions, name: &str) {
+        if !self.global.is_empty() {
+            cfg_options.apply_diff(self.global.clone());
+        };
+        if let Some(diff) = self.selective.get(name) {
+            cfg_options.apply_diff(diff.clone());
+        };
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs
new file mode 100644
index 0000000..762e01c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs
@@ -0,0 +1,85 @@
+//! Cargo-like environment variables injection.
+use base_db::Env;
+use rustc_hash::FxHashMap;
+use toolchain::Tool;
+
+use crate::{utf8_stdout, ManifestPath, PackageData, Sysroot, TargetKind};
+
+/// Recreates the compile-time environment variables that Cargo sets.
+///
+/// Should be synced with
+/// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
+///
+/// FIXME: ask Cargo to provide this data instead of re-deriving.
+pub(crate) fn inject_cargo_package_env(env: &mut Env, package: &PackageData) {
+    // FIXME: Missing variables:
+    // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
+
+    let manifest_dir = package.manifest.parent();
+    env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str());
+
+    env.set("CARGO_PKG_VERSION", package.version.to_string());
+    env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
+    env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
+    env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
+    env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
+
+    env.set("CARGO_PKG_AUTHORS", package.authors.join(":").clone());
+
+    env.set("CARGO_PKG_NAME", package.name.clone());
+    env.set("CARGO_PKG_DESCRIPTION", package.description.as_deref().unwrap_or_default());
+    env.set("CARGO_PKG_HOMEPAGE", package.homepage.as_deref().unwrap_or_default());
+    env.set("CARGO_PKG_REPOSITORY", package.repository.as_deref().unwrap_or_default());
+    env.set("CARGO_PKG_LICENSE", package.license.as_deref().unwrap_or_default());
+    env.set(
+        "CARGO_PKG_LICENSE_FILE",
+        package.license_file.as_ref().map(ToString::to_string).unwrap_or_default(),
+    );
+    env.set(
+        "CARGO_PKG_README",
+        package.readme.as_ref().map(ToString::to_string).unwrap_or_default(),
+    );
+
+    env.set(
+        "CARGO_PKG_RUST_VERSION",
+        package.rust_version.as_ref().map(ToString::to_string).unwrap_or_default(),
+    );
+}
+
+pub(crate) fn inject_cargo_env(env: &mut Env) {
+    env.set("CARGO", Tool::Cargo.path().to_string());
+}
+
+pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) {
+    _ = kind;
+    // FIXME
+    // if kind.is_executable() {
+    //     env.set("CARGO_BIN_NAME", cargo_name);
+    // }
+    env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
+}
+
+pub(crate) fn cargo_config_env(
+    cargo_toml: &ManifestPath,
+    extra_env: &FxHashMap<String, String>,
+    sysroot: Option<&Sysroot>,
+) -> FxHashMap<String, String> {
+    let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo);
+    cargo_config.envs(extra_env);
+    cargo_config
+        .current_dir(cargo_toml.parent())
+        .args(["-Z", "unstable-options", "config", "get", "env"])
+        .env("RUSTC_BOOTSTRAP", "1");
+    // if successful we receive `env.key.value = "value" per entry
+    tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
+    utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default()
+}
+
+fn parse_output_cargo_config_env(stdout: String) -> FxHashMap<String, String> {
+    stdout
+        .lines()
+        .filter_map(|l| l.strip_prefix("env."))
+        .filter_map(|l| l.split_once(".value = "))
+        .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned()))
+        .collect()
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index 28696aa..7f3e35c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -19,7 +19,8 @@
 
 mod build_scripts;
 mod cargo_workspace;
-mod cfg_flag;
+mod cfg;
+mod env;
 mod manifest_path;
 mod project_json;
 mod rustc_cfg;
@@ -47,10 +48,11 @@
         CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
         RustLibSource, Target, TargetData, TargetKind,
     },
+    cfg::CfgOverrides,
     manifest_path::ManifestPath,
     project_json::{ProjectJson, ProjectJsonData},
     sysroot::Sysroot,
-    workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
+    workspace::{FileLoader, PackageRoot, ProjectWorkspace},
 };
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index 512588c..fac6eb8 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -49,14 +49,13 @@
 //! user explores them belongs to that extension (it's totally valid to change
 //! rust-project.json over time via configuration request!)
 
-use base_db::{CrateDisplayName, CrateId, CrateName, Dependency};
-use la_arena::RawIdx;
+use base_db::{CrateDisplayName, CrateName};
 use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
 use rustc_hash::FxHashMap;
-use serde::{de, Deserialize};
+use serde::{de, Deserialize, Serialize};
 use span::Edition;
 
-use crate::cfg_flag::CfgFlag;
+use crate::cfg::CfgFlag;
 
 /// Roots and crates that compose this Rust project.
 #[derive(Clone, Debug, Eq, PartialEq)]
@@ -74,10 +73,10 @@
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Crate {
     pub(crate) display_name: Option<CrateDisplayName>,
-    pub(crate) root_module: AbsPathBuf,
+    pub root_module: AbsPathBuf,
     pub(crate) edition: Edition,
     pub(crate) version: Option<String>,
-    pub(crate) deps: Vec<Dependency>,
+    pub(crate) deps: Vec<Dep>,
     pub(crate) cfg: Vec<CfgFlag>,
     pub(crate) target: Option<String>,
     pub(crate) env: FxHashMap<String, String>,
@@ -128,16 +127,7 @@
                         root_module,
                         edition: crate_data.edition.into(),
                         version: crate_data.version.as_ref().map(ToString::to_string),
-                        deps: crate_data
-                            .deps
-                            .into_iter()
-                            .map(|dep_data| {
-                                Dependency::new(
-                                    dep_data.name,
-                                    CrateId::from_raw(RawIdx::from(dep_data.krate as u32)),
-                                )
-                            })
-                            .collect::<Vec<_>>(),
+                        deps: crate_data.deps,
                         cfg: crate_data.cfg,
                         target: crate_data.target,
                         env: crate_data.env,
@@ -161,11 +151,8 @@
     }
 
     /// Returns an iterator over the crates in the project.
-    pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ {
-        self.crates
-            .iter()
-            .enumerate()
-            .map(|(idx, krate)| (CrateId::from_raw(RawIdx::from(idx as u32)), krate))
+    pub fn crates(&self) -> impl Iterator<Item = (CrateArrayIdx, &Crate)> {
+        self.crates.iter().enumerate().map(|(idx, krate)| (CrateArrayIdx(idx), krate))
     }
 
     /// Returns the path to the project's root folder.
@@ -174,21 +161,21 @@
     }
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct ProjectJsonData {
     sysroot: Option<Utf8PathBuf>,
     sysroot_src: Option<Utf8PathBuf>,
     crates: Vec<CrateData>,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 struct CrateData {
     display_name: Option<String>,
     root_module: Utf8PathBuf,
     edition: EditionData,
     #[serde(default)]
     version: Option<semver::Version>,
-    deps: Vec<DepData>,
+    deps: Vec<Dep>,
     #[serde(default)]
     cfg: Vec<CfgFlag>,
     target: Option<String>,
@@ -203,7 +190,7 @@
     repository: Option<String>,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename = "edition")]
 enum EditionData {
     #[serde(rename = "2015")]
@@ -227,16 +214,25 @@
     }
 }
 
-#[derive(Deserialize, Debug, Clone)]
-struct DepData {
+/// Identifies a crate by position in the crates array.
+///
+/// This will differ from `CrateId` when multiple `ProjectJson`
+/// workspaces are loaded.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
+#[serde(transparent)]
+pub struct CrateArrayIdx(pub usize);
+
+#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
+pub(crate) struct Dep {
     /// Identifies a crate by position in the crates array.
     #[serde(rename = "crate")]
-    krate: usize,
+    pub(crate) krate: CrateArrayIdx,
+    #[serde(serialize_with = "serialize_crate_name")]
     #[serde(deserialize_with = "deserialize_crate_name")]
-    name: CrateName,
+    pub(crate) name: CrateName,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 struct CrateSource {
     include_dirs: Vec<Utf8PathBuf>,
     exclude_dirs: Vec<Utf8PathBuf>,
@@ -249,3 +245,10 @@
     let name = String::deserialize(de)?;
     CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {err:?}")))
 }
+
+fn serialize_crate_name<S>(name: &CrateName, se: S) -> Result<S::Ok, S::Error>
+where
+    S: serde::Serializer,
+{
+    se.serialize_str(name)
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
index 501b1fd..4f69b2b 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
@@ -4,7 +4,7 @@
 use rustc_hash::FxHashMap;
 use toolchain::Tool;
 
-use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot};
+use crate::{cfg::CfgFlag, utf8_stdout, ManifestPath, Sysroot};
 
 /// Determines how `rustc --print cfg` is discovered and invoked.
 pub(crate) enum RustcCfgConfig<'a> {
@@ -32,9 +32,6 @@
         }
     }
 
-    // Add miri cfg, which is useful for mir eval in stdlib
-    res.push(CfgFlag::Atom("miri".into()));
-
     let rustc_cfgs = get_rust_cfgs(target, extra_env, config);
 
     let rustc_cfgs = match rustc_cfgs {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index fc0b507..fd09dff 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -75,6 +75,7 @@
         rustc_cfg: Vec::new(),
         toolchain: None,
         target_layout: Err(Arc::from("test has no data layout")),
+        cfg_overrides: Default::default(),
     };
     to_crate_graph(project_workspace)
 }
@@ -97,6 +98,11 @@
     }
 }
 
+fn replace_cargo(s: &mut String) {
+    let path = toolchain::Tool::Cargo.path().to_string().escape_debug().collect::<String>();
+    *s = s.replace(&path, "$CARGO$");
+}
+
 fn replace_root(s: &mut String, direction: bool) {
     if direction {
         let root = if cfg!(windows) { r#"C:\\ROOT\"# } else { "/ROOT/" };
@@ -155,7 +161,9 @@
 
 fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) {
     let mut crate_graph = format!("{crate_graph:#?}");
+
     replace_root(&mut crate_graph, false);
+    replace_cargo(&mut crate_graph);
     replace_fake_sys_root(&mut crate_graph);
     expect.assert_eq(&crate_graph);
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index b8c5885..98c5a02 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -2,7 +2,7 @@
 //! metadata` or `rust-project.json`) into representation stored in the salsa
 //! database -- `CrateGraph`.
 
-use std::{collections::VecDeque, fmt, fs, iter, str::FromStr, sync};
+use std::{collections::VecDeque, fmt, fs, iter, sync};
 
 use anyhow::{format_err, Context};
 use base_db::{
@@ -21,8 +21,9 @@
 use crate::{
     build_scripts::BuildScriptOutput,
     cargo_workspace::{DepKind, PackageData, RustLibSource},
-    cfg_flag::CfgFlag,
-    project_json::Crate,
+    cfg::{CfgFlag, CfgOverrides},
+    env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
+    project_json::{Crate, CrateArrayIdx},
     rustc_cfg::{self, RustcCfgConfig},
     sysroot::{SysrootCrate, SysrootMode},
     target_data_layout::{self, RustcDataLayoutConfig},
@@ -30,20 +31,7 @@
     ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
 };
 
-/// A set of cfg-overrides per crate.
-#[derive(Default, Debug, Clone, Eq, PartialEq)]
-pub struct CfgOverrides {
-    /// A global set of overrides matching all crates.
-    pub global: CfgDiff,
-    /// A set of overrides matching specific crates.
-    pub selective: FxHashMap<String, CfgDiff>,
-}
-
-impl CfgOverrides {
-    pub fn len(&self) -> usize {
-        self.global.len() + self.selective.values().map(|it| it.len()).sum::<usize>()
-    }
-}
+pub type FileLoader<'a> = &'a mut dyn for<'b> FnMut(&'b AbsPath) -> Option<FileId>;
 
 /// `PackageRoot` describes a package root folder.
 /// Which may be an external dependency, or a member of
@@ -60,30 +48,46 @@
 pub enum ProjectWorkspace {
     /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
     Cargo {
+        /// The workspace as returned by `cargo metadata`.
         cargo: CargoWorkspace,
+        /// The build script results for the workspace.
         build_scripts: WorkspaceBuildScripts,
+        /// The sysroot loaded for this workspace.
         sysroot: Result<Sysroot, Option<String>>,
+        /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been
+        /// disabled or was otherwise not requested.
         rustc: Result<Box<(CargoWorkspace, WorkspaceBuildScripts)>, Option<String>>,
         /// Holds cfg flags for the current target. We get those by running
         /// `rustc --print cfg`.
-        ///
-        /// FIXME: make this a per-crate map, as, eg, build.rs might have a
-        /// different target.
+        // FIXME: make this a per-crate map, as, eg, build.rs might have a
+        // different target.
         rustc_cfg: Vec<CfgFlag>,
+        /// A set of cfg overrides for this workspace.
         cfg_overrides: CfgOverrides,
+        /// The toolchain version used by this workspace.
         toolchain: Option<Version>,
+        /// The target data layout queried for workspace.
         target_layout: TargetLayoutLoadResult,
+        /// Environment variables set in the `.cargo/config` file.
         cargo_config_extra_env: FxHashMap<String, String>,
     },
     /// Project workspace was manually specified using a `rust-project.json` file.
     Json {
+        /// The loaded project json file.
         project: ProjectJson,
+        /// The sysroot loaded for this workspace.
         sysroot: Result<Sysroot, Option<String>>,
         /// Holds cfg flags for the current target. We get those by running
         /// `rustc --print cfg`.
+        // FIXME: make this a per-crate map, as, eg, build.rs might have a
+        // different target.
         rustc_cfg: Vec<CfgFlag>,
+        /// The toolchain version used by this workspace.
         toolchain: Option<Version>,
+        /// The target data layout queried for workspace.
         target_layout: TargetLayoutLoadResult,
+        /// A set of cfg overrides for this workspace.
+        cfg_overrides: CfgOverrides,
     },
     // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
     // That's not the end user experience we should strive for.
@@ -95,14 +99,24 @@
     // //
     /// Project with a set of disjoint files, not belonging to any particular workspace.
     /// Backed by basic sysroot crates for basic completion and highlighting.
-    DetachedFiles {
-        files: Vec<AbsPathBuf>,
+    DetachedFile {
+        /// The file in question.
+        file: AbsPathBuf,
+        /// The sysroot loaded for this workspace.
         sysroot: Result<Sysroot, Option<String>>,
         /// Holds cfg flags for the current target. We get those by running
         /// `rustc --print cfg`.
+        // FIXME: make this a per-crate map, as, eg, build.rs might have a
+        // different target.
         rustc_cfg: Vec<CfgFlag>,
+        /// The toolchain version used by this workspace.
         toolchain: Option<Version>,
+        /// The target data layout queried for workspace.
         target_layout: TargetLayoutLoadResult,
+        /// A set of cfg overrides for the files.
+        cfg_overrides: CfgOverrides,
+        /// Is this file a cargo script file?
+        cargo_script: Option<CargoWorkspace>,
     },
 }
 
@@ -141,6 +155,7 @@
                 rustc_cfg,
                 toolchain,
                 target_layout: data_layout,
+                cfg_overrides,
             } => {
                 let mut debug_struct = f.debug_struct("Json");
                 debug_struct.field("n_crates", &project.n_crates());
@@ -150,22 +165,28 @@
                 debug_struct
                     .field("n_rustc_cfg", &rustc_cfg.len())
                     .field("toolchain", &toolchain)
-                    .field("data_layout", &data_layout);
+                    .field("data_layout", &data_layout)
+                    .field("n_cfg_overrides", &cfg_overrides.len());
                 debug_struct.finish()
             }
-            ProjectWorkspace::DetachedFiles {
-                files,
+            ProjectWorkspace::DetachedFile {
+                file,
                 sysroot,
                 rustc_cfg,
                 toolchain,
                 target_layout,
+                cfg_overrides,
+                cargo_script,
             } => f
                 .debug_struct("DetachedFiles")
-                .field("n_files", &files.len())
+                .field("file", &file)
+                .field("cargo_script", &cargo_script.is_some())
                 .field("sysroot", &sysroot.is_ok())
+                .field("cargo_script", &cargo_script.is_some())
                 .field("n_rustc_cfg", &rustc_cfg.len())
                 .field("toolchain", &toolchain)
                 .field("data_layout", &target_layout)
+                .field("n_cfg_overrides", &cfg_overrides.len())
                 .finish(),
         }
     }
@@ -219,6 +240,7 @@
                     project_json,
                     config.target.as_deref(),
                     &config.extra_env,
+                    &config.cfg_overrides,
                 )
             }
             ProjectManifest::CargoToml(cargo_toml) => {
@@ -360,6 +382,7 @@
         project_json: ProjectJson,
         target: Option<&str>,
         extra_env: &FxHashMap<String, String>,
+        cfg_overrides: &CfgOverrides,
     ) -> ProjectWorkspace {
         let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
             (Some(sysroot), Some(sysroot_src)) => {
@@ -406,57 +429,86 @@
             rustc_cfg,
             toolchain,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+            cfg_overrides: cfg_overrides.clone(),
         }
     }
 
     pub fn load_detached_files(
         detached_files: Vec<AbsPathBuf>,
         config: &CargoConfig,
-    ) -> anyhow::Result<ProjectWorkspace> {
-        let dir = detached_files
-            .first()
-            .and_then(|it| it.parent())
-            .ok_or_else(|| format_err!("No detached files to load"))?;
-        let sysroot = match &config.sysroot {
-            Some(RustLibSource::Path(path)) => {
-                Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
-                    .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
-            }
-            Some(RustLibSource::Discover) => Sysroot::discover(
-                dir,
-                &config.extra_env,
-                config.sysroot_query_metadata,
-            )
-            .map_err(|e| {
-                Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}"))
-            }),
-            None => Err(None),
-        };
+    ) -> Vec<anyhow::Result<ProjectWorkspace>> {
+        detached_files
+            .into_iter()
+            .map(|detached_file| {
+                let dir = detached_file
+                    .parent()
+                    .ok_or_else(|| format_err!("detached file has no parent"))?;
+                let sysroot = match &config.sysroot {
+                    Some(RustLibSource::Path(path)) => {
+                        Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
+                            .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
+                    }
+                    Some(RustLibSource::Discover) => {
+                        Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata)
+                            .map_err(|e| {
+                                Some(format!(
+                                    "Failed to find sysroot for {dir}. Is rust-src installed? {e}"
+                                ))
+                            })
+                    }
+                    None => Err(None),
+                };
 
-        let sysroot_ref = sysroot.as_ref().ok();
-        let toolchain =
-            match get_toolchain_version(dir, sysroot_ref, Tool::Rustc, &config.extra_env, "rustc ")
-            {
-                Ok(it) => it,
-                Err(e) => {
-                    tracing::error!("{e}");
-                    None
-                }
-            };
+                let sysroot_ref = sysroot.as_ref().ok();
+                let toolchain = match get_toolchain_version(
+                    dir,
+                    sysroot_ref,
+                    Tool::Rustc,
+                    &config.extra_env,
+                    "rustc ",
+                ) {
+                    Ok(it) => it,
+                    Err(e) => {
+                        tracing::error!("{e}");
+                        None
+                    }
+                };
 
-        let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref));
-        let data_layout = target_data_layout::get(
-            RustcDataLayoutConfig::Rustc(sysroot_ref),
-            None,
-            &config.extra_env,
-        );
-        Ok(ProjectWorkspace::DetachedFiles {
-            files: detached_files,
-            sysroot,
-            rustc_cfg,
-            toolchain,
-            target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
-        })
+                let rustc_cfg =
+                    rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref));
+                let data_layout = target_data_layout::get(
+                    RustcDataLayoutConfig::Rustc(sysroot_ref),
+                    None,
+                    &config.extra_env,
+                );
+
+                let cargo_script = ManifestPath::try_from(detached_file.clone())
+                    .ok()
+                    .and_then(|file| {
+                        CargoWorkspace::fetch_metadata(
+                            &file,
+                            file.parent(),
+                            config,
+                            sysroot_ref,
+                            &|_| (),
+                        )
+                        .ok()
+                    })
+                    .map(CargoWorkspace::new);
+
+                Ok(ProjectWorkspace::DetachedFile {
+                    file: detached_file,
+                    sysroot,
+                    rustc_cfg,
+                    toolchain,
+                    target_layout: data_layout
+                        .map(Arc::from)
+                        .map_err(|it| Arc::from(it.to_string())),
+                    cfg_overrides: config.cfg_overrides.clone(),
+                    cargo_script,
+                })
+            })
+            .collect()
     }
 
     /// Runs the build scripts for this [`ProjectWorkspace`].
@@ -466,7 +518,13 @@
         progress: &dyn Fn(String),
     ) -> anyhow::Result<WorkspaceBuildScripts> {
         match self {
-            ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => {
+            ProjectWorkspace::DetachedFile {
+                cargo_script: Some(cargo),
+                toolchain,
+                sysroot,
+                ..
+            }
+            | ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => {
                 WorkspaceBuildScripts::run_for_workspace(
                     config,
                     cargo,
@@ -478,9 +536,8 @@
                     format!("Failed to run build scripts for {}", cargo.workspace_root())
                 })
             }
-            ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
-                Ok(WorkspaceBuildScripts::default())
-            }
+            ProjectWorkspace::DetachedFile { cargo_script: None, .. }
+            | ProjectWorkspace::Json { .. } => Ok(WorkspaceBuildScripts::default()),
         }
     }
 
@@ -536,11 +593,11 @@
         }
     }
 
-    pub fn workspace_definition_path(&self) -> Option<&AbsPath> {
+    pub fn workspace_definition_path(&self) -> &AbsPath {
         match self {
-            ProjectWorkspace::Cargo { cargo, .. } => Some(cargo.workspace_root()),
-            ProjectWorkspace::Json { project, .. } => Some(project.path()),
-            ProjectWorkspace::DetachedFiles { .. } => None,
+            ProjectWorkspace::Cargo { cargo, .. } => cargo.workspace_root(),
+            ProjectWorkspace::Json { project, .. } => project.path(),
+            ProjectWorkspace::DetachedFile { file, .. } => file,
         }
     }
 
@@ -548,10 +605,10 @@
         match self {
             ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
             | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. }
-            | ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => {
+            | ProjectWorkspace::DetachedFile { sysroot: Ok(sysroot), .. } => {
                 sysroot.discover_proc_macro_srv()
             }
-            ProjectWorkspace::DetachedFiles { .. } => {
+            ProjectWorkspace::DetachedFile { .. } => {
                 Err(anyhow::format_err!("cannot find proc-macro server, no sysroot was found"))
             }
             ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::format_err!(
@@ -611,6 +668,7 @@
                 rustc_cfg: _,
                 toolchain: _,
                 target_layout: _,
+                cfg_overrides: _,
             } => project
                 .crates()
                 .map(|(_, krate)| PackageRoot {
@@ -681,15 +739,50 @@
                     }))
                     .collect()
             }
-            ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
-                .iter()
-                .map(|detached_file| PackageRoot {
+            ProjectWorkspace::DetachedFile { file, cargo_script, sysroot, .. } => {
+                iter::once(PackageRoot {
                     is_local: true,
-                    include: vec![detached_file.clone()],
+                    include: vec![file.clone()],
                     exclude: Vec::new(),
                 })
+                .chain(cargo_script.iter().flat_map(|cargo| {
+                    cargo.packages().map(|pkg| {
+                        let is_local = cargo[pkg].is_local;
+                        let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
+
+                        let mut include = vec![pkg_root.clone()];
+
+                        // In case target's path is manually set in Cargo.toml to be
+                        // outside the package root, add its parent as an extra include.
+                        // An example of this situation would look like this:
+                        //
+                        // ```toml
+                        // [lib]
+                        // path = "../../src/lib.rs"
+                        // ```
+                        let extra_targets = cargo[pkg]
+                            .targets
+                            .iter()
+                            .filter(|&&tgt| matches!(cargo[tgt].kind, TargetKind::Lib { .. }))
+                            .filter_map(|&tgt| cargo[tgt].root.parent())
+                            .map(|tgt| tgt.normalize().to_path_buf())
+                            .filter(|path| !path.starts_with(&pkg_root));
+                        include.extend(extra_targets);
+
+                        let mut exclude = vec![pkg_root.join(".git")];
+                        if is_local {
+                            exclude.push(pkg_root.join("target"));
+                        } else {
+                            exclude.push(pkg_root.join("tests"));
+                            exclude.push(pkg_root.join("examples"));
+                            exclude.push(pkg_root.join("benches"));
+                        }
+                        PackageRoot { is_local, include, exclude }
+                    })
+                }))
                 .chain(mk_sysroot(sysroot.as_ref()))
-                .collect(),
+                .collect()
+            }
         }
     }
 
@@ -705,16 +798,17 @@
                 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
                 cargo.packages().len() + sysroot_package_len + rustc_package_len
             }
-            ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
+            ProjectWorkspace::DetachedFile { sysroot, cargo_script, .. } => {
                 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
-                sysroot_package_len + files.len()
+                sysroot_package_len
+                    + cargo_script.as_ref().map_or(1, |cargo| cargo.packages().len())
             }
         }
     }
 
     pub fn to_crate_graph(
         &self,
-        load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+        load: FileLoader<'_>,
         extra_env: &FxHashMap<String, String>,
     ) -> (CrateGraph, ProcMacroPaths) {
         let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered();
@@ -726,6 +820,7 @@
                 rustc_cfg,
                 toolchain: _,
                 target_layout: _,
+                cfg_overrides,
             } => (
                 project_json_to_crate_graph(
                     rustc_cfg.clone(),
@@ -733,6 +828,7 @@
                     project,
                     sysroot.as_ref().ok(),
                     extra_env,
+                    cfg_overrides,
                 ),
                 sysroot,
             ),
@@ -758,24 +854,39 @@
                 ),
                 sysroot,
             ),
-            ProjectWorkspace::DetachedFiles {
-                files,
+            ProjectWorkspace::DetachedFile {
+                file,
                 sysroot,
                 rustc_cfg,
                 toolchain: _,
                 target_layout: _,
+                cfg_overrides,
+                cargo_script,
             } => (
-                detached_files_to_crate_graph(
-                    rustc_cfg.clone(),
-                    load,
-                    files,
-                    sysroot.as_ref().ok(),
-                ),
+                if let Some(cargo) = cargo_script {
+                    cargo_to_crate_graph(
+                        &mut |path| load(path),
+                        None,
+                        cargo,
+                        sysroot.as_ref().ok(),
+                        rustc_cfg.clone(),
+                        cfg_overrides,
+                        &WorkspaceBuildScripts::default(),
+                    )
+                } else {
+                    detached_file_to_crate_graph(
+                        rustc_cfg.clone(),
+                        load,
+                        file,
+                        sysroot.as_ref().ok(),
+                        cfg_overrides,
+                    )
+                },
                 sysroot,
             ),
         };
 
-        if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Workspace(_)))
+        if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Stitched(_)))
             && crate_graph.patch_cfg_if()
         {
             tracing::debug!("Patched std to depend on cfg-if")
@@ -820,35 +931,56 @@
                     && cargo_config_extra_env == o_cargo_config_extra_env
             }
             (
-                Self::Json { project, sysroot, rustc_cfg, toolchain, target_layout: _ },
+                Self::Json {
+                    project,
+                    sysroot,
+                    rustc_cfg,
+                    toolchain,
+                    target_layout: _,
+                    cfg_overrides,
+                },
                 Self::Json {
                     project: o_project,
                     sysroot: o_sysroot,
                     rustc_cfg: o_rustc_cfg,
                     toolchain: o_toolchain,
                     target_layout: _,
+                    cfg_overrides: o_cfg_overrides,
                 },
             ) => {
                 project == o_project
                     && rustc_cfg == o_rustc_cfg
                     && sysroot == o_sysroot
                     && toolchain == o_toolchain
+                    && cfg_overrides == o_cfg_overrides
             }
             (
-                Self::DetachedFiles { files, sysroot, rustc_cfg, toolchain, target_layout },
-                Self::DetachedFiles {
-                    files: o_files,
+                Self::DetachedFile {
+                    file,
+                    sysroot,
+                    rustc_cfg,
+                    cargo_script,
+                    toolchain,
+                    target_layout,
+                    cfg_overrides,
+                },
+                Self::DetachedFile {
+                    file: o_file,
                     sysroot: o_sysroot,
                     rustc_cfg: o_rustc_cfg,
+                    cargo_script: o_cargo_script,
                     toolchain: o_toolchain,
                     target_layout: o_target_layout,
+                    cfg_overrides: o_cfg_overrides,
                 },
             ) => {
-                files == o_files
+                file == o_file
                     && sysroot == o_sysroot
                     && rustc_cfg == o_rustc_cfg
                     && toolchain == o_toolchain
                     && target_layout == o_target_layout
+                    && cfg_overrides == o_cfg_overrides
+                    && cargo_script == o_cargo_script
             }
             _ => false,
         }
@@ -865,10 +997,11 @@
 
 fn project_json_to_crate_graph(
     rustc_cfg: Vec<CfgFlag>,
-    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+    load: FileLoader<'_>,
     project: &ProjectJson,
     sysroot: Option<&Sysroot>,
     extra_env: &FxHashMap<String, String>,
+    override_cfg: &CfgOverrides,
 ) -> (CrateGraph, ProcMacroPaths) {
     let mut res = (CrateGraph::default(), ProcMacroPaths::default());
     let (crate_graph, proc_macros) = &mut res;
@@ -878,12 +1011,13 @@
 
     let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned());
     let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
-    let crates: FxHashMap<CrateId, CrateId> = project
+
+    let idx_to_crate_id: FxHashMap<CrateArrayIdx, CrateId> = project
         .crates()
-        .filter_map(|(crate_id, krate)| Some((crate_id, krate, load(&krate.root_module)?)))
+        .filter_map(|(idx, krate)| Some((idx, krate, load(&krate.root_module)?)))
         .map(
             |(
-                crate_id,
+                idx,
                 Crate {
                     display_name,
                     edition,
@@ -907,17 +1041,22 @@
                     None => &rustc_cfg,
                 };
 
+                let mut cfg_options = target_cfgs
+                    .iter()
+                    .chain(cfg.iter())
+                    .chain(iter::once(&r_a_cfg_flag))
+                    .cloned()
+                    .collect();
+                override_cfg.apply(
+                    &mut cfg_options,
+                    display_name.as_ref().map(|it| it.canonical_name()).unwrap_or_default(),
+                );
                 let crate_graph_crate_id = crate_graph.add_crate_root(
                     file_id,
                     *edition,
                     display_name.clone(),
                     version.clone(),
-                    target_cfgs
-                        .iter()
-                        .chain(cfg.iter())
-                        .chain(iter::once(&r_a_cfg_flag))
-                        .cloned()
-                        .collect(),
+                    Arc::new(cfg_options),
                     None,
                     env,
                     *is_proc_macro,
@@ -939,13 +1078,13 @@
                         proc_macros.insert(crate_graph_crate_id, node);
                     }
                 }
-                (crate_id, crate_graph_crate_id)
+                (idx, crate_graph_crate_id)
             },
         )
         .collect();
 
-    for (from, krate) in project.crates() {
-        if let Some(&from) = crates.get(&from) {
+    for (from_idx, krate) in project.crates() {
+        if let Some(&from) = idx_to_crate_id.get(&from_idx) {
             if let Some((public_deps, libproc_macro)) = &sysroot_deps {
                 public_deps.add_to_crate_graph(crate_graph, from);
                 if let Some(proc_macro) = libproc_macro {
@@ -954,8 +1093,8 @@
             }
 
             for dep in &krate.deps {
-                if let Some(&to) = crates.get(&dep.crate_id) {
-                    add_dep(crate_graph, from, dep.name.clone(), to)
+                if let Some(&to) = idx_to_crate_id.get(&dep.krate) {
+                    add_dep(crate_graph, from, dep.name.clone(), to);
                 }
             }
         }
@@ -964,7 +1103,7 @@
 }
 
 fn cargo_to_crate_graph(
-    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+    load: FileLoader<'_>,
     rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
     cargo: &CargoWorkspace,
     sysroot: Option<&Sysroot>,
@@ -981,7 +1120,7 @@
         None => (SysrootPublicDeps::default(), None),
     };
 
-    let cfg_options = create_cfg_options(rustc_cfg);
+    let cfg_options = CfgOptions::from_iter(rustc_cfg);
 
     // Mapping of a package to its library target
     let mut pkg_to_lib_crate = FxHashMap::default();
@@ -996,25 +1135,13 @@
         let cfg_options = {
             let mut cfg_options = cfg_options.clone();
 
-            // Add test cfg for local crates
             if cargo[pkg].is_local {
+                // Add test cfg for local crates
                 cfg_options.insert_atom("test".into());
                 cfg_options.insert_atom("rust_analyzer".into());
             }
 
-            if !override_cfg.global.is_empty() {
-                cfg_options.apply_diff(override_cfg.global.clone());
-            };
-            if let Some(diff) = override_cfg.selective.get(&cargo[pkg].name) {
-                // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
-                // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
-                // working on rust-lang/rust as that's the only time it appears outside sysroot).
-                //
-                // A more ideal solution might be to reanalyze crates based on where the cursor is and
-                // figure out the set of cfgs that would have to apply to make it active.
-
-                cfg_options.apply_diff(diff.clone());
-            };
+            override_cfg.apply(&mut cfg_options, &cargo[pkg].name);
             cfg_options
         };
 
@@ -1150,6 +1277,7 @@
                 &pkg_crates,
                 &cfg_options,
                 override_cfg,
+                // FIXME: Remove this once rustc switched over to rust-project.json
                 if rustc_workspace.workspace_root() == cargo.workspace_root() {
                     // the rustc workspace does not use the installed toolchain's proc-macro server
                     // so we need to make sure we don't use the pre compiled proc-macros there either
@@ -1163,11 +1291,12 @@
     res
 }
 
-fn detached_files_to_crate_graph(
+fn detached_file_to_crate_graph(
     rustc_cfg: Vec<CfgFlag>,
-    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
-    detached_files: &[AbsPathBuf],
+    load: FileLoader<'_>,
+    detached_file: &AbsPathBuf,
     sysroot: Option<&Sysroot>,
+    override_cfg: &CfgOverrides,
 ) -> (CrateGraph, ProcMacroPaths) {
     let _p = tracing::span!(tracing::Level::INFO, "detached_files_to_crate_graph").entered();
     let mut crate_graph = CrateGraph::default();
@@ -1176,37 +1305,38 @@
         None => (SysrootPublicDeps::default(), None),
     };
 
-    let mut cfg_options = create_cfg_options(rustc_cfg);
+    let mut cfg_options = CfgOptions::from_iter(rustc_cfg);
+    cfg_options.insert_atom("test".into());
     cfg_options.insert_atom("rust_analyzer".into());
+    override_cfg.apply(&mut cfg_options, "");
+    let cfg_options = Arc::new(cfg_options);
 
-    for detached_file in detached_files {
-        let file_id = match load(detached_file) {
-            Some(file_id) => file_id,
-            None => {
-                tracing::error!("Failed to load detached file {:?}", detached_file);
-                continue;
-            }
-        };
-        let display_name = detached_file
-            .file_stem()
-            .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
-        let detached_file_crate = crate_graph.add_crate_root(
-            file_id,
-            Edition::CURRENT,
-            display_name.clone(),
-            None,
-            cfg_options.clone(),
-            None,
-            Env::default(),
-            false,
-            CrateOrigin::Local {
-                repo: None,
-                name: display_name.map(|n| n.canonical_name().to_owned()),
-            },
-        );
+    let file_id = match load(detached_file) {
+        Some(file_id) => file_id,
+        None => {
+            tracing::error!("Failed to load detached file {:?}", detached_file);
+            return (crate_graph, FxHashMap::default());
+        }
+    };
+    let display_name = detached_file
+        .file_stem()
+        .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
+    let detached_file_crate = crate_graph.add_crate_root(
+        file_id,
+        Edition::CURRENT,
+        display_name.clone(),
+        None,
+        cfg_options.clone(),
+        None,
+        Env::default(),
+        false,
+        CrateOrigin::Local {
+            repo: None,
+            name: display_name.map(|n| n.canonical_name().to_owned()),
+        },
+    );
 
-        public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
-    }
+    public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
     (crate_graph, FxHashMap::default())
 }
 
@@ -1214,7 +1344,7 @@
     crate_graph: &mut CrateGraph,
     proc_macros: &mut ProcMacroPaths,
     pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
-    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+    load: FileLoader<'_>,
     rustc_workspace: &CargoWorkspace,
     cargo: &CargoWorkspace,
     public_deps: &SysrootPublicDeps,
@@ -1246,20 +1376,7 @@
             }
 
             let mut cfg_options = cfg_options.clone();
-
-            if !override_cfg.global.is_empty() {
-                cfg_options.apply_diff(override_cfg.global.clone());
-            };
-            if let Some(diff) = override_cfg.selective.get(&rustc_workspace[pkg].name) {
-                // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
-                // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
-                // working on rust-lang/rust as that's the only time it appears outside sysroot).
-                //
-                // A more ideal solution might be to reanalyze crates based on where the cursor is and
-                // figure out the set of cfgs that would have to apply to make it active.
-
-                cfg_options.apply_diff(diff.clone());
-            };
+            override_cfg.apply(&mut cfg_options, &rustc_workspace[pkg].name);
 
             for &tgt in rustc_workspace[pkg].targets.iter() {
                 let kind @ TargetKind::Lib { is_proc_macro } = rustc_workspace[tgt].kind else {
@@ -1361,26 +1478,22 @@
     };
 
     let mut env = Env::default();
-    inject_cargo_env(pkg, &mut env);
-    if let Ok(cname) = String::from_str(cargo_name) {
-        // CARGO_CRATE_NAME is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark.
-        env.set("CARGO_CRATE_NAME", cname.replace('-', "_"));
-    }
+    inject_cargo_package_env(&mut env, pkg);
+    inject_cargo_env(&mut env);
+    inject_rustc_tool_env(&mut env, cargo_name, kind);
 
     if let Some(envs) = build_data.map(|it| &it.envs) {
         for (k, v) in envs {
             env.set(k, v.clone());
         }
     }
-
-    let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_owned());
     let crate_id = crate_graph.add_crate_root(
         file_id,
         edition,
-        Some(display_name),
+        Some(CrateDisplayName::from_canonical_name(cargo_name.to_owned())),
         Some(pkg.version.to_string()),
-        cfg_options,
-        potential_cfg_options,
+        Arc::new(cfg_options),
+        potential_cfg_options.map(Arc::new),
         env,
         matches!(kind, TargetKind::Lib { is_proc_macro: true }),
         origin,
@@ -1416,7 +1529,7 @@
     crate_graph: &mut CrateGraph,
     sysroot: &Sysroot,
     rustc_cfg: Vec<CfgFlag>,
-    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+    load: FileLoader<'_>,
 ) -> (SysrootPublicDeps, Option<CrateId>) {
     let _p = tracing::span!(tracing::Level::INFO, "sysroot_to_crate_graph").entered();
     match sysroot.mode() {
@@ -1427,7 +1540,17 @@
                 cargo,
                 None,
                 rustc_cfg,
-                &CfgOverrides::default(),
+                &CfgOverrides {
+                    global: CfgDiff::new(
+                        vec![
+                            CfgAtom::Flag("debug_assertions".into()),
+                            CfgAtom::Flag("miri".into()),
+                        ],
+                        vec![],
+                    )
+                    .unwrap(),
+                    ..Default::default()
+                },
                 &WorkspaceBuildScripts::default(),
             );
 
@@ -1436,7 +1559,7 @@
             let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap();
             for (cid, c) in cg.iter_mut() {
                 // uninject `test` flag so `core` keeps working.
-                c.cfg_options.apply_diff(diff.clone());
+                Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
                 // patch the origin
                 if c.origin.is_local() {
                     let lang_crate = LangCrateOrigin::from(
@@ -1485,13 +1608,18 @@
             (SysrootPublicDeps { deps: pub_deps }, libproc_macro)
         }
         SysrootMode::Stitched(stitched) => {
-            let cfg_options = create_cfg_options(rustc_cfg);
+            let cfg_options = Arc::new({
+                let mut cfg_options = CfgOptions::default();
+                cfg_options.extend(rustc_cfg);
+                cfg_options.insert_atom("debug_assertions".into());
+                cfg_options.insert_atom("miri".into());
+                cfg_options
+            });
             let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = stitched
                 .crates()
                 .filter_map(|krate| {
                     let file_id = load(&stitched[krate].root)?;
 
-                    let env = Env::default();
                     let display_name =
                         CrateDisplayName::from_canonical_name(stitched[krate].name.clone());
                     let crate_id = crate_graph.add_crate_root(
@@ -1501,7 +1629,7 @@
                         None,
                         cfg_options.clone(),
                         None,
-                        env,
+                        Env::default(),
                         false,
                         CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
                     );
@@ -1559,71 +1687,3 @@
         tracing::error!("{}", err)
     }
 }
-
-/// Recreates the compile-time environment variables that Cargo sets.
-///
-/// Should be synced with
-/// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
-///
-/// FIXME: ask Cargo to provide this data instead of re-deriving.
-fn inject_cargo_env(package: &PackageData, env: &mut Env) {
-    // FIXME: Missing variables:
-    // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
-
-    let manifest_dir = package.manifest.parent();
-    env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str().to_owned());
-
-    // Not always right, but works for common cases.
-    env.set("CARGO", "cargo".into());
-
-    env.set("CARGO_PKG_VERSION", package.version.to_string());
-    env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
-    env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
-    env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
-    env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
-
-    env.set("CARGO_PKG_AUTHORS", String::new());
-
-    env.set("CARGO_PKG_NAME", package.name.clone());
-    // FIXME: This isn't really correct (a package can have many crates with different names), but
-    // it's better than leaving the variable unset.
-    env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
-    env.set("CARGO_PKG_DESCRIPTION", String::new());
-    env.set("CARGO_PKG_HOMEPAGE", String::new());
-    env.set("CARGO_PKG_REPOSITORY", String::new());
-    env.set("CARGO_PKG_LICENSE", String::new());
-
-    env.set("CARGO_PKG_LICENSE_FILE", String::new());
-}
-
-fn create_cfg_options(rustc_cfg: Vec<CfgFlag>) -> CfgOptions {
-    let mut cfg_options = CfgOptions::default();
-    cfg_options.extend(rustc_cfg);
-    cfg_options.insert_atom("debug_assertions".into());
-    cfg_options
-}
-
-fn cargo_config_env(
-    cargo_toml: &ManifestPath,
-    extra_env: &FxHashMap<String, String>,
-    sysroot: Option<&Sysroot>,
-) -> FxHashMap<String, String> {
-    let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo);
-    cargo_config.envs(extra_env);
-    cargo_config
-        .current_dir(cargo_toml.parent())
-        .args(["-Z", "unstable-options", "config", "get", "env"])
-        .env("RUSTC_BOOTSTRAP", "1");
-    // if successful we receive `env.key.value = "value" per entry
-    tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
-    utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default()
-}
-
-fn parse_output_cargo_config_env(stdout: String) -> FxHashMap<String, String> {
-    stdout
-        .lines()
-        .filter_map(|l| l.strip_prefix("env."))
-        .filter_map(|l| l.split_once(".value = "))
-        .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned()))
-        .collect()
-}
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
index 0ad19ca..c2a2d6e 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -17,7 +17,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
                 "test",
             ],
@@ -25,20 +24,22 @@
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "hello_world",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -77,7 +78,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
                 "test",
             ],
@@ -85,20 +85,22 @@
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "hello_world",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -144,7 +146,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
                 "test",
             ],
@@ -152,20 +153,22 @@
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "an_example",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -211,7 +214,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
                 "test",
             ],
@@ -219,20 +221,22 @@
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "it",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -278,7 +282,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "feature=default",
                 "feature=std",
             ],
@@ -286,7 +289,6 @@
         potential_cfg_options: Some(
             CfgOptions(
                 [
-                    "debug_assertions",
                     "feature=align",
                     "feature=const-extern-fn",
                     "feature=default",
@@ -299,20 +301,22 @@
         ),
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-                "CARGO_PKG_VERSION": "0.2.98",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "libc",
+                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+                "CARGO_PKG_AUTHORS": "The Rust Project Developers",
+                "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n",
+                "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc",
+                "CARGO_PKG_LICENSE": "MIT OR Apache-2.0",
                 "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
-                "CARGO_PKG_DESCRIPTION": "",
                 "CARGO_PKG_NAME": "libc",
-                "CARGO_PKG_VERSION_PATCH": "98",
-                "CARGO": "cargo",
-                "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_README": "README.md",
+                "CARGO_PKG_REPOSITORY": "https://github.com/rust-lang/libc",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.2.98",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "2",
+                "CARGO_PKG_VERSION_PATCH": "98",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
index 0ad19ca..c2a2d6e 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -17,7 +17,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
                 "test",
             ],
@@ -25,20 +24,22 @@
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "hello_world",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -77,7 +78,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
                 "test",
             ],
@@ -85,20 +85,22 @@
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "hello_world",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -144,7 +146,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
                 "test",
             ],
@@ -152,20 +153,22 @@
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "an_example",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -211,7 +214,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
                 "test",
             ],
@@ -219,20 +221,22 @@
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "it",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -278,7 +282,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "feature=default",
                 "feature=std",
             ],
@@ -286,7 +289,6 @@
         potential_cfg_options: Some(
             CfgOptions(
                 [
-                    "debug_assertions",
                     "feature=align",
                     "feature=const-extern-fn",
                     "feature=default",
@@ -299,20 +301,22 @@
         ),
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-                "CARGO_PKG_VERSION": "0.2.98",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "libc",
+                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+                "CARGO_PKG_AUTHORS": "The Rust Project Developers",
+                "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n",
+                "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc",
+                "CARGO_PKG_LICENSE": "MIT OR Apache-2.0",
                 "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
-                "CARGO_PKG_DESCRIPTION": "",
                 "CARGO_PKG_NAME": "libc",
-                "CARGO_PKG_VERSION_PATCH": "98",
-                "CARGO": "cargo",
-                "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_README": "README.md",
+                "CARGO_PKG_REPOSITORY": "https://github.com/rust-lang/libc",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.2.98",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "2",
+                "CARGO_PKG_VERSION_PATCH": "98",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
index e2334dc..c291ffc 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -17,27 +17,28 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
             ],
         ),
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "hello_world",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -76,27 +77,28 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
             ],
         ),
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "hello_world",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -142,27 +144,28 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
             ],
         ),
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "an_example",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -208,27 +211,28 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "rust_analyzer",
             ],
         ),
         potential_cfg_options: None,
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                "CARGO_PKG_VERSION": "0.1.0",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "it",
-                "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                "CARGO_PKG_AUTHORS": "",
                 "CARGO_PKG_DESCRIPTION": "",
+                "CARGO_PKG_HOMEPAGE": "",
+                "CARGO_PKG_LICENSE": "",
+                "CARGO_PKG_LICENSE_FILE": "",
                 "CARGO_PKG_NAME": "hello-world",
-                "CARGO_PKG_VERSION_PATCH": "0",
-                "CARGO": "cargo",
+                "CARGO_PKG_README": "",
                 "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.1.0",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "1",
+                "CARGO_PKG_VERSION_PATCH": "0",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
@@ -274,7 +278,6 @@
         ),
         cfg_options: CfgOptions(
             [
-                "debug_assertions",
                 "feature=default",
                 "feature=std",
             ],
@@ -282,7 +285,6 @@
         potential_cfg_options: Some(
             CfgOptions(
                 [
-                    "debug_assertions",
                     "feature=align",
                     "feature=const-extern-fn",
                     "feature=default",
@@ -295,20 +297,22 @@
         ),
         env: Env {
             entries: {
-                "CARGO_PKG_LICENSE": "",
-                "CARGO_PKG_VERSION_MAJOR": "0",
-                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-                "CARGO_PKG_VERSION": "0.2.98",
-                "CARGO_PKG_AUTHORS": "",
+                "CARGO": "$CARGO$",
                 "CARGO_CRATE_NAME": "libc",
+                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+                "CARGO_PKG_AUTHORS": "The Rust Project Developers",
+                "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n",
+                "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc",
+                "CARGO_PKG_LICENSE": "MIT OR Apache-2.0",
                 "CARGO_PKG_LICENSE_FILE": "",
-                "CARGO_PKG_HOMEPAGE": "",
-                "CARGO_PKG_DESCRIPTION": "",
                 "CARGO_PKG_NAME": "libc",
-                "CARGO_PKG_VERSION_PATCH": "98",
-                "CARGO": "cargo",
-                "CARGO_PKG_REPOSITORY": "",
+                "CARGO_PKG_README": "README.md",
+                "CARGO_PKG_REPOSITORY": "https://github.com/rust-lang/libc",
+                "CARGO_PKG_RUST_VERSION": "",
+                "CARGO_PKG_VERSION": "0.2.98",
+                "CARGO_PKG_VERSION_MAJOR": "0",
                 "CARGO_PKG_VERSION_MINOR": "2",
+                "CARGO_PKG_VERSION_PATCH": "98",
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
index ccaba96..80c9136 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -16,6 +16,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -53,6 +54,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -82,6 +84,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -111,6 +114,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -140,6 +144,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -184,6 +189,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -213,6 +219,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -299,6 +306,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -328,6 +336,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
@@ -357,6 +366,7 @@
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
+                "miri",
             ],
         ),
         potential_cfg_options: None,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 6d70124..cd33498 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -39,11 +39,13 @@
 tracing-subscriber.workspace = true
 tracing-tree.workspace = true
 triomphe.workspace = true
+toml = "0.8.8"
 nohash-hasher.workspace = true
 always-assert = "0.2.0"
 walkdir = "2.3.2"
 semver.workspace = true
 memchr = "2.7.1"
+indexmap = { workspace = true, features = ["serde"] }
 
 cfg.workspace = true
 flycheck.workspace = true
@@ -92,6 +94,7 @@
     "hir/in-rust-tree",
     "hir-def/in-rust-tree",
     "hir-ty/in-rust-tree",
+    "load-cargo/in-rust-tree",
 ]
 
 [lints]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
index 815a989..6a5f7b0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -33,53 +33,55 @@
         kind: &RunnableKind,
         cfg: &Option<CfgExpr>,
     ) -> (Vec<String>, Vec<String>) {
-        let mut args = Vec::new();
-        let mut extra_args = Vec::new();
+        let extra_test_binary_args = snap.config.runnables().extra_test_binary_args;
+
+        let mut cargo_args = Vec::new();
+        let mut executable_args = Vec::new();
 
         match kind {
             RunnableKind::Test { test_id, attr } => {
-                args.push("test".to_owned());
-                extra_args.push(test_id.to_string());
+                cargo_args.push("test".to_owned());
+                executable_args.push(test_id.to_string());
                 if let TestId::Path(_) = test_id {
-                    extra_args.push("--exact".to_owned());
+                    executable_args.push("--exact".to_owned());
                 }
-                extra_args.push("--nocapture".to_owned());
+                executable_args.extend(extra_test_binary_args);
                 if attr.ignore {
-                    extra_args.push("--ignored".to_owned());
+                    executable_args.push("--ignored".to_owned());
                 }
             }
             RunnableKind::TestMod { path } => {
-                args.push("test".to_owned());
-                extra_args.push(path.clone());
-                extra_args.push("--nocapture".to_owned());
+                cargo_args.push("test".to_owned());
+                executable_args.push(path.clone());
+                executable_args.extend(extra_test_binary_args);
             }
             RunnableKind::Bench { test_id } => {
-                args.push("bench".to_owned());
-                extra_args.push(test_id.to_string());
+                cargo_args.push("bench".to_owned());
+                executable_args.push(test_id.to_string());
                 if let TestId::Path(_) = test_id {
-                    extra_args.push("--exact".to_owned());
+                    executable_args.push("--exact".to_owned());
                 }
-                extra_args.push("--nocapture".to_owned());
+                executable_args.extend(extra_test_binary_args);
             }
             RunnableKind::DocTest { test_id } => {
-                args.push("test".to_owned());
-                args.push("--doc".to_owned());
-                extra_args.push(test_id.to_string());
-                extra_args.push("--nocapture".to_owned());
+                cargo_args.push("test".to_owned());
+                cargo_args.push("--doc".to_owned());
+                executable_args.push(test_id.to_string());
+                executable_args.extend(extra_test_binary_args);
             }
             RunnableKind::Bin => {
                 let subcommand = match spec {
                     Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => "test",
                     _ => "run",
                 };
-                args.push(subcommand.to_owned());
+                cargo_args.push(subcommand.to_owned());
             }
         }
 
         let (allowed_features, target_required_features) = if let Some(mut spec) = spec {
             let allowed_features = mem::take(&mut spec.features);
             let required_features = mem::take(&mut spec.required_features);
-            spec.push_to(&mut args, kind);
+            spec.push_to(&mut cargo_args, kind);
             (allowed_features, required_features)
         } else {
             (Default::default(), Default::default())
@@ -89,10 +91,10 @@
 
         match &cargo_config.features {
             CargoFeatures::All => {
-                args.push("--all-features".to_owned());
+                cargo_args.push("--all-features".to_owned());
                 for feature in target_required_features {
-                    args.push("--features".to_owned());
-                    args.push(feature);
+                    cargo_args.push("--features".to_owned());
+                    cargo_args.push(feature);
                 }
             }
             CargoFeatures::Selected { features, no_default_features } => {
@@ -108,16 +110,16 @@
 
                 feats.dedup();
                 for feature in feats {
-                    args.push("--features".to_owned());
-                    args.push(feature);
+                    cargo_args.push("--features".to_owned());
+                    cargo_args.push(feature);
                 }
 
                 if *no_default_features {
-                    args.push("--no-default-features".to_owned());
+                    cargo_args.push("--no-default-features".to_owned());
                 }
             }
         }
-        (args, extra_args)
+        (cargo_args, executable_args)
     }
 
     pub(crate) fn for_file(
@@ -208,6 +210,7 @@
 mod tests {
     use super::*;
 
+    use ide::Edition;
     use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY};
     use syntax::{
         ast::{self, AstNode},
@@ -216,7 +219,7 @@
 
     fn check(cfg: &str, expected_features: &[&str]) {
         let cfg_expr = {
-            let source_file = ast::SourceFile::parse(cfg).ok().unwrap();
+            let source_file = ast::SourceFile::parse(cfg, Edition::CURRENT).ok().unwrap();
             let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
             let tt = syntax_node_to_token_tree(tt.syntax(), &DummyTestSpanMap, DUMMY);
             CfgExpr::parse(&tt)
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index fdd7719..a1eea88 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -280,7 +280,9 @@
         let mut fail = 0;
         for &a in adts {
             let generic_params = db.generic_params(a.into());
-            if generic_params.iter().next().is_some() || generic_params.iter_lt().next().is_some() {
+            if generic_params.iter_type_or_consts().next().is_some()
+                || generic_params.iter_lt().next().is_some()
+            {
                 // Data types with generics don't have layout.
                 continue;
             }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/parse.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/parse.rs
index 757f2dd..ead4d70 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/parse.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/parse.rs
@@ -1,4 +1,5 @@
 //! Read Rust code on stdin, print syntax tree on stdout.
+use ide::Edition;
 use syntax::{AstNode, SourceFile};
 
 use crate::cli::{flags, read_stdin};
@@ -7,7 +8,7 @@
     pub fn run(self) -> anyhow::Result<()> {
         let _p = tracing::span!(tracing::Level::INFO, "parsing").entered();
         let text = read_stdin()?;
-        let file = SourceFile::parse(&text).tree();
+        let file = SourceFile::parse(&text, Edition::CURRENT).tree();
         if !self.no_dump {
             println!("{:#?}", file.syntax());
         }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
index b233730..6964977 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
@@ -92,7 +92,7 @@
 
         let _ = io::stdout().write(output.as_bytes());
         let _ = io::stdout().flush();
-        self.text = text.to_owned();
+        text.clone_into(&mut self.text);
     }
 
     fn set_value(&mut self, value: f32) {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index eeec13a..2f9394d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -75,12 +75,14 @@
             &cargo_config.extra_env,
         );
 
-        let workspace = ProjectWorkspace::DetachedFiles {
-            files: vec![tmp_file],
+        let workspace = ProjectWorkspace::DetachedFile {
+            file: tmp_file,
             sysroot,
             rustc_cfg: vec![],
             toolchain: None,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+            cfg_overrides: Default::default(),
+            cargo_script: None,
         };
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: false,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 7475a8e..e956791 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -6,21 +6,21 @@
 //! Of particular interest is the `feature_flags` hash map: while other fields
 //! configure the server itself, feature flags are passed into analysis, and
 //! tweak things like automatic insertion of `()` in completions.
-
 use std::{fmt, iter, ops::Not};
 
 use cfg::{CfgAtom, CfgDiff};
-use flycheck::FlycheckConfig;
+use flycheck::{CargoOptions, FlycheckConfig};
 use ide::{
     AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
     HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
     InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
-    Snippet, SnippetScope,
+    Snippet, SnippetScope, SourceRootId,
 };
 use ide_db::{
     imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
     SnippetCap,
 };
+use indexmap::IndexMap;
 use itertools::Itertools;
 use lsp_types::{ClientCapabilities, MarkupKind};
 use paths::{Utf8Path, Utf8PathBuf};
@@ -29,7 +29,7 @@
 };
 use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
-use serde::{de::DeserializeOwned, Deserialize};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use stdx::format_to_acc;
 use vfs::{AbsPath, AbsPathBuf};
 
@@ -59,36 +59,45 @@
 // To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep
 // parsing the old name.
 config_data! {
-    struct ConfigData {
+    /// Configs that apply on a workspace-wide scope. There are 3 levels on which a global configuration can be configured
+    ///
+    /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer.toml)
+    /// 2. Client's own configurations (e.g `settings.json` on VS Code)
+    /// 3. `rust-analyzer.toml` file located at the workspace root
+    ///
+    /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle.
+    global: struct GlobalDefaultConfigData <- GlobalConfigInput -> {
         /// Whether to insert #[must_use] when generating `as_` methods
         /// for enum variants.
-        assist_emitMustUse: bool               = "false",
+        assist_emitMustUse: bool               = false,
         /// Placeholder expression to use for missing expressions in assists.
-        assist_expressionFillDefault: ExprFillDefaultDef              = "\"todo\"",
+        assist_expressionFillDefault: ExprFillDefaultDef              = ExprFillDefaultDef::Todo,
 
         /// Warm up caches on project load.
-        cachePriming_enable: bool = "true",
+        cachePriming_enable: bool = true,
         /// How many worker threads to handle priming caches. The default `0` means to pick automatically.
-        cachePriming_numThreads: ParallelCachePrimingNumThreads = "0",
+        cachePriming_numThreads: ParallelCachePrimingNumThreads = 0u8,
 
+        /// Pass `--all-targets` to cargo invocation.
+        cargo_allTargets: bool           = true,
         /// Automatically refresh project info via `cargo metadata` on
         /// `Cargo.toml` or `.cargo/config.toml` changes.
-        cargo_autoreload: bool           = "true",
+        pub(crate) cargo_autoreload: bool           = true,
         /// Run build scripts (`build.rs`) for more precise code analysis.
-        cargo_buildScripts_enable: bool  = "true",
+        cargo_buildScripts_enable: bool  = true,
         /// Specifies the working directory for running build scripts.
         /// - "workspace": run build scripts for a workspace in the workspace's root directory.
         ///   This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
         /// - "root": run build scripts in the project's root directory.
         /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
         /// is set.
-        cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"",
+        cargo_buildScripts_invocationLocation: InvocationLocation = InvocationLocation::Workspace,
         /// Specifies the invocation strategy to use when running the build scripts command.
         /// If `per_workspace` is set, the command will be executed for each workspace.
         /// If `once` is set, the command will be executed once.
         /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
         /// is set.
-        cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
+        cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace,
         /// Override the command rust-analyzer uses to run build scripts and
         /// build procedural macros. The command is required to output json
         /// and should therefore include `--message-format=json` or a similar
@@ -107,80 +116,84 @@
         /// cargo check --quiet --workspace --message-format=json --all-targets
         /// ```
         /// .
-        cargo_buildScripts_overrideCommand: Option<Vec<String>> = "null",
+        cargo_buildScripts_overrideCommand: Option<Vec<String>> = None,
         /// Rerun proc-macros building/build-scripts running when proc-macro
         /// or build-script sources change and are saved.
-        cargo_buildScripts_rebuildOnSave: bool = "true",
+        cargo_buildScripts_rebuildOnSave: bool = true,
         /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
         /// avoid checking unnecessary things.
-        cargo_buildScripts_useRustcWrapper: bool = "true",
+        cargo_buildScripts_useRustcWrapper: bool = true,
         /// List of cfg options to enable with the given values.
-        cargo_cfgs: FxHashMap<String, String> = "{}",
+        cargo_cfgs: FxHashMap<String, Option<String>> = {
+            let mut m = FxHashMap::default();
+            m.insert("debug_assertions".to_owned(), None);
+            m.insert("miri".to_owned(), None);
+            m
+        },
         /// Extra arguments that are passed to every cargo invocation.
-        cargo_extraArgs: Vec<String> = "[]",
+        cargo_extraArgs: Vec<String> = vec![],
         /// Extra environment variables that will be set when running cargo, rustc
         /// or other commands within the workspace. Useful for setting RUSTFLAGS.
-        cargo_extraEnv: FxHashMap<String, String> = "{}",
+        cargo_extraEnv: FxHashMap<String, String> = FxHashMap::default(),
         /// List of features to activate.
         ///
         /// Set this to `"all"` to pass `--all-features` to cargo.
-        cargo_features: CargoFeaturesDef      = "[]",
+        cargo_features: CargoFeaturesDef      = CargoFeaturesDef::Selected(vec![]),
         /// Whether to pass `--no-default-features` to cargo.
-        cargo_noDefaultFeatures: bool    = "false",
+        cargo_noDefaultFeatures: bool    = false,
         /// Relative path to the sysroot, or "discover" to try to automatically find it via
         /// "rustc --print sysroot".
         ///
         /// Unsetting this disables sysroot loading.
         ///
         /// This option does not take effect until rust-analyzer is restarted.
-        cargo_sysroot: Option<String>    = "\"discover\"",
+        cargo_sysroot: Option<String>    = Some("discover".to_owned()),
         /// Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze
         /// third-party dependencies of the standard libraries.
         ///
         /// This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer
         /// will attempt to clean up afterwards, but nevertheless requires the location to be
         /// writable to.
-        cargo_sysrootQueryMetadata: bool     = "false",
+        cargo_sysrootQueryMetadata: bool     = false,
         /// Relative path to the sysroot library sources. If left unset, this will default to
         /// `{cargo.sysroot}/lib/rustlib/src/rust/library`.
         ///
         /// This option does not take effect until rust-analyzer is restarted.
-        cargo_sysrootSrc: Option<String>    = "null",
+        cargo_sysrootSrc: Option<String>    = None,
         /// Compilation target override (target triple).
         // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
         // than `checkOnSave_target`
-        cargo_target: Option<String>     = "null",
+        cargo_target: Option<String>     = None,
         /// Optional path to a rust-analyzer specific target directory.
         /// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro
         /// building from locking the `Cargo.lock` at the expense of duplicating build artifacts.
         ///
         /// Set to `true` to use a subdirectory of the existing target directory or
         /// set to a path relative to the workspace to use that path.
-        cargo_targetDir | rust_analyzerTargetDir: Option<TargetDirectory> = "null",
-        /// Unsets the implicit `#[cfg(test)]` for the specified crates.
-        cargo_unsetTest: Vec<String>     = "[\"core\"]",
+        cargo_targetDir | rust_analyzerTargetDir: Option<TargetDirectory> = None,
 
         /// Run the check command for diagnostics on save.
-        checkOnSave | checkOnSave_enable: bool                         = "true",
+        checkOnSave | checkOnSave_enable: bool                         = true,
 
-        /// Check all targets and tests (`--all-targets`).
-        check_allTargets | checkOnSave_allTargets: bool                  = "true",
+        /// Check all targets and tests (`--all-targets`). Defaults to
+        /// `#rust-analyzer.cargo.allTargets#`.
+        check_allTargets | checkOnSave_allTargets: Option<bool>          = None,
         /// Cargo command to use for `cargo check`.
-        check_command | checkOnSave_command: String                      = "\"check\"",
+        check_command | checkOnSave_command: String                      = "check".to_owned(),
         /// Extra arguments for `cargo check`.
-        check_extraArgs | checkOnSave_extraArgs: Vec<String>             = "[]",
+        check_extraArgs | checkOnSave_extraArgs: Vec<String>             = vec![],
         /// Extra environment variables that will be set when running `cargo check`.
         /// Extends `#rust-analyzer.cargo.extraEnv#`.
-        check_extraEnv | checkOnSave_extraEnv: FxHashMap<String, String> = "{}",
+        check_extraEnv | checkOnSave_extraEnv: FxHashMap<String, String> = FxHashMap::default(),
         /// List of features to activate. Defaults to
         /// `#rust-analyzer.cargo.features#`.
         ///
         /// Set to `"all"` to pass `--all-features` to Cargo.
-        check_features | checkOnSave_features: Option<CargoFeaturesDef>  = "null",
+        check_features | checkOnSave_features: Option<CargoFeaturesDef>  = None,
         /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
         ///
         /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
-        check_ignore: FxHashSet<String> = "[]",
+        check_ignore: FxHashSet<String> = FxHashSet::default(),
         /// Specifies the working directory for running checks.
         /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
         // FIXME: Ideally we would support this in some way
@@ -188,16 +201,16 @@
         /// - "root": run checks in the project's root directory.
         /// This config only has an effect when `#rust-analyzer.check.overrideCommand#`
         /// is set.
-        check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
+        check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = InvocationLocation::Workspace,
         /// Specifies the invocation strategy to use when running the check command.
         /// If `per_workspace` is set, the command will be executed for each workspace.
         /// If `once` is set, the command will be executed once.
         /// This config only has an effect when `#rust-analyzer.check.overrideCommand#`
         /// is set.
-        check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
+        check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace,
         /// Whether to pass `--no-default-features` to Cargo. Defaults to
         /// `#rust-analyzer.cargo.noDefaultFeatures#`.
-        check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option<bool>         = "null",
+        check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option<bool>         = None,
         /// Override the command rust-analyzer uses instead of `cargo check` for
         /// diagnostics on save. The command is required to output json and
         /// should therefore include `--message-format=json` or a similar option
@@ -225,37 +238,238 @@
         /// cargo check --workspace --message-format=json --all-targets
         /// ```
         /// .
-        check_overrideCommand | checkOnSave_overrideCommand: Option<Vec<String>>             = "null",
+        check_overrideCommand | checkOnSave_overrideCommand: Option<Vec<String>>             = None,
         /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
         ///
         /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
         /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
         ///
         /// Aliased as `"checkOnSave.targets"`.
-        check_targets | checkOnSave_targets | checkOnSave_target: Option<CheckOnSaveTargets> = "null",
+        check_targets | checkOnSave_targets | checkOnSave_target: Option<CheckOnSaveTargets> = None,
         /// Whether `--workspace` should be passed to `cargo check`.
         /// If false, `-p <package>` will be passed instead.
-        check_workspace: bool = "true",
+        check_workspace: bool = true,
 
+        /// List of rust-analyzer diagnostics to disable.
+        diagnostics_disabled: FxHashSet<String> = FxHashSet::default(),
+        /// Whether to show native rust-analyzer diagnostics.
+        diagnostics_enable: bool                = true,
+        /// Whether to show experimental rust-analyzer diagnostics that might
+        /// have more false positives than usual.
+        diagnostics_experimental_enable: bool    = false,
+        /// Map of prefixes to be substituted when parsing diagnostic file paths.
+        /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
+        diagnostics_remapPrefix: FxHashMap<String, String> = FxHashMap::default(),
+        /// Whether to run additional style lints.
+        diagnostics_styleLints_enable: bool =    false,
+        /// List of warnings that should be displayed with hint severity.
+        ///
+        /// The warnings will be indicated by faded text or three dots in code
+        /// and will not show up in the `Problems Panel`.
+        diagnostics_warningsAsHint: Vec<String> = vec![],
+        /// List of warnings that should be displayed with info severity.
+        ///
+        /// The warnings will be indicated by a blue squiggly underline in code
+        /// and a blue icon in the `Problems Panel`.
+        diagnostics_warningsAsInfo: Vec<String> = vec![],
+        /// These directories will be ignored by rust-analyzer. They are
+        /// relative to the workspace root, and globs are not supported. You may
+        /// also need to add the folders to Code's `files.watcherExclude`.
+        files_excludeDirs: Vec<Utf8PathBuf> = vec![],
+        /// Controls file watching implementation.
+        files_watcher: FilesWatcherDef = FilesWatcherDef::Client,
+
+        /// Whether to show `Debug` action. Only applies when
+        /// `#rust-analyzer.hover.actions.enable#` is set.
+        hover_actions_debug_enable: bool           = true,
+        /// Whether to show HoverActions in Rust files.
+        hover_actions_enable: bool          = true,
+        /// Whether to show `Go to Type Definition` action. Only applies when
+        /// `#rust-analyzer.hover.actions.enable#` is set.
+        hover_actions_gotoTypeDef_enable: bool     = true,
+        /// Whether to show `Implementations` action. Only applies when
+        /// `#rust-analyzer.hover.actions.enable#` is set.
+        hover_actions_implementations_enable: bool = true,
+        /// Whether to show `References` action. Only applies when
+        /// `#rust-analyzer.hover.actions.enable#` is set.
+        hover_actions_references_enable: bool      = false,
+        /// Whether to show `Run` action. Only applies when
+        /// `#rust-analyzer.hover.actions.enable#` is set.
+        hover_actions_run_enable: bool             = true,
+
+        /// Whether to show documentation on hover.
+        hover_documentation_enable: bool           = true,
+        /// Whether to show keyword hover popups. Only applies when
+        /// `#rust-analyzer.hover.documentation.enable#` is set.
+        hover_documentation_keywords_enable: bool  = true,
+        /// Use markdown syntax for links on hover.
+        hover_links_enable: bool = true,
+        /// How to render the align information in a memory layout hover.
+        hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
+        /// Whether to show memory layout data on hover.
+        hover_memoryLayout_enable: bool = true,
+        /// How to render the niche information in a memory layout hover.
+        hover_memoryLayout_niches: Option<bool> = Some(false),
+        /// How to render the offset information in a memory layout hover.
+        hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
+        /// How to render the size information in a memory layout hover.
+        hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Both),
+
+        /// How many fields of a struct to display when hovering a struct.
+        hover_show_structFields: Option<usize> = None,
+        /// How many associated items of a trait to display when hovering a trait.
+        hover_show_traitAssocItems: Option<usize> = None,
+
+        /// Enables the experimental support for interpreting tests.
+        interpret_tests: bool                                      = false,
+
+        /// Whether to show `Debug` lens. Only applies when
+        /// `#rust-analyzer.lens.enable#` is set.
+        lens_debug_enable: bool            = true,
+        /// Whether to show CodeLens in Rust files.
+        lens_enable: bool           = true,
+        /// Internal config: use custom client-side commands even when the
+        /// client doesn't set the corresponding capability.
+        lens_forceCustomCommands: bool = true,
+        /// Whether to show `Implementations` lens. Only applies when
+        /// `#rust-analyzer.lens.enable#` is set.
+        lens_implementations_enable: bool  = true,
+        /// Where to render annotations.
+        lens_location: AnnotationLocation = AnnotationLocation::AboveName,
+        /// Whether to show `References` lens for Struct, Enum, and Union.
+        /// Only applies when `#rust-analyzer.lens.enable#` is set.
+        lens_references_adt_enable: bool = false,
+        /// Whether to show `References` lens for Enum Variants.
+        /// Only applies when `#rust-analyzer.lens.enable#` is set.
+        lens_references_enumVariant_enable: bool = false,
+        /// Whether to show `Method References` lens. Only applies when
+        /// `#rust-analyzer.lens.enable#` is set.
+        lens_references_method_enable: bool = false,
+        /// Whether to show `References` lens for Trait.
+        /// Only applies when `#rust-analyzer.lens.enable#` is set.
+        lens_references_trait_enable: bool = false,
+        /// Whether to show `Run` lens. Only applies when
+        /// `#rust-analyzer.lens.enable#` is set.
+        lens_run_enable: bool              = true,
+
+        /// Disable project auto-discovery in favor of explicitly specified set
+        /// of projects.
+        ///
+        /// Elements must be paths pointing to `Cargo.toml`,
+        /// `rust-project.json`, or JSON objects in `rust-project.json` format.
+        linkedProjects: Vec<ManifestOrProjectJson> = vec![],
+
+        /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
+        lru_capacity: Option<usize>                 = None,
+        /// Sets the LRU capacity of the specified queries.
+        lru_query_capacities: FxHashMap<Box<str>, usize> = FxHashMap::default(),
+
+        /// Whether to show `can't find Cargo.toml` error message.
+        notifications_cargoTomlNotFound: bool      = true,
+
+        /// Whether to send an UnindexedProject notification to the client.
+        notifications_unindexedProject: bool      = false,
+
+        /// How many worker threads in the main loop. The default `null` means to pick automatically.
+        numThreads: Option<usize> = None,
+
+        /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
+        procMacro_attributes_enable: bool = true,
+        /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
+        procMacro_enable: bool                     = true,
+        /// These proc-macros will be ignored when trying to expand them.
+        ///
+        /// This config takes a map of crate names with the exported proc-macro names to ignore as values.
+        procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>>          = FxHashMap::default(),
+        /// Internal config, path to proc-macro server executable.
+        procMacro_server: Option<Utf8PathBuf>          = None,
+
+        /// Exclude imports from find-all-references.
+        references_excludeImports: bool = false,
+
+        /// Exclude tests from find-all-references.
+        references_excludeTests: bool = false,
+
+        /// Command to be executed instead of 'cargo' for runnables.
+        runnables_command: Option<String> = None,
+        /// Additional arguments to be passed to cargo for runnables such as
+        /// tests or binaries. For example, it may be `--release`.
+        runnables_extraArgs: Vec<String>   = vec![],
+        /// Additional arguments to be passed through Cargo to launched tests, benchmarks, or
+        /// doc-tests.
+        ///
+        /// Unless the launched target uses a
+        /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),
+        /// they will end up being interpreted as options to
+        /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).
+        runnables_extraTestBinaryArgs: Vec<String> = vec!["--show-output".to_owned()],
+
+        /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
+        /// projects, or "discover" to try to automatically find it if the `rustc-dev` component
+        /// is installed.
+        ///
+        /// Any project which uses rust-analyzer with the rustcPrivate
+        /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
+        ///
+        /// This option does not take effect until rust-analyzer is restarted.
+        rustc_source: Option<String> = None,
+
+        /// Additional arguments to `rustfmt`.
+        rustfmt_extraArgs: Vec<String>               = vec![],
+        /// Advanced option, fully override the command rust-analyzer uses for
+        /// formatting. This should be the equivalent of `rustfmt` here, and
+        /// not that of `cargo fmt`. The file contents will be passed on the
+        /// standard input and the formatted result will be read from the
+        /// standard output.
+        rustfmt_overrideCommand: Option<Vec<String>> = None,
+        /// Enables the use of rustfmt's unstable range formatting command for the
+        /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
+        /// available on a nightly build.
+        rustfmt_rangeFormatting_enable: bool = false,
+
+
+        /// Show full signature of the callable. Only shows parameters if disabled.
+        signatureInfo_detail: SignatureDetail                           = SignatureDetail::Full,
+        /// Show documentation.
+        signatureInfo_documentation_enable: bool                       = true,
+
+        /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
+        typing_autoClosingAngleBrackets_enable: bool = false,
+
+        /// Workspace symbol search kind.
+        workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes,
+        /// Limits the number of items returned from a workspace symbol search (Defaults to 128).
+        /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
+        /// Other clients requires all results upfront and might require a higher limit.
+        workspace_symbol_search_limit: usize = 128,
+        /// Workspace symbol search scope.
+        workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = WorkspaceSymbolSearchScopeDef::Workspace,
+    }
+}
+
+config_data! {
+    /// Local configurations can be overridden for every crate by placing a `rust-analyzer.toml` on crate root.
+    /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle.
+    local: struct LocalDefaultConfigData <- LocalConfigInput ->  {
         /// Toggles the additional completions that automatically add imports when completed.
         /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
-        completion_autoimport_enable: bool       = "true",
+        completion_autoimport_enable: bool       = true,
         /// Toggles the additional completions that automatically show method calls and field accesses
         /// with `self` prefixed to them when inside a method.
-        completion_autoself_enable: bool        = "true",
+        completion_autoself_enable: bool        = true,
         /// Whether to add parenthesis and argument snippets when completing function.
-        completion_callable_snippets: CallableCompletionDef  = "\"fill_arguments\"",
+        completion_callable_snippets: CallableCompletionDef  = CallableCompletionDef::FillArguments,
         /// Whether to show full function/method signatures in completion docs.
-        completion_fullFunctionSignatures_enable: bool = "false",
+        completion_fullFunctionSignatures_enable: bool = false,
         /// Maximum number of completions to return. If `None`, the limit is infinite.
-        completion_limit: Option<usize> = "null",
+        completion_limit: Option<usize> = None,
         /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
-        completion_postfix_enable: bool         = "true",
+        completion_postfix_enable: bool         = true,
         /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
-        completion_privateEditable_enable: bool = "false",
+        completion_privateEditable_enable: bool = false,
         /// Custom completion snippets.
-        // NOTE: Keep this list in sync with the feature docs of user snippets.
-        completion_snippets_custom: FxHashMap<String, SnippetDef> = r#"{
+        // NOTE: we use IndexMap for deterministic serialization ordering
+        completion_snippets_custom: IndexMap<String, SnippetDef> = serde_json::from_str(r#"{
             "Arc::new": {
                 "postfix": "arc",
                 "body": "Arc::new(${receiver})",
@@ -295,323 +509,139 @@
                 "description": "Wrap the expression in an `Option::Some`",
                 "scope": "expr"
             }
-        }"#,
+        }"#).unwrap(),
         /// Whether to enable term search based snippets like `Some(foo.bar().baz())`.
-        completion_termSearch_enable: bool = "false",
-
-        /// List of rust-analyzer diagnostics to disable.
-        diagnostics_disabled: FxHashSet<String> = "[]",
-        /// Whether to show native rust-analyzer diagnostics.
-        diagnostics_enable: bool                = "true",
-        /// Whether to show experimental rust-analyzer diagnostics that might
-        /// have more false positives than usual.
-        diagnostics_experimental_enable: bool    = "false",
-        /// Map of prefixes to be substituted when parsing diagnostic file paths.
-        /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
-        diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
-        /// Whether to run additional style lints.
-        diagnostics_styleLints_enable: bool =    "false",
-        /// List of warnings that should be displayed with hint severity.
-        ///
-        /// The warnings will be indicated by faded text or three dots in code
-        /// and will not show up in the `Problems Panel`.
-        diagnostics_warningsAsHint: Vec<String> = "[]",
-        /// List of warnings that should be displayed with info severity.
-        ///
-        /// The warnings will be indicated by a blue squiggly underline in code
-        /// and a blue icon in the `Problems Panel`.
-        diagnostics_warningsAsInfo: Vec<String> = "[]",
-        /// These directories will be ignored by rust-analyzer. They are
-        /// relative to the workspace root, and globs are not supported. You may
-        /// also need to add the folders to Code's `files.watcherExclude`.
-        files_excludeDirs: Vec<Utf8PathBuf> = "[]",
-        /// Controls file watching implementation.
-        files_watcher: FilesWatcherDef = "\"client\"",
+        completion_termSearch_enable: bool = false,
 
         /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
-        highlightRelated_breakPoints_enable: bool = "true",
+        highlightRelated_breakPoints_enable: bool = true,
         /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
-        highlightRelated_closureCaptures_enable: bool = "true",
+        highlightRelated_closureCaptures_enable: bool = true,
         /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
-        highlightRelated_exitPoints_enable: bool = "true",
+        highlightRelated_exitPoints_enable: bool = true,
         /// Enables highlighting of related references while the cursor is on any identifier.
-        highlightRelated_references_enable: bool = "true",
+        highlightRelated_references_enable: bool = true,
         /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
-        highlightRelated_yieldPoints_enable: bool = "true",
-
-        /// Whether to show `Debug` action. Only applies when
-        /// `#rust-analyzer.hover.actions.enable#` is set.
-        hover_actions_debug_enable: bool           = "true",
-        /// Whether to show HoverActions in Rust files.
-        hover_actions_enable: bool          = "true",
-        /// Whether to show `Go to Type Definition` action. Only applies when
-        /// `#rust-analyzer.hover.actions.enable#` is set.
-        hover_actions_gotoTypeDef_enable: bool     = "true",
-        /// Whether to show `Implementations` action. Only applies when
-        /// `#rust-analyzer.hover.actions.enable#` is set.
-        hover_actions_implementations_enable: bool = "true",
-        /// Whether to show `References` action. Only applies when
-        /// `#rust-analyzer.hover.actions.enable#` is set.
-        hover_actions_references_enable: bool      = "false",
-        /// Whether to show `Run` action. Only applies when
-        /// `#rust-analyzer.hover.actions.enable#` is set.
-        hover_actions_run_enable: bool             = "true",
-
-        /// Whether to show documentation on hover.
-        hover_documentation_enable: bool           = "true",
-        /// Whether to show keyword hover popups. Only applies when
-        /// `#rust-analyzer.hover.documentation.enable#` is set.
-        hover_documentation_keywords_enable: bool  = "true",
-        /// Use markdown syntax for links on hover.
-        hover_links_enable: bool = "true",
-        /// How to render the align information in a memory layout hover.
-        hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
-        /// Whether to show memory layout data on hover.
-        hover_memoryLayout_enable: bool = "true",
-        /// How to render the niche information in a memory layout hover.
-        hover_memoryLayout_niches: Option<bool> = "false",
-        /// How to render the offset information in a memory layout hover.
-        hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
-        /// How to render the size information in a memory layout hover.
-        hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = "\"both\"",
-
-        /// How many fields of a struct to display when hovering a struct.
-        hover_show_structFields: Option<usize> = "null",
-        /// How many associated items of a trait to display when hovering a trait.
-        hover_show_traitAssocItems: Option<usize> = "null",
+        highlightRelated_yieldPoints_enable: bool = true,
 
         /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
-        imports_granularity_enforce: bool              = "false",
+        imports_granularity_enforce: bool              = false,
         /// How imports should be grouped into use statements.
-        imports_granularity_group: ImportGranularityDef  = "\"crate\"",
+        imports_granularity_group: ImportGranularityDef  = ImportGranularityDef::Crate,
         /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
-        imports_group_enable: bool                           = "true",
+        imports_group_enable: bool                           = true,
         /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
-        imports_merge_glob: bool           = "true",
+        imports_merge_glob: bool           = true,
         /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
-        imports_preferNoStd | imports_prefer_no_std: bool = "false",
-        /// Whether to prefer import paths containing a `prelude` module.
-        imports_preferPrelude: bool                       = "false",
+        imports_preferNoStd | imports_prefer_no_std: bool = false,
+         /// Whether to prefer import paths containing a `prelude` module.
+        imports_preferPrelude: bool                       = false,
         /// The path structure for newly inserted paths to use.
-        imports_prefix: ImportPrefixDef               = "\"plain\"",
+        imports_prefix: ImportPrefixDef               = ImportPrefixDef::Plain,
+
 
         /// Whether to show inlay type hints for binding modes.
-        inlayHints_bindingModeHints_enable: bool                   = "false",
+        inlayHints_bindingModeHints_enable: bool                   = false,
         /// Whether to show inlay type hints for method chains.
-        inlayHints_chainingHints_enable: bool                      = "true",
+        inlayHints_chainingHints_enable: bool                      = true,
         /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
-        inlayHints_closingBraceHints_enable: bool                  = "true",
+        inlayHints_closingBraceHints_enable: bool                  = true,
         /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
         /// to always show them).
-        inlayHints_closingBraceHints_minLines: usize               = "25",
+        inlayHints_closingBraceHints_minLines: usize               = 25,
         /// Whether to show inlay hints for closure captures.
-        inlayHints_closureCaptureHints_enable: bool                          = "false",
+        inlayHints_closureCaptureHints_enable: bool                          = false,
         /// Whether to show inlay type hints for return types of closures.
-        inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef  = "\"never\"",
+        inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef  = ClosureReturnTypeHintsDef::Never,
         /// Closure notation in type and chaining inlay hints.
-        inlayHints_closureStyle: ClosureStyle                                = "\"impl_fn\"",
+        inlayHints_closureStyle: ClosureStyle                                = ClosureStyle::ImplFn,
         /// Whether to show enum variant discriminant hints.
-        inlayHints_discriminantHints_enable: DiscriminantHintsDef            = "\"never\"",
+        inlayHints_discriminantHints_enable: DiscriminantHintsDef            = DiscriminantHintsDef::Never,
         /// Whether to show inlay hints for type adjustments.
-        inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"",
+        inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never,
         /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
-        inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false",
+        inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false,
         /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
-        inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"",
+        inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix,
         /// Whether to show implicit drop hints.
-        inlayHints_implicitDrops_enable: bool                      = "false",
+        inlayHints_implicitDrops_enable: bool                      = false,
         /// Whether to show inlay type hints for elided lifetimes in function signatures.
-        inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
+        inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never,
         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
-        inlayHints_lifetimeElisionHints_useParameterNames: bool    = "false",
+        inlayHints_lifetimeElisionHints_useParameterNames: bool    = false,
         /// Maximum length for inlay hints. Set to null to have an unlimited length.
-        inlayHints_maxLength: Option<usize>                        = "25",
+        inlayHints_maxLength: Option<usize>                        = Some(25),
         /// Whether to show function parameter name inlay hints at the call
         /// site.
-        inlayHints_parameterHints_enable: bool                     = "true",
+        inlayHints_parameterHints_enable: bool                     = true,
         /// Whether to show exclusive range inlay hints.
-        inlayHints_rangeExclusiveHints_enable: bool                = "false",
+        inlayHints_rangeExclusiveHints_enable: bool                = false,
         /// Whether to show inlay hints for compiler inserted reborrows.
         /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.
-        inlayHints_reborrowHints_enable: ReborrowHintsDef          = "\"never\"",
+        inlayHints_reborrowHints_enable: ReborrowHintsDef          = ReborrowHintsDef::Never,
         /// Whether to render leading colons for type hints, and trailing colons for parameter hints.
-        inlayHints_renderColons: bool                              = "true",
+        inlayHints_renderColons: bool                              = true,
         /// Whether to show inlay type hints for variables.
-        inlayHints_typeHints_enable: bool                          = "true",
+        inlayHints_typeHints_enable: bool                          = true,
         /// Whether to hide inlay type hints for `let` statements that initialize to a closure.
         /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
-        inlayHints_typeHints_hideClosureInitialization: bool       = "false",
+        inlayHints_typeHints_hideClosureInitialization: bool       = false,
         /// Whether to hide inlay type hints for constructors.
-        inlayHints_typeHints_hideNamedConstructor: bool            = "false",
-        /// Enables the experimental support for interpreting tests.
-        interpret_tests: bool                                      = "false",
+        inlayHints_typeHints_hideNamedConstructor: bool            = false,
+
 
         /// Join lines merges consecutive declaration and initialization of an assignment.
-        joinLines_joinAssignments: bool = "true",
+        joinLines_joinAssignments: bool = true,
         /// Join lines inserts else between consecutive ifs.
-        joinLines_joinElseIf: bool = "true",
+        joinLines_joinElseIf: bool = true,
         /// Join lines removes trailing commas.
-        joinLines_removeTrailingComma: bool = "true",
+        joinLines_removeTrailingComma: bool = true,
         /// Join lines unwraps trivial blocks.
-        joinLines_unwrapTrivialBlock: bool = "true",
-
-
-        /// Whether to show `Debug` lens. Only applies when
-        /// `#rust-analyzer.lens.enable#` is set.
-        lens_debug_enable: bool            = "true",
-        /// Whether to show CodeLens in Rust files.
-        lens_enable: bool           = "true",
-        /// Internal config: use custom client-side commands even when the
-        /// client doesn't set the corresponding capability.
-        lens_forceCustomCommands: bool = "true",
-        /// Whether to show `Implementations` lens. Only applies when
-        /// `#rust-analyzer.lens.enable#` is set.
-        lens_implementations_enable: bool  = "true",
-        /// Where to render annotations.
-        lens_location: AnnotationLocation = "\"above_name\"",
-        /// Whether to show `References` lens for Struct, Enum, and Union.
-        /// Only applies when `#rust-analyzer.lens.enable#` is set.
-        lens_references_adt_enable: bool = "false",
-        /// Whether to show `References` lens for Enum Variants.
-        /// Only applies when `#rust-analyzer.lens.enable#` is set.
-        lens_references_enumVariant_enable: bool = "false",
-        /// Whether to show `Method References` lens. Only applies when
-        /// `#rust-analyzer.lens.enable#` is set.
-        lens_references_method_enable: bool = "false",
-        /// Whether to show `References` lens for Trait.
-        /// Only applies when `#rust-analyzer.lens.enable#` is set.
-        lens_references_trait_enable: bool = "false",
-        /// Whether to show `Run` lens. Only applies when
-        /// `#rust-analyzer.lens.enable#` is set.
-        lens_run_enable: bool              = "true",
-
-        /// Disable project auto-discovery in favor of explicitly specified set
-        /// of projects.
-        ///
-        /// Elements must be paths pointing to `Cargo.toml`,
-        /// `rust-project.json`, or JSON objects in `rust-project.json` format.
-        linkedProjects: Vec<ManifestOrProjectJson> = "[]",
-
-        /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
-        lru_capacity: Option<usize>                 = "null",
-        /// Sets the LRU capacity of the specified queries.
-        lru_query_capacities: FxHashMap<Box<str>, usize> = "{}",
-
-        /// Whether to show `can't find Cargo.toml` error message.
-        notifications_cargoTomlNotFound: bool      = "true",
-
-        /// Whether to send an UnindexedProject notification to the client.
-        notifications_unindexedProject: bool      = "false",
-
-        /// How many worker threads in the main loop. The default `null` means to pick automatically.
-        numThreads: Option<usize> = "null",
-
-        /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
-        procMacro_attributes_enable: bool = "true",
-        /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
-        procMacro_enable: bool                     = "true",
-        /// These proc-macros will be ignored when trying to expand them.
-        ///
-        /// This config takes a map of crate names with the exported proc-macro names to ignore as values.
-        procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>>          = "{}",
-        /// Internal config, path to proc-macro server executable.
-        procMacro_server: Option<Utf8PathBuf>          = "null",
-
-        /// Exclude imports from find-all-references.
-        references_excludeImports: bool = "false",
-
-        /// Exclude tests from find-all-references.
-        references_excludeTests: bool = "false",
-
-        /// Command to be executed instead of 'cargo' for runnables.
-        runnables_command: Option<String> = "null",
-        /// Additional arguments to be passed to cargo for runnables such as
-        /// tests or binaries. For example, it may be `--release`.
-        runnables_extraArgs: Vec<String>   = "[]",
-
-        /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
-        /// projects, or "discover" to try to automatically find it if the `rustc-dev` component
-        /// is installed.
-        ///
-        /// Any project which uses rust-analyzer with the rustcPrivate
-        /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
-        ///
-        /// This option does not take effect until rust-analyzer is restarted.
-        rustc_source: Option<String> = "null",
-
-        /// Additional arguments to `rustfmt`.
-        rustfmt_extraArgs: Vec<String>               = "[]",
-        /// Advanced option, fully override the command rust-analyzer uses for
-        /// formatting. This should be the equivalent of `rustfmt` here, and
-        /// not that of `cargo fmt`. The file contents will be passed on the
-        /// standard input and the formatted result will be read from the
-        /// standard output.
-        rustfmt_overrideCommand: Option<Vec<String>> = "null",
-        /// Enables the use of rustfmt's unstable range formatting command for the
-        /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
-        /// available on a nightly build.
-        rustfmt_rangeFormatting_enable: bool = "false",
+        joinLines_unwrapTrivialBlock: bool = true,
 
         /// Inject additional highlighting into doc comments.
         ///
         /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
         /// doc links.
-        semanticHighlighting_doc_comment_inject_enable: bool = "true",
+        semanticHighlighting_doc_comment_inject_enable: bool = true,
         /// Whether the server is allowed to emit non-standard tokens and modifiers.
-        semanticHighlighting_nonStandardTokens: bool = "true",
+        semanticHighlighting_nonStandardTokens: bool = true,
         /// Use semantic tokens for operators.
         ///
         /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
         /// they are tagged with modifiers.
-        semanticHighlighting_operator_enable: bool = "true",
+        semanticHighlighting_operator_enable: bool = true,
         /// Use specialized semantic tokens for operators.
         ///
         /// When enabled, rust-analyzer will emit special token types for operator tokens instead
         /// of the generic `operator` token type.
-        semanticHighlighting_operator_specialization_enable: bool = "false",
+        semanticHighlighting_operator_specialization_enable: bool = false,
         /// Use semantic tokens for punctuation.
         ///
         /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
         /// they are tagged with modifiers or have a special role.
-        semanticHighlighting_punctuation_enable: bool = "false",
+        semanticHighlighting_punctuation_enable: bool = false,
         /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
         /// calls.
-        semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
+        semanticHighlighting_punctuation_separate_macro_bang: bool = false,
         /// Use specialized semantic tokens for punctuation.
         ///
         /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
         /// of the generic `punctuation` token type.
-        semanticHighlighting_punctuation_specialization_enable: bool = "false",
+        semanticHighlighting_punctuation_specialization_enable: bool = false,
         /// Use semantic tokens for strings.
         ///
         /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
         /// By disabling semantic tokens for strings, other grammars can be used to highlight
         /// their contents.
-        semanticHighlighting_strings_enable: bool = "true",
-
-        /// Show full signature of the callable. Only shows parameters if disabled.
-        signatureInfo_detail: SignatureDetail                           = "\"full\"",
-        /// Show documentation.
-        signatureInfo_documentation_enable: bool                       = "true",
-
-        /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
-        typing_autoClosingAngleBrackets_enable: bool = "false",
-
-        /// Workspace symbol search kind.
-        workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = "\"only_types\"",
-        /// Limits the number of items returned from a workspace symbol search (Defaults to 128).
-        /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
-        /// Other clients requires all results upfront and might require a higher limit.
-        workspace_symbol_search_limit: usize = "128",
-        /// Workspace symbol search scope.
-        workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = "\"workspace\"",
+        semanticHighlighting_strings_enable: bool = true,
     }
 }
 
-impl Default for ConfigData {
-    fn default() -> Self {
-        ConfigData::from_json(serde_json::Value::Null, &mut Vec::new())
-    }
+config_data! {
+    /// Configs that only make sense when they are set by a client. As such they can only be defined
+    /// by setting them using client's settings (e.g `settings.json` on VS Code).
+    client: struct ClientDefaultConfigData <- ClientConfigInput -> {}
 }
 
 #[derive(Debug, Clone)]
@@ -621,10 +651,40 @@
     workspace_roots: Vec<AbsPathBuf>,
     caps: lsp_types::ClientCapabilities,
     root_path: AbsPathBuf,
-    data: ConfigData,
     detached_files: Vec<AbsPathBuf>,
     snippets: Vec<Snippet>,
     visual_studio_code_version: Option<Version>,
+
+    default_config: DefaultConfigData,
+    client_config: FullConfigInput,
+    user_config: GlobalLocalConfigInput,
+    #[allow(dead_code)]
+    ratoml_files: FxHashMap<SourceRootId, RatomlNode>,
+}
+
+#[derive(Clone, Debug)]
+struct RatomlNode {
+    #[allow(dead_code)]
+    node: GlobalLocalConfigInput,
+    #[allow(dead_code)]
+    parent: Option<SourceRootId>,
+}
+
+macro_rules! try_ {
+    ($expr:expr) => {
+        || -> _ { Some($expr) }()
+    };
+}
+macro_rules! try_or {
+    ($expr:expr, $or:expr) => {
+        try_!($expr).unwrap_or($or)
+    };
+}
+
+macro_rules! try_or_def {
+    ($expr:expr) => {
+        try_!($expr).unwrap_or_default()
+    };
 }
 
 type ParallelCachePrimingNumThreads = u8;
@@ -672,7 +732,7 @@
     pub location: AnnotationLocation,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum AnnotationLocation {
     AboveName,
@@ -774,6 +834,8 @@
     pub override_cargo: Option<String>,
     /// Additional arguments for the `cargo`, e.g. `--release`.
     pub cargo_extra_args: Vec<String>,
+    /// Additional arguments for the binary being run, if it is a test or benchmark.
+    pub extra_test_binary_args: Vec<String>,
 }
 
 /// Configuration for workspace symbol search requests.
@@ -827,13 +889,16 @@
     ) -> Self {
         Config {
             caps,
-            data: ConfigData::default(),
             detached_files: Vec::new(),
             discovered_projects: Vec::new(),
             root_path,
             snippets: Default::default(),
             workspace_roots,
             visual_studio_code_version,
+            client_config: FullConfigInput::default(),
+            user_config: GlobalLocalConfigInput::default(),
+            ratoml_files: FxHashMap::default(),
+            default_config: DefaultConfigData::default(),
         }
     }
 
@@ -863,15 +928,19 @@
         }
         let mut errors = Vec::new();
         self.detached_files =
-            get_field::<Vec<Utf8PathBuf>>(&mut json, &mut errors, "detachedFiles", None, "[]")
+            get_field::<Vec<Utf8PathBuf>>(&mut json, &mut errors, "detachedFiles", None)
+                .unwrap_or_default()
                 .into_iter()
                 .map(AbsPathBuf::assert)
                 .collect();
         patch_old_style::patch_json_for_outdated_configs(&mut json);
-        self.data = ConfigData::from_json(json, &mut errors);
-        tracing::debug!("deserialized config data: {:#?}", self.data);
+        self.client_config = FullConfigInput::from_json(json, &mut errors);
+        tracing::debug!(?self.client_config, "deserialized config data");
         self.snippets.clear();
-        for (name, def) in self.data.completion_snippets_custom.iter() {
+
+        let snips = self.completion_snippets_custom(None).to_owned();
+
+        for (name, def) in snips.iter() {
             if def.prefix.is_empty() && def.postfix.is_empty() {
                 continue;
             }
@@ -909,7 +978,7 @@
 
     fn validate(&self, error_sink: &mut Vec<(String, serde_json::Error)>) {
         use serde::de::Error;
-        if self.data.check_command.is_empty() {
+        if self.check_command().is_empty() {
             error_sink.push((
                 "/check/command".to_owned(),
                 serde_json::Error::custom("expected a non-empty string"),
@@ -918,7 +987,7 @@
     }
 
     pub fn json_schema() -> serde_json::Value {
-        ConfigData::json_schema()
+        FullConfigInput::json_schema()
     }
 
     pub fn root_path(&self) -> &AbsPathBuf {
@@ -934,44 +1003,302 @@
     }
 }
 
-macro_rules! try_ {
-    ($expr:expr) => {
-        || -> _ { Some($expr) }()
-    };
-}
-macro_rules! try_or {
-    ($expr:expr, $or:expr) => {
-        try_!($expr).unwrap_or($or)
-    };
-}
-
-macro_rules! try_or_def {
-    ($expr:expr) => {
-        try_!($expr).unwrap_or_default()
-    };
-}
-
 impl Config {
+    pub fn assist(&self, source_root: Option<SourceRootId>) -> AssistConfig {
+        AssistConfig {
+            snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
+            allowed: None,
+            insert_use: self.insert_use_config(source_root),
+            prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
+            assist_emit_must_use: self.assist_emitMustUse().to_owned(),
+            prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
+        }
+    }
+
+    pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig {
+        CompletionConfig {
+            enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(),
+            enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned()
+                && completion_item_edit_resolve(&self.caps),
+            enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(),
+            enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(),
+            full_function_signatures: self
+                .completion_fullFunctionSignatures_enable(source_root)
+                .to_owned(),
+            callable: match self.completion_callable_snippets(source_root) {
+                CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments),
+                CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
+                CallableCompletionDef::None => None,
+            },
+            insert_use: self.insert_use_config(source_root),
+            prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
+            snippet_cap: SnippetCap::new(try_or_def!(
+                self.caps
+                    .text_document
+                    .as_ref()?
+                    .completion
+                    .as_ref()?
+                    .completion_item
+                    .as_ref()?
+                    .snippet_support?
+            )),
+            snippets: self.snippets.clone().to_vec(),
+            limit: self.completion_limit(source_root).to_owned(),
+            enable_term_search: self.completion_termSearch_enable(source_root).to_owned(),
+            prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
+        }
+    }
+
+    pub fn diagnostics(&self, source_root: Option<SourceRootId>) -> DiagnosticsConfig {
+        DiagnosticsConfig {
+            enabled: *self.diagnostics_enable(),
+            proc_attr_macros_enabled: self.expand_proc_attr_macros(),
+            proc_macros_enabled: *self.procMacro_enable(),
+            disable_experimental: !self.diagnostics_experimental_enable(),
+            disabled: self.diagnostics_disabled().clone(),
+            expr_fill_default: match self.assist_expressionFillDefault() {
+                ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
+                ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
+            },
+            insert_use: self.insert_use_config(source_root),
+            prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
+            prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
+            style_lints: self.diagnostics_styleLints_enable().to_owned(),
+        }
+    }
+    pub fn expand_proc_attr_macros(&self) -> bool {
+        self.procMacro_enable().to_owned() && self.procMacro_attributes_enable().to_owned()
+    }
+
+    pub fn highlight_related(&self, source_root: Option<SourceRootId>) -> HighlightRelatedConfig {
+        HighlightRelatedConfig {
+            references: self.highlightRelated_references_enable(source_root).to_owned(),
+            break_points: self.highlightRelated_breakPoints_enable(source_root).to_owned(),
+            exit_points: self.highlightRelated_exitPoints_enable(source_root).to_owned(),
+            yield_points: self.highlightRelated_yieldPoints_enable(source_root).to_owned(),
+            closure_captures: self.highlightRelated_closureCaptures_enable(source_root).to_owned(),
+        }
+    }
+
+    pub fn hover_actions(&self) -> HoverActionsConfig {
+        let enable = self.experimental("hoverActions") && self.hover_actions_enable().to_owned();
+        HoverActionsConfig {
+            implementations: enable && self.hover_actions_implementations_enable().to_owned(),
+            references: enable && self.hover_actions_references_enable().to_owned(),
+            run: enable && self.hover_actions_run_enable().to_owned(),
+            debug: enable && self.hover_actions_debug_enable().to_owned(),
+            goto_type_def: enable && self.hover_actions_gotoTypeDef_enable().to_owned(),
+        }
+    }
+
+    pub fn hover(&self) -> HoverConfig {
+        let mem_kind = |kind| match kind {
+            MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
+            MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
+            MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal,
+        };
+        HoverConfig {
+            links_in_hover: self.hover_links_enable().to_owned(),
+            memory_layout: self.hover_memoryLayout_enable().then_some(MemoryLayoutHoverConfig {
+                size: self.hover_memoryLayout_size().map(mem_kind),
+                offset: self.hover_memoryLayout_offset().map(mem_kind),
+                alignment: self.hover_memoryLayout_alignment().map(mem_kind),
+                niches: self.hover_memoryLayout_niches().unwrap_or_default(),
+            }),
+            documentation: self.hover_documentation_enable().to_owned(),
+            format: {
+                let is_markdown = try_or_def!(self
+                    .caps
+                    .text_document
+                    .as_ref()?
+                    .hover
+                    .as_ref()?
+                    .content_format
+                    .as_ref()?
+                    .as_slice())
+                .contains(&MarkupKind::Markdown);
+                if is_markdown {
+                    HoverDocFormat::Markdown
+                } else {
+                    HoverDocFormat::PlainText
+                }
+            },
+            keywords: self.hover_documentation_keywords_enable().to_owned(),
+            max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(),
+            max_struct_field_count: self.hover_show_structFields().to_owned(),
+        }
+    }
+
+    pub fn inlay_hints(&self, source_root: Option<SourceRootId>) -> InlayHintsConfig {
+        let client_capability_fields = self
+            .caps
+            .text_document
+            .as_ref()
+            .and_then(|text| text.inlay_hint.as_ref())
+            .and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
+            .map(|inlay_resolve| inlay_resolve.properties.iter())
+            .into_iter()
+            .flatten()
+            .cloned()
+            .collect::<FxHashSet<_>>();
+
+        InlayHintsConfig {
+            render_colons: self.inlayHints_renderColons(source_root).to_owned(),
+            type_hints: self.inlayHints_typeHints_enable(source_root).to_owned(),
+            parameter_hints: self.inlayHints_parameterHints_enable(source_root).to_owned(),
+            chaining_hints: self.inlayHints_chainingHints_enable(source_root).to_owned(),
+            discriminant_hints: match self.inlayHints_discriminantHints_enable(source_root) {
+                DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
+                DiscriminantHintsDef::Never => ide::DiscriminantHints::Never,
+                DiscriminantHintsDef::Fieldless => ide::DiscriminantHints::Fieldless,
+            },
+            closure_return_type_hints: match self
+                .inlayHints_closureReturnTypeHints_enable(source_root)
+            {
+                ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always,
+                ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never,
+                ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock,
+            },
+            lifetime_elision_hints: match self.inlayHints_lifetimeElisionHints_enable(source_root) {
+                LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always,
+                LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never,
+                LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial,
+            },
+            hide_named_constructor_hints: self
+                .inlayHints_typeHints_hideNamedConstructor(source_root)
+                .to_owned(),
+            hide_closure_initialization_hints: self
+                .inlayHints_typeHints_hideClosureInitialization(source_root)
+                .to_owned(),
+            closure_style: match self.inlayHints_closureStyle(source_root) {
+                ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
+                ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
+                ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId,
+                ClosureStyle::Hide => hir::ClosureStyle::Hide,
+            },
+            closure_capture_hints: self
+                .inlayHints_closureCaptureHints_enable(source_root)
+                .to_owned(),
+            adjustment_hints: match self.inlayHints_expressionAdjustmentHints_enable(source_root) {
+                AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
+                AdjustmentHintsDef::Never => {
+                    match self.inlayHints_reborrowHints_enable(source_root) {
+                        ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => {
+                            ide::AdjustmentHints::ReborrowOnly
+                        }
+                        ReborrowHintsDef::Never => ide::AdjustmentHints::Never,
+                    }
+                }
+                AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly,
+            },
+            adjustment_hints_mode: match self.inlayHints_expressionAdjustmentHints_mode(source_root)
+            {
+                AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix,
+                AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix,
+                AdjustmentHintsModeDef::PreferPrefix => ide::AdjustmentHintsMode::PreferPrefix,
+                AdjustmentHintsModeDef::PreferPostfix => ide::AdjustmentHintsMode::PreferPostfix,
+            },
+            adjustment_hints_hide_outside_unsafe: self
+                .inlayHints_expressionAdjustmentHints_hideOutsideUnsafe(source_root)
+                .to_owned(),
+            binding_mode_hints: self.inlayHints_bindingModeHints_enable(source_root).to_owned(),
+            param_names_for_lifetime_elision_hints: self
+                .inlayHints_lifetimeElisionHints_useParameterNames(source_root)
+                .to_owned(),
+            max_length: self.inlayHints_maxLength(source_root).to_owned(),
+            closing_brace_hints_min_lines: if self
+                .inlayHints_closingBraceHints_enable(source_root)
+                .to_owned()
+            {
+                Some(self.inlayHints_closingBraceHints_minLines(source_root).to_owned())
+            } else {
+                None
+            },
+            fields_to_resolve: InlayFieldsToResolve {
+                resolve_text_edits: client_capability_fields.contains("textEdits"),
+                resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
+                resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
+                resolve_label_location: client_capability_fields.contains("label.location"),
+                resolve_label_command: client_capability_fields.contains("label.command"),
+            },
+            implicit_drop_hints: self.inlayHints_implicitDrops_enable(source_root).to_owned(),
+            range_exclusive_hints: self
+                .inlayHints_rangeExclusiveHints_enable(source_root)
+                .to_owned(),
+        }
+    }
+
+    fn insert_use_config(&self, source_root: Option<SourceRootId>) -> InsertUseConfig {
+        InsertUseConfig {
+            granularity: match self.imports_granularity_group(source_root) {
+                ImportGranularityDef::Preserve => ImportGranularity::Preserve,
+                ImportGranularityDef::Item => ImportGranularity::Item,
+                ImportGranularityDef::Crate => ImportGranularity::Crate,
+                ImportGranularityDef::Module => ImportGranularity::Module,
+                ImportGranularityDef::One => ImportGranularity::One,
+            },
+            enforce_granularity: self.imports_granularity_enforce(source_root).to_owned(),
+            prefix_kind: match self.imports_prefix(source_root) {
+                ImportPrefixDef::Plain => PrefixKind::Plain,
+                ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
+                ImportPrefixDef::BySelf => PrefixKind::BySelf,
+            },
+            group: self.imports_group_enable(source_root).to_owned(),
+            skip_glob_imports: !self.imports_merge_glob(source_root),
+        }
+    }
+
+    pub fn join_lines(&self, source_root: Option<SourceRootId>) -> JoinLinesConfig {
+        JoinLinesConfig {
+            join_else_if: self.joinLines_joinElseIf(source_root).to_owned(),
+            remove_trailing_comma: self.joinLines_removeTrailingComma(source_root).to_owned(),
+            unwrap_trivial_blocks: self.joinLines_unwrapTrivialBlock(source_root).to_owned(),
+            join_assignments: self.joinLines_joinAssignments(source_root).to_owned(),
+        }
+    }
+
+    pub fn highlighting_non_standard_tokens(&self, source_root: Option<SourceRootId>) -> bool {
+        self.semanticHighlighting_nonStandardTokens(source_root).to_owned()
+    }
+
+    pub fn highlighting_config(&self, source_root: Option<SourceRootId>) -> HighlightConfig {
+        HighlightConfig {
+            strings: self.semanticHighlighting_strings_enable(source_root).to_owned(),
+            punctuation: self.semanticHighlighting_punctuation_enable(source_root).to_owned(),
+            specialize_punctuation: self
+                .semanticHighlighting_punctuation_specialization_enable(source_root)
+                .to_owned(),
+            macro_bang: self
+                .semanticHighlighting_punctuation_separate_macro_bang(source_root)
+                .to_owned(),
+            operator: self.semanticHighlighting_operator_enable(source_root).to_owned(),
+            specialize_operator: self
+                .semanticHighlighting_operator_specialization_enable(source_root)
+                .to_owned(),
+            inject_doc_comment: self
+                .semanticHighlighting_doc_comment_inject_enable(source_root)
+                .to_owned(),
+            syntactic_name_ref_highlighting: false,
+        }
+    }
+
     pub fn has_linked_projects(&self) -> bool {
-        !self.data.linkedProjects.is_empty()
+        !self.linkedProjects().is_empty()
     }
     pub fn linked_manifests(&self) -> impl Iterator<Item = &Utf8Path> + '_ {
-        self.data.linkedProjects.iter().filter_map(|it| match it {
+        self.linkedProjects().iter().filter_map(|it| match it {
             ManifestOrProjectJson::Manifest(p) => Some(&**p),
             ManifestOrProjectJson::ProjectJson(_) => None,
         })
     }
     pub fn has_linked_project_jsons(&self) -> bool {
-        self.data
-            .linkedProjects
-            .iter()
-            .any(|it| matches!(it, ManifestOrProjectJson::ProjectJson(_)))
+        self.linkedProjects().iter().any(|it| matches!(it, ManifestOrProjectJson::ProjectJson(_)))
     }
     pub fn linked_or_discovered_projects(&self) -> Vec<LinkedProject> {
-        match self.data.linkedProjects.as_slice() {
+        match self.linkedProjects().as_slice() {
             [] => {
                 let exclude_dirs: Vec<_> =
-                    self.data.files_excludeDirs.iter().map(|p| self.root_path.join(p)).collect();
+                    self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect();
                 self.discovered_projects
                     .iter()
                     .filter(
@@ -1025,7 +1352,7 @@
     }
 
     pub fn prefill_caches(&self) -> bool {
-        self.data.cachePriming_enable
+        self.cachePriming_enable().to_owned()
     }
 
     pub fn location_link(&self) -> bool {
@@ -1162,117 +1489,95 @@
     }
 
     pub fn publish_diagnostics(&self) -> bool {
-        self.data.diagnostics_enable
-    }
-
-    pub fn diagnostics(&self) -> DiagnosticsConfig {
-        DiagnosticsConfig {
-            enabled: self.data.diagnostics_enable,
-            proc_attr_macros_enabled: self.expand_proc_attr_macros(),
-            proc_macros_enabled: self.data.procMacro_enable,
-            disable_experimental: !self.data.diagnostics_experimental_enable,
-            disabled: self.data.diagnostics_disabled.clone(),
-            expr_fill_default: match self.data.assist_expressionFillDefault {
-                ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
-                ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
-            },
-            insert_use: self.insert_use_config(),
-            prefer_no_std: self.data.imports_preferNoStd,
-            prefer_prelude: self.data.imports_preferPrelude,
-            style_lints: self.data.diagnostics_styleLints_enable,
-        }
+        self.diagnostics_enable().to_owned()
     }
 
     pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
         DiagnosticsMapConfig {
-            remap_prefix: self.data.diagnostics_remapPrefix.clone(),
-            warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
-            warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
-            check_ignore: self.data.check_ignore.clone(),
+            remap_prefix: self.diagnostics_remapPrefix().clone(),
+            warnings_as_info: self.diagnostics_warningsAsInfo().clone(),
+            warnings_as_hint: self.diagnostics_warningsAsHint().clone(),
+            check_ignore: self.check_ignore().clone(),
         }
     }
 
     pub fn extra_args(&self) -> &Vec<String> {
-        &self.data.cargo_extraArgs
+        self.cargo_extraArgs()
     }
 
     pub fn extra_env(&self) -> &FxHashMap<String, String> {
-        &self.data.cargo_extraEnv
+        self.cargo_extraEnv()
     }
 
     pub fn check_extra_args(&self) -> Vec<String> {
         let mut extra_args = self.extra_args().clone();
-        extra_args.extend_from_slice(&self.data.check_extraArgs);
+        extra_args.extend_from_slice(self.check_extraArgs());
         extra_args
     }
 
     pub fn check_extra_env(&self) -> FxHashMap<String, String> {
-        let mut extra_env = self.data.cargo_extraEnv.clone();
-        extra_env.extend(self.data.check_extraEnv.clone());
+        let mut extra_env = self.cargo_extraEnv().clone();
+        extra_env.extend(self.check_extraEnv().clone());
         extra_env
     }
 
     pub fn lru_parse_query_capacity(&self) -> Option<usize> {
-        self.data.lru_capacity
+        self.lru_capacity().to_owned()
     }
 
-    pub fn lru_query_capacities(&self) -> Option<&FxHashMap<Box<str>, usize>> {
-        self.data.lru_query_capacities.is_empty().not().then_some(&self.data.lru_query_capacities)
+    pub fn lru_query_capacities_config(&self) -> Option<&FxHashMap<Box<str>, usize>> {
+        self.lru_query_capacities().is_empty().not().then(|| self.lru_query_capacities())
     }
 
     pub fn proc_macro_srv(&self) -> Option<AbsPathBuf> {
-        let path = self.data.procMacro_server.clone()?;
+        let path = self.procMacro_server().clone()?;
         Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
     }
 
     pub fn ignored_proc_macros(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
-        &self.data.procMacro_ignored
+        self.procMacro_ignored()
     }
 
     pub fn expand_proc_macros(&self) -> bool {
-        self.data.procMacro_enable
-    }
-
-    pub fn expand_proc_attr_macros(&self) -> bool {
-        self.data.procMacro_enable && self.data.procMacro_attributes_enable
+        self.procMacro_enable().to_owned()
     }
 
     pub fn files(&self) -> FilesConfig {
         FilesConfig {
-            watcher: match self.data.files_watcher {
+            watcher: match self.files_watcher() {
                 FilesWatcherDef::Client if self.did_change_watched_files_dynamic_registration() => {
                     FilesWatcher::Client
                 }
                 _ => FilesWatcher::Server,
             },
-            exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(),
+            exclude: self.files_excludeDirs().iter().map(|it| self.root_path.join(it)).collect(),
         }
     }
 
     pub fn notifications(&self) -> NotificationsConfig {
         NotificationsConfig {
-            cargo_toml_not_found: self.data.notifications_cargoTomlNotFound,
-            unindexed_project: self.data.notifications_unindexedProject,
+            cargo_toml_not_found: self.notifications_cargoTomlNotFound().to_owned(),
+            unindexed_project: self.notifications_unindexedProject().to_owned(),
         }
     }
 
-    pub fn cargo_autoreload(&self) -> bool {
-        self.data.cargo_autoreload
+    pub fn cargo_autoreload_config(&self) -> bool {
+        self.cargo_autoreload().to_owned()
     }
 
     pub fn run_build_scripts(&self) -> bool {
-        self.data.cargo_buildScripts_enable || self.data.procMacro_enable
+        self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned()
     }
 
     pub fn cargo(&self) -> CargoConfig {
-        let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| {
+        let rustc_source = self.rustc_source().as_ref().map(|rustc_src| {
             if rustc_src == "discover" {
                 RustLibSource::Discover
             } else {
                 RustLibSource::Path(self.root_path.join(rustc_src))
             }
         });
-        let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| {
+        let sysroot = self.cargo_sysroot().as_ref().map(|sysroot| {
             if sysroot == "discover" {
                 RustLibSource::Discover
             } else {
@@ -1280,88 +1585,91 @@
             }
         });
         let sysroot_src =
-            self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot));
-        let sysroot_query_metadata = self.data.cargo_sysrootQueryMetadata;
+            self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot));
+        let sysroot_query_metadata = self.cargo_sysrootQueryMetadata();
 
         CargoConfig {
-            features: match &self.data.cargo_features {
+            all_targets: *self.cargo_allTargets(),
+            features: match &self.cargo_features() {
                 CargoFeaturesDef::All => CargoFeatures::All,
                 CargoFeaturesDef::Selected(features) => CargoFeatures::Selected {
                     features: features.clone(),
-                    no_default_features: self.data.cargo_noDefaultFeatures,
+                    no_default_features: self.cargo_noDefaultFeatures().to_owned(),
                 },
             },
-            target: self.data.cargo_target.clone(),
+            target: self.cargo_target().clone(),
             sysroot,
-            sysroot_query_metadata,
+            sysroot_query_metadata: *sysroot_query_metadata,
             sysroot_src,
             rustc_source,
             cfg_overrides: project_model::CfgOverrides {
                 global: CfgDiff::new(
-                    self.data
-                        .cargo_cfgs
+                    self.cargo_cfgs()
                         .iter()
-                        .map(|(key, val)| {
-                            if val.is_empty() {
-                                CfgAtom::Flag(key.into())
-                            } else {
-                                CfgAtom::KeyValue { key: key.into(), value: val.into() }
-                            }
+                        .map(|(key, val)| match val {
+                            Some(val) => CfgAtom::KeyValue { key: key.into(), value: val.into() },
+                            None => CfgAtom::Flag(key.into()),
                         })
                         .collect(),
                     vec![],
                 )
                 .unwrap(),
-                selective: self
-                    .data
-                    .cargo_unsetTest
-                    .iter()
-                    .map(|it| {
-                        (
-                            it.clone(),
-                            CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(),
-                        )
-                    })
-                    .collect(),
+                selective: Default::default(),
             },
-            wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
-            invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy {
+            wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(),
+            invocation_strategy: match self.cargo_buildScripts_invocationStrategy() {
                 InvocationStrategy::Once => project_model::InvocationStrategy::Once,
                 InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace,
             },
-            invocation_location: match self.data.cargo_buildScripts_invocationLocation {
+            invocation_location: match self.cargo_buildScripts_invocationLocation() {
                 InvocationLocation::Root => {
                     project_model::InvocationLocation::Root(self.root_path.clone())
                 }
                 InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
             },
-            run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
-            extra_args: self.data.cargo_extraArgs.clone(),
-            extra_env: self.data.cargo_extraEnv.clone(),
+            run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(),
+            extra_args: self.cargo_extraArgs().clone(),
+            extra_env: self.cargo_extraEnv().clone(),
             target_dir: self.target_dir_from_config(),
         }
     }
 
     pub fn rustfmt(&self) -> RustfmtConfig {
-        match &self.data.rustfmt_overrideCommand {
+        match &self.rustfmt_overrideCommand() {
             Some(args) if !args.is_empty() => {
                 let mut args = args.clone();
                 let command = args.remove(0);
                 RustfmtConfig::CustomCommand { command, args }
             }
             Some(_) | None => RustfmtConfig::Rustfmt {
-                extra_args: self.data.rustfmt_extraArgs.clone(),
-                enable_range_formatting: self.data.rustfmt_rangeFormatting_enable,
+                extra_args: self.rustfmt_extraArgs().clone(),
+                enable_range_formatting: *self.rustfmt_rangeFormatting_enable(),
             },
         }
     }
 
     pub fn flycheck_workspace(&self) -> bool {
-        self.data.check_workspace
+        *self.check_workspace()
+    }
+
+    pub fn cargo_test_options(&self) -> CargoOptions {
+        CargoOptions {
+            target_triples: self.cargo_target().clone().into_iter().collect(),
+            all_targets: false,
+            no_default_features: *self.cargo_noDefaultFeatures(),
+            all_features: matches!(self.cargo_features(), CargoFeaturesDef::All),
+            features: match self.cargo_features().clone() {
+                CargoFeaturesDef::All => vec![],
+                CargoFeaturesDef::Selected(it) => it,
+            },
+            extra_args: self.extra_args().clone(),
+            extra_env: self.extra_env().clone(),
+            target_dir: self.target_dir_from_config(),
+        }
     }
 
     pub fn flycheck(&self) -> FlycheckConfig {
-        match &self.data.check_overrideCommand {
+        match &self.check_overrideCommand() {
             Some(args) if !args.is_empty() => {
                 let mut args = args.clone();
                 let command = args.remove(0);
@@ -1369,13 +1677,13 @@
                     command,
                     args,
                     extra_env: self.check_extra_env(),
-                    invocation_strategy: match self.data.check_invocationStrategy {
+                    invocation_strategy: match self.check_invocationStrategy() {
                         InvocationStrategy::Once => flycheck::InvocationStrategy::Once,
                         InvocationStrategy::PerWorkspace => {
                             flycheck::InvocationStrategy::PerWorkspace
                         }
                     },
-                    invocation_location: match self.data.check_invocationLocation {
+                    invocation_location: match self.check_invocationLocation() {
                         InvocationLocation::Root => {
                             flycheck::InvocationLocation::Root(self.root_path.clone())
                         }
@@ -1384,44 +1692,43 @@
                 }
             }
             Some(_) | None => FlycheckConfig::CargoCommand {
-                command: self.data.check_command.clone(),
-                target_triples: self
-                    .data
-                    .check_targets
-                    .clone()
-                    .and_then(|targets| match &targets.0[..] {
-                        [] => None,
-                        targets => Some(targets.into()),
-                    })
-                    .unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()),
-                all_targets: self.data.check_allTargets,
-                no_default_features: self
-                    .data
-                    .check_noDefaultFeatures
-                    .unwrap_or(self.data.cargo_noDefaultFeatures),
-                all_features: matches!(
-                    self.data.check_features.as_ref().unwrap_or(&self.data.cargo_features),
-                    CargoFeaturesDef::All
-                ),
-                features: match self
-                    .data
-                    .check_features
-                    .clone()
-                    .unwrap_or_else(|| self.data.cargo_features.clone())
-                {
-                    CargoFeaturesDef::All => vec![],
-                    CargoFeaturesDef::Selected(it) => it,
+                command: self.check_command().clone(),
+                options: CargoOptions {
+                    target_triples: self
+                        .check_targets()
+                        .clone()
+                        .and_then(|targets| match &targets.0[..] {
+                            [] => None,
+                            targets => Some(targets.into()),
+                        })
+                        .unwrap_or_else(|| self.cargo_target().clone().into_iter().collect()),
+                    all_targets: self.check_allTargets().unwrap_or(*self.cargo_allTargets()),
+                    no_default_features: self
+                        .check_noDefaultFeatures()
+                        .unwrap_or(*self.cargo_noDefaultFeatures()),
+                    all_features: matches!(
+                        self.check_features().as_ref().unwrap_or(self.cargo_features()),
+                        CargoFeaturesDef::All
+                    ),
+                    features: match self
+                        .check_features()
+                        .clone()
+                        .unwrap_or_else(|| self.cargo_features().clone())
+                    {
+                        CargoFeaturesDef::All => vec![],
+                        CargoFeaturesDef::Selected(it) => it,
+                    },
+                    extra_args: self.check_extra_args(),
+                    extra_env: self.check_extra_env(),
+                    target_dir: self.target_dir_from_config(),
                 },
-                extra_args: self.check_extra_args(),
-                extra_env: self.check_extra_env(),
                 ansi_color_output: self.color_diagnostic_output(),
-                target_dir: self.target_dir_from_config(),
             },
         }
     }
 
     fn target_dir_from_config(&self) -> Option<Utf8PathBuf> {
-        self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir {
+        self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir {
             TargetDirectory::UseSubdirectory(true) => {
                 Some(Utf8PathBuf::from("target/rust-analyzer"))
             }
@@ -1432,294 +1739,67 @@
     }
 
     pub fn check_on_save(&self) -> bool {
-        self.data.checkOnSave
+        *self.checkOnSave()
     }
 
     pub fn script_rebuild_on_save(&self) -> bool {
-        self.data.cargo_buildScripts_rebuildOnSave
+        *self.cargo_buildScripts_rebuildOnSave()
     }
 
     pub fn runnables(&self) -> RunnablesConfig {
         RunnablesConfig {
-            override_cargo: self.data.runnables_command.clone(),
-            cargo_extra_args: self.data.runnables_extraArgs.clone(),
-        }
-    }
-
-    pub fn inlay_hints(&self) -> InlayHintsConfig {
-        let client_capability_fields = self
-            .caps
-            .text_document
-            .as_ref()
-            .and_then(|text| text.inlay_hint.as_ref())
-            .and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
-            .map(|inlay_resolve| inlay_resolve.properties.iter())
-            .into_iter()
-            .flatten()
-            .cloned()
-            .collect::<FxHashSet<_>>();
-
-        InlayHintsConfig {
-            render_colons: self.data.inlayHints_renderColons,
-            type_hints: self.data.inlayHints_typeHints_enable,
-            parameter_hints: self.data.inlayHints_parameterHints_enable,
-            chaining_hints: self.data.inlayHints_chainingHints_enable,
-            implicit_drop_hints: self.data.inlayHints_implicitDrops_enable,
-            discriminant_hints: match self.data.inlayHints_discriminantHints_enable {
-                DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
-                DiscriminantHintsDef::Never => ide::DiscriminantHints::Never,
-                DiscriminantHintsDef::Fieldless => ide::DiscriminantHints::Fieldless,
-            },
-            closure_return_type_hints: match self.data.inlayHints_closureReturnTypeHints_enable {
-                ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always,
-                ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never,
-                ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock,
-            },
-            lifetime_elision_hints: match self.data.inlayHints_lifetimeElisionHints_enable {
-                LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always,
-                LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never,
-                LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial,
-            },
-            hide_named_constructor_hints: self.data.inlayHints_typeHints_hideNamedConstructor,
-            hide_closure_initialization_hints: self
-                .data
-                .inlayHints_typeHints_hideClosureInitialization,
-            closure_style: match self.data.inlayHints_closureStyle {
-                ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
-                ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
-                ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId,
-                ClosureStyle::Hide => hir::ClosureStyle::Hide,
-            },
-            closure_capture_hints: self.data.inlayHints_closureCaptureHints_enable,
-            adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable {
-                AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
-                AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable {
-                    ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => {
-                        ide::AdjustmentHints::ReborrowOnly
-                    }
-                    ReborrowHintsDef::Never => ide::AdjustmentHints::Never,
-                },
-                AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly,
-            },
-            adjustment_hints_mode: match self.data.inlayHints_expressionAdjustmentHints_mode {
-                AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix,
-                AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix,
-                AdjustmentHintsModeDef::PreferPrefix => ide::AdjustmentHintsMode::PreferPrefix,
-                AdjustmentHintsModeDef::PreferPostfix => ide::AdjustmentHintsMode::PreferPostfix,
-            },
-            adjustment_hints_hide_outside_unsafe: self
-                .data
-                .inlayHints_expressionAdjustmentHints_hideOutsideUnsafe,
-            binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
-            param_names_for_lifetime_elision_hints: self
-                .data
-                .inlayHints_lifetimeElisionHints_useParameterNames,
-            max_length: self.data.inlayHints_maxLength,
-            closing_brace_hints_min_lines: if self.data.inlayHints_closingBraceHints_enable {
-                Some(self.data.inlayHints_closingBraceHints_minLines)
-            } else {
-                None
-            },
-            range_exclusive_hints: self.data.inlayHints_rangeExclusiveHints_enable,
-            fields_to_resolve: InlayFieldsToResolve {
-                resolve_text_edits: client_capability_fields.contains("textEdits"),
-                resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
-                resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
-                resolve_label_location: client_capability_fields.contains("label.location"),
-                resolve_label_command: client_capability_fields.contains("label.command"),
-            },
-        }
-    }
-
-    fn insert_use_config(&self) -> InsertUseConfig {
-        InsertUseConfig {
-            granularity: match self.data.imports_granularity_group {
-                ImportGranularityDef::Preserve => ImportGranularity::Preserve,
-                ImportGranularityDef::Item => ImportGranularity::Item,
-                ImportGranularityDef::Crate => ImportGranularity::Crate,
-                ImportGranularityDef::Module => ImportGranularity::Module,
-                ImportGranularityDef::One => ImportGranularity::One,
-            },
-            enforce_granularity: self.data.imports_granularity_enforce,
-            prefix_kind: match self.data.imports_prefix {
-                ImportPrefixDef::Plain => PrefixKind::Plain,
-                ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
-                ImportPrefixDef::BySelf => PrefixKind::BySelf,
-            },
-            group: self.data.imports_group_enable,
-            skip_glob_imports: !self.data.imports_merge_glob,
-        }
-    }
-
-    pub fn completion(&self) -> CompletionConfig {
-        CompletionConfig {
-            enable_postfix_completions: self.data.completion_postfix_enable,
-            enable_imports_on_the_fly: self.data.completion_autoimport_enable
-                && completion_item_edit_resolve(&self.caps),
-            enable_self_on_the_fly: self.data.completion_autoself_enable,
-            enable_private_editable: self.data.completion_privateEditable_enable,
-            enable_term_search: self.data.completion_termSearch_enable,
-            full_function_signatures: self.data.completion_fullFunctionSignatures_enable,
-            callable: match self.data.completion_callable_snippets {
-                CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments),
-                CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
-                CallableCompletionDef::None => None,
-            },
-            insert_use: self.insert_use_config(),
-            prefer_no_std: self.data.imports_preferNoStd,
-            prefer_prelude: self.data.imports_preferPrelude,
-            snippet_cap: SnippetCap::new(try_or_def!(
-                self.caps
-                    .text_document
-                    .as_ref()?
-                    .completion
-                    .as_ref()?
-                    .completion_item
-                    .as_ref()?
-                    .snippet_support?
-            )),
-            snippets: self.snippets.clone(),
-            limit: self.data.completion_limit,
+            override_cargo: self.runnables_command().clone(),
+            cargo_extra_args: self.runnables_extraArgs().clone(),
+            extra_test_binary_args: self.runnables_extraTestBinaryArgs().clone(),
         }
     }
 
     pub fn find_all_refs_exclude_imports(&self) -> bool {
-        self.data.references_excludeImports
+        *self.references_excludeImports()
     }
 
     pub fn find_all_refs_exclude_tests(&self) -> bool {
-        self.data.references_excludeTests
+        *self.references_excludeTests()
     }
 
     pub fn snippet_cap(&self) -> bool {
         self.experimental("snippetTextEdit")
     }
 
-    pub fn assist(&self) -> AssistConfig {
-        AssistConfig {
-            snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
-            allowed: None,
-            insert_use: self.insert_use_config(),
-            prefer_no_std: self.data.imports_preferNoStd,
-            prefer_prelude: self.data.imports_preferPrelude,
-            assist_emit_must_use: self.data.assist_emitMustUse,
-        }
-    }
-
-    pub fn join_lines(&self) -> JoinLinesConfig {
-        JoinLinesConfig {
-            join_else_if: self.data.joinLines_joinElseIf,
-            remove_trailing_comma: self.data.joinLines_removeTrailingComma,
-            unwrap_trivial_blocks: self.data.joinLines_unwrapTrivialBlock,
-            join_assignments: self.data.joinLines_joinAssignments,
-        }
-    }
-
     pub fn call_info(&self) -> CallInfoConfig {
         CallInfoConfig {
-            params_only: matches!(self.data.signatureInfo_detail, SignatureDetail::Parameters),
-            docs: self.data.signatureInfo_documentation_enable,
+            params_only: matches!(self.signatureInfo_detail(), SignatureDetail::Parameters),
+            docs: *self.signatureInfo_documentation_enable(),
         }
     }
 
     pub fn lens(&self) -> LensConfig {
         LensConfig {
-            run: self.data.lens_enable && self.data.lens_run_enable,
-            debug: self.data.lens_enable && self.data.lens_debug_enable,
-            interpret: self.data.lens_enable
-                && self.data.lens_run_enable
-                && self.data.interpret_tests,
-            implementations: self.data.lens_enable && self.data.lens_implementations_enable,
-            method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
-            refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
-            refs_trait: self.data.lens_enable && self.data.lens_references_trait_enable,
-            enum_variant_refs: self.data.lens_enable
-                && self.data.lens_references_enumVariant_enable,
-            location: self.data.lens_location,
-        }
-    }
-
-    pub fn hover_actions(&self) -> HoverActionsConfig {
-        let enable = self.experimental("hoverActions") && self.data.hover_actions_enable;
-        HoverActionsConfig {
-            implementations: enable && self.data.hover_actions_implementations_enable,
-            references: enable && self.data.hover_actions_references_enable,
-            run: enable && self.data.hover_actions_run_enable,
-            debug: enable && self.data.hover_actions_debug_enable,
-            goto_type_def: enable && self.data.hover_actions_gotoTypeDef_enable,
-        }
-    }
-
-    pub fn highlighting_non_standard_tokens(&self) -> bool {
-        self.data.semanticHighlighting_nonStandardTokens
-    }
-
-    pub fn highlighting_config(&self) -> HighlightConfig {
-        HighlightConfig {
-            strings: self.data.semanticHighlighting_strings_enable,
-            punctuation: self.data.semanticHighlighting_punctuation_enable,
-            specialize_punctuation: self
-                .data
-                .semanticHighlighting_punctuation_specialization_enable,
-            macro_bang: self.data.semanticHighlighting_punctuation_separate_macro_bang,
-            operator: self.data.semanticHighlighting_operator_enable,
-            specialize_operator: self.data.semanticHighlighting_operator_specialization_enable,
-            inject_doc_comment: self.data.semanticHighlighting_doc_comment_inject_enable,
-            syntactic_name_ref_highlighting: false,
-        }
-    }
-
-    pub fn hover(&self) -> HoverConfig {
-        let mem_kind = |kind| match kind {
-            MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
-            MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
-            MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal,
-        };
-        HoverConfig {
-            links_in_hover: self.data.hover_links_enable,
-            memory_layout: self.data.hover_memoryLayout_enable.then_some(MemoryLayoutHoverConfig {
-                size: self.data.hover_memoryLayout_size.map(mem_kind),
-                offset: self.data.hover_memoryLayout_offset.map(mem_kind),
-                alignment: self.data.hover_memoryLayout_alignment.map(mem_kind),
-                niches: self.data.hover_memoryLayout_niches.unwrap_or_default(),
-            }),
-            documentation: self.data.hover_documentation_enable,
-            format: {
-                let is_markdown = try_or_def!(self
-                    .caps
-                    .text_document
-                    .as_ref()?
-                    .hover
-                    .as_ref()?
-                    .content_format
-                    .as_ref()?
-                    .as_slice())
-                .contains(&MarkupKind::Markdown);
-                if is_markdown {
-                    HoverDocFormat::Markdown
-                } else {
-                    HoverDocFormat::PlainText
-                }
-            },
-            keywords: self.data.hover_documentation_keywords_enable,
-            max_trait_assoc_items_count: self.data.hover_show_traitAssocItems,
-            max_struct_field_count: self.data.hover_show_structFields,
+            run: *self.lens_run_enable(),
+            debug: *self.lens_enable() && *self.lens_debug_enable(),
+            interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(),
+            implementations: *self.lens_enable() && *self.lens_implementations_enable(),
+            method_refs: *self.lens_enable() && *self.lens_references_method_enable(),
+            refs_adt: *self.lens_enable() && *self.lens_references_adt_enable(),
+            refs_trait: *self.lens_enable() && *self.lens_references_trait_enable(),
+            enum_variant_refs: *self.lens_enable() && *self.lens_references_enumVariant_enable(),
+            location: *self.lens_location(),
         }
     }
 
     pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig {
         WorkspaceSymbolConfig {
-            search_scope: match self.data.workspace_symbol_search_scope {
+            search_scope: match self.workspace_symbol_search_scope() {
                 WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
                 WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
                     WorkspaceSymbolSearchScope::WorkspaceAndDependencies
                 }
             },
-            search_kind: match self.data.workspace_symbol_search_kind {
+            search_kind: match self.workspace_symbol_search_kind() {
                 WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes,
                 WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols,
             },
-            search_limit: self.data.workspace_symbol_search_limit,
+            search_limit: *self.workspace_symbol_search_limit(),
         }
     }
 
@@ -1753,7 +1833,7 @@
             try_or!(self.caps.experimental.as_ref()?.get("commands")?, &serde_json::Value::Null);
         let commands: Option<lsp_ext::ClientCommandOptions> =
             serde_json::from_value(commands.clone()).ok();
-        let force = commands.is_none() && self.data.lens_forceCustomCommands;
+        let force = commands.is_none() && *self.lens_forceCustomCommands();
         let commands = commands.map(|it| it.commands).unwrap_or_default();
 
         let get = |name: &str| commands.iter().any(|it| it == name) || force;
@@ -1767,29 +1847,19 @@
         }
     }
 
-    pub fn highlight_related(&self) -> HighlightRelatedConfig {
-        HighlightRelatedConfig {
-            references: self.data.highlightRelated_references_enable,
-            break_points: self.data.highlightRelated_breakPoints_enable,
-            exit_points: self.data.highlightRelated_exitPoints_enable,
-            yield_points: self.data.highlightRelated_yieldPoints_enable,
-            closure_captures: self.data.highlightRelated_closureCaptures_enable,
-        }
-    }
-
     pub fn prime_caches_num_threads(&self) -> u8 {
-        match self.data.cachePriming_numThreads {
+        match *self.cachePriming_numThreads() {
             0 => num_cpus::get_physical().try_into().unwrap_or(u8::MAX),
             n => n,
         }
     }
 
     pub fn main_loop_num_threads(&self) -> usize {
-        self.data.numThreads.unwrap_or(num_cpus::get_physical())
+        self.numThreads().unwrap_or(num_cpus::get_physical())
     }
 
     pub fn typing_autoclose_angle(&self) -> bool {
-        self.data.typing_autoClosingAngleBrackets_enable
+        *self.typing_autoClosingAngleBrackets_enable()
     }
 
     // VSCode is our reference implementation, so we allow ourselves to work around issues by
@@ -1800,100 +1870,120 @@
 }
 // Deserialization definitions
 
-macro_rules! create_bool_or_string_de {
+macro_rules! create_bool_or_string_serde {
     ($ident:ident<$bool:literal, $string:literal>) => {
-        fn $ident<'de, D>(d: D) -> Result<(), D::Error>
-        where
-            D: serde::Deserializer<'de>,
-        {
-            struct V;
-            impl<'de> serde::de::Visitor<'de> for V {
-                type Value = ();
+        mod $ident {
+            pub(super) fn deserialize<'de, D>(d: D) -> Result<(), D::Error>
+            where
+                D: serde::Deserializer<'de>,
+            {
+                struct V;
+                impl<'de> serde::de::Visitor<'de> for V {
+                    type Value = ();
 
-                fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-                    formatter.write_str(concat!(
-                        stringify!($bool),
-                        " or \"",
-                        stringify!($string),
-                        "\""
-                    ))
-                }
+                    fn expecting(
+                        &self,
+                        formatter: &mut std::fmt::Formatter<'_>,
+                    ) -> std::fmt::Result {
+                        formatter.write_str(concat!(
+                            stringify!($bool),
+                            " or \"",
+                            stringify!($string),
+                            "\""
+                        ))
+                    }
 
-                fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
-                where
-                    E: serde::de::Error,
-                {
-                    match v {
-                        $bool => Ok(()),
-                        _ => Err(serde::de::Error::invalid_value(
-                            serde::de::Unexpected::Bool(v),
-                            &self,
-                        )),
+                    fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
+                    where
+                        E: serde::de::Error,
+                    {
+                        match v {
+                            $bool => Ok(()),
+                            _ => Err(serde::de::Error::invalid_value(
+                                serde::de::Unexpected::Bool(v),
+                                &self,
+                            )),
+                        }
+                    }
+
+                    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+                    where
+                        E: serde::de::Error,
+                    {
+                        match v {
+                            $string => Ok(()),
+                            _ => Err(serde::de::Error::invalid_value(
+                                serde::de::Unexpected::Str(v),
+                                &self,
+                            )),
+                        }
+                    }
+
+                    fn visit_enum<A>(self, a: A) -> Result<Self::Value, A::Error>
+                    where
+                        A: serde::de::EnumAccess<'de>,
+                    {
+                        use serde::de::VariantAccess;
+                        let (variant, va) = a.variant::<&'de str>()?;
+                        va.unit_variant()?;
+                        match variant {
+                            $string => Ok(()),
+                            _ => Err(serde::de::Error::invalid_value(
+                                serde::de::Unexpected::Str(variant),
+                                &self,
+                            )),
+                        }
                     }
                 }
-
-                fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
-                where
-                    E: serde::de::Error,
-                {
-                    match v {
-                        $string => Ok(()),
-                        _ => Err(serde::de::Error::invalid_value(
-                            serde::de::Unexpected::Str(v),
-                            &self,
-                        )),
-                    }
-                }
-
-                fn visit_enum<A>(self, a: A) -> Result<Self::Value, A::Error>
-                where
-                    A: serde::de::EnumAccess<'de>,
-                {
-                    use serde::de::VariantAccess;
-                    let (variant, va) = a.variant::<&'de str>()?;
-                    va.unit_variant()?;
-                    match variant {
-                        $string => Ok(()),
-                        _ => Err(serde::de::Error::invalid_value(
-                            serde::de::Unexpected::Str(variant),
-                            &self,
-                        )),
-                    }
-                }
+                d.deserialize_any(V)
             }
-            d.deserialize_any(V)
+
+            pub(super) fn serialize<S>(serializer: S) -> Result<S::Ok, S::Error>
+            where
+                S: serde::Serializer,
+            {
+                serializer.serialize_str($string)
+            }
         }
     };
 }
-create_bool_or_string_de!(true_or_always<true, "always">);
-create_bool_or_string_de!(false_or_never<false, "never">);
+create_bool_or_string_serde!(true_or_always<true, "always">);
+create_bool_or_string_serde!(false_or_never<false, "never">);
 
 macro_rules! named_unit_variant {
     ($variant:ident) => {
-        pub(super) fn $variant<'de, D>(deserializer: D) -> Result<(), D::Error>
-        where
-            D: serde::Deserializer<'de>,
-        {
-            struct V;
-            impl<'de> serde::de::Visitor<'de> for V {
-                type Value = ();
-                fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-                    f.write_str(concat!("\"", stringify!($variant), "\""))
-                }
-                fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
-                    if value == stringify!($variant) {
-                        Ok(())
-                    } else {
-                        Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
+        pub(super) mod $variant {
+            pub(in super::super) fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error>
+            where
+                D: serde::Deserializer<'de>,
+            {
+                struct V;
+                impl<'de> serde::de::Visitor<'de> for V {
+                    type Value = ();
+                    fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                        f.write_str(concat!("\"", stringify!($variant), "\""))
+                    }
+                    fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
+                        if value == stringify!($variant) {
+                            Ok(())
+                        } else {
+                            Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
+                        }
                     }
                 }
+                deserializer.deserialize_str(V)
             }
-            deserializer.deserialize_str(V)
+            pub(in super::super) fn serialize<S>(serializer: S) -> Result<S::Ok, S::Error>
+            where
+                S: serde::Serializer,
+            {
+                serializer.serialize_str(stringify!($variant))
+            }
         }
     };
 }
 
-mod de_unit_v {
+mod unit_v {
     named_unit_variant!(all);
     named_unit_variant!(skip_trivial);
     named_unit_variant!(mutable);
@@ -1905,7 +1995,7 @@
     named_unit_variant!(both);
 }
 
-#[derive(Deserialize, Debug, Clone, Copy)]
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
 #[serde(rename_all = "snake_case")]
 #[derive(Default)]
 enum SnippetScopeDef {
@@ -1915,67 +2005,92 @@
     Type,
 }
 
-#[derive(Deserialize, Debug, Clone, Default)]
+#[derive(Serialize, Deserialize, Debug, Clone, Default)]
 #[serde(default)]
 struct SnippetDef {
-    #[serde(deserialize_with = "single_or_array")]
+    #[serde(with = "single_or_array")]
+    #[serde(skip_serializing_if = "Vec::is_empty")]
     prefix: Vec<String>,
-    #[serde(deserialize_with = "single_or_array")]
+
+    #[serde(with = "single_or_array")]
+    #[serde(skip_serializing_if = "Vec::is_empty")]
     postfix: Vec<String>,
-    description: Option<String>,
-    #[serde(deserialize_with = "single_or_array")]
+
+    #[serde(with = "single_or_array")]
+    #[serde(skip_serializing_if = "Vec::is_empty")]
     body: Vec<String>,
-    #[serde(deserialize_with = "single_or_array")]
+
+    #[serde(with = "single_or_array")]
+    #[serde(skip_serializing_if = "Vec::is_empty")]
     requires: Vec<String>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    description: Option<String>,
+
     scope: SnippetScopeDef,
 }
 
-fn single_or_array<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
-where
-    D: serde::Deserializer<'de>,
-{
-    struct SingleOrVec;
+mod single_or_array {
+    use serde::{Deserialize, Serialize};
 
-    impl<'de> serde::de::Visitor<'de> for SingleOrVec {
-        type Value = Vec<String>;
+    pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        struct SingleOrVec;
 
-        fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-            formatter.write_str("string or array of strings")
+        impl<'de> serde::de::Visitor<'de> for SingleOrVec {
+            type Value = Vec<String>;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                formatter.write_str("string or array of strings")
+            }
+
+            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                Ok(vec![value.to_owned()])
+            }
+
+            fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
+            where
+                A: serde::de::SeqAccess<'de>,
+            {
+                Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))
+            }
         }
 
-        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
-        where
-            E: serde::de::Error,
-        {
-            Ok(vec![value.to_owned()])
-        }
-
-        fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
-        where
-            A: serde::de::SeqAccess<'de>,
-        {
-            Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))
-        }
+        deserializer.deserialize_any(SingleOrVec)
     }
 
-    deserializer.deserialize_any(SingleOrVec)
+    pub(super) fn serialize<S>(vec: &[String], serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        match vec {
+            // []  case is handled by skip_serializing_if
+            [single] => serializer.serialize_str(single),
+            slice => slice.serialize(serializer),
+        }
+    }
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum ManifestOrProjectJson {
     Manifest(Utf8PathBuf),
     ProjectJson(ProjectJsonData),
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum ExprFillDefaultDef {
     Todo,
     Default,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum ImportGranularityDef {
     Preserve,
@@ -1985,7 +2100,7 @@
     One,
 }
 
-#[derive(Deserialize, Debug, Copy, Clone)]
+#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
 #[serde(rename_all = "snake_case")]
 enum CallableCompletionDef {
     FillArguments,
@@ -1993,54 +2108,54 @@
     None,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum CargoFeaturesDef {
-    #[serde(deserialize_with = "de_unit_v::all")]
+    #[serde(with = "unit_v::all")]
     All,
     Selected(Vec<String>),
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
-enum InvocationStrategy {
+pub(crate) enum InvocationStrategy {
     Once,
     PerWorkspace,
 }
 
-#[derive(Deserialize, Debug, Clone)]
-struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec<String>);
+#[derive(Serialize, Deserialize, Debug, Clone)]
+struct CheckOnSaveTargets(#[serde(with = "single_or_array")] Vec<String>);
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum InvocationLocation {
     Root,
     Workspace,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum LifetimeElisionDef {
-    #[serde(deserialize_with = "true_or_always")]
+    #[serde(with = "true_or_always")]
     Always,
-    #[serde(deserialize_with = "false_or_never")]
+    #[serde(with = "false_or_never")]
     Never,
-    #[serde(deserialize_with = "de_unit_v::skip_trivial")]
+    #[serde(with = "unit_v::skip_trivial")]
     SkipTrivial,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum ClosureReturnTypeHintsDef {
-    #[serde(deserialize_with = "true_or_always")]
+    #[serde(with = "true_or_always")]
     Always,
-    #[serde(deserialize_with = "false_or_never")]
+    #[serde(with = "false_or_never")]
     Never,
-    #[serde(deserialize_with = "de_unit_v::with_block")]
+    #[serde(with = "unit_v::with_block")]
     WithBlock,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum ClosureStyle {
     ImplFn,
@@ -2049,40 +2164,40 @@
     Hide,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum ReborrowHintsDef {
-    #[serde(deserialize_with = "true_or_always")]
+    #[serde(with = "true_or_always")]
     Always,
-    #[serde(deserialize_with = "false_or_never")]
+    #[serde(with = "false_or_never")]
     Never,
-    #[serde(deserialize_with = "de_unit_v::mutable")]
+    #[serde(with = "unit_v::mutable")]
     Mutable,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum AdjustmentHintsDef {
-    #[serde(deserialize_with = "true_or_always")]
+    #[serde(with = "true_or_always")]
     Always,
-    #[serde(deserialize_with = "false_or_never")]
+    #[serde(with = "false_or_never")]
     Never,
-    #[serde(deserialize_with = "de_unit_v::reborrow")]
+    #[serde(with = "unit_v::reborrow")]
     Reborrow,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum DiscriminantHintsDef {
-    #[serde(deserialize_with = "true_or_always")]
+    #[serde(with = "true_or_always")]
     Always,
-    #[serde(deserialize_with = "false_or_never")]
+    #[serde(with = "false_or_never")]
     Never,
-    #[serde(deserialize_with = "de_unit_v::fieldless")]
+    #[serde(with = "unit_v::fieldless")]
     Fieldless,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum AdjustmentHintsModeDef {
     Prefix,
@@ -2091,7 +2206,7 @@
     PreferPostfix,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum FilesWatcherDef {
     Client,
@@ -2099,7 +2214,7 @@
     Server,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum ImportPrefixDef {
     Plain,
@@ -2109,40 +2224,51 @@
     ByCrate,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum WorkspaceSymbolSearchScopeDef {
     Workspace,
     WorkspaceAndDependencies,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum SignatureDetail {
     Full,
     Parameters,
 }
 
-#[derive(Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum WorkspaceSymbolSearchKindDef {
     OnlyTypes,
     AllSymbols,
 }
 
-#[derive(Deserialize, Debug, Copy, Clone)]
+#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
 #[serde(rename_all = "snake_case")]
 #[serde(untagged)]
-pub enum MemoryLayoutHoverRenderKindDef {
-    #[serde(deserialize_with = "de_unit_v::decimal")]
+enum MemoryLayoutHoverRenderKindDef {
+    #[serde(with = "unit_v::decimal")]
     Decimal,
-    #[serde(deserialize_with = "de_unit_v::hexadecimal")]
+    #[serde(with = "unit_v::hexadecimal")]
     Hexadecimal,
-    #[serde(deserialize_with = "de_unit_v::both")]
+    #[serde(with = "unit_v::both")]
     Both,
 }
 
-#[derive(Deserialize, Debug, Clone, PartialEq)]
+#[test]
+fn untagged_option_hover_render_kind() {
+    let hex = MemoryLayoutHoverRenderKindDef::Hexadecimal;
+
+    let ser = serde_json::to_string(&Some(hex)).unwrap();
+    assert_eq!(&ser, "\"hexadecimal\"");
+
+    let opt: Option<_> = serde_json::from_str("\"hexadecimal\"").unwrap();
+    assert_eq!(opt, Some(hex));
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
 #[serde(rename_all = "snake_case")]
 #[serde(untagged)]
 pub enum TargetDirectory {
@@ -2150,68 +2276,326 @@
     Directory(Utf8PathBuf),
 }
 
+macro_rules! _default_val {
+    (@verbatim: $s:literal, $ty:ty) => {{
+        let default_: $ty = serde_json::from_str(&$s).unwrap();
+        default_
+    }};
+    ($default:expr, $ty:ty) => {{
+        let default_: $ty = $default;
+        default_
+    }};
+}
+use _default_val as default_val;
+
+macro_rules! _default_str {
+    (@verbatim: $s:literal, $_ty:ty) => {
+        $s.to_owned()
+    };
+    ($default:expr, $ty:ty) => {{
+        let val = default_val!($default, $ty);
+        serde_json::to_string_pretty(&val).unwrap()
+    }};
+}
+use _default_str as default_str;
+
+macro_rules! _impl_for_config_data {
+    (local, $(
+            $(#[doc=$doc:literal])*
+            $vis:vis $field:ident : $ty:ty = $default:expr,
+        )*
+    ) => {
+        impl Config {
+            $(
+                $($doc)*
+                #[allow(non_snake_case)]
+                $vis fn $field(&self, _source_root: Option<SourceRootId>) -> &$ty {
+                    if let Some(v) = self.client_config.local.$field.as_ref() {
+                        return &v;
+                    }
+
+                    if let Some(v) = self.user_config.local.$field.as_ref() {
+                        return &v;
+                    }
+
+                    &self.default_config.local.$field
+                }
+            )*
+        }
+    };
+    (global, $(
+                $(#[doc=$doc:literal])*
+                $vis:vis $field:ident : $ty:ty = $default:expr,
+            )*
+        ) => {
+        impl Config {
+            $(
+                $($doc)*
+                #[allow(non_snake_case)]
+                $vis fn $field(&self) -> &$ty {
+                    if let Some(v) = self.client_config.global.$field.as_ref() {
+                        return &v;
+                    }
+
+                    if let Some(v) = self.user_config.global.$field.as_ref() {
+                        return &v;
+                    }
+
+                    &self.default_config.global.$field
+                }
+            )*
+        }
+    };
+    (client, $(
+        $(#[doc=$doc:literal])*
+        $vis:vis $field:ident : $ty:ty = $default:expr,
+    )*
+    ) => {
+        impl Config {
+            $(
+                $($doc)*
+                #[allow(non_snake_case)]
+                $vis fn $field(&self) -> &$ty {
+                    if let Some(v) = self.client_config.global.$field.as_ref() {
+                        return &v;
+                    }
+
+                    &self.default_config.client.$field
+                }
+            )*
+        }
+    };
+}
+use _impl_for_config_data as impl_for_config_data;
+
 macro_rules! _config_data {
-    (struct $name:ident {
+    // modname is for the tests
+    ($(#[doc=$dox:literal])* $modname:ident: struct $name:ident <- $input:ident -> {
         $(
             $(#[doc=$doc:literal])*
-            $field:ident $(| $alias:ident)*: $ty:ty = $default:expr,
+            $vis:vis $field:ident $(| $alias:ident)*: $ty:ty = $(@$marker:ident: )? $default:expr,
         )*
     }) => {
+        /// Default config values for this grouping.
         #[allow(non_snake_case)]
-        #[derive(Debug, Clone)]
+        #[derive(Debug, Clone, Serialize)]
         struct $name { $($field: $ty,)* }
-        impl $name {
-            fn from_json(mut json: serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> $name {
+
+        impl_for_config_data!{
+            $modname,
+            $(
+                $vis $field : $ty = $default,
+            )*
+        }
+
+        /// All fields `Option<T>`, `None` representing fields not set in a particular JSON/TOML blob.
+        #[allow(non_snake_case)]
+        #[derive(Clone, Serialize, Default)]
+        struct $input { $(
+            #[serde(skip_serializing_if = "Option::is_none")]
+            $field: Option<$ty>,
+        )* }
+
+        impl std::fmt::Debug for $input {
+            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                let mut s = f.debug_struct(stringify!($input));
+                $(
+                    if let Some(val) = self.$field.as_ref() {
+                        s.field(stringify!($field), val);
+                    }
+                )*
+                s.finish()
+            }
+        }
+
+        impl Default for $name {
+            fn default() -> Self {
                 $name {$(
+                    $field: default_val!($(@$marker:)? $default, $ty),
+                )*}
+            }
+        }
+
+        #[allow(unused)]
+        impl $name {
+            /// Applies overrides from some more local config blob, to self.
+            fn apply_input(&mut self, input: $input) {
+                $(
+                    if let Some(value) = input.$field {
+                        self.$field = value;
+                    }
+                )*
+            }
+
+            fn clone_with_overrides(&self, input: $input) -> Self {
+                Self {$(
+                    $field: input.$field.unwrap_or_else(|| self.$field.clone()),
+                )*}
+            }
+        }
+
+        #[allow(unused, clippy::ptr_arg)]
+        impl $input {
+            fn from_json(json: &mut serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> Self {
+                Self {$(
                     $field: get_field(
-                        &mut json,
+                        json,
                         error_sink,
                         stringify!($field),
                         None$(.or(Some(stringify!($alias))))*,
-                        $default,
                     ),
                 )*}
             }
 
-            fn json_schema() -> serde_json::Value {
-                schema(&[
-                    $({
-                        let field = stringify!($field);
-                        let ty = stringify!($ty);
-
-                        (field, ty, &[$($doc),*], $default)
-                    },)*
-                ])
+            fn from_toml(toml: &mut toml::Table, error_sink: &mut Vec<(String, toml::de::Error)>) -> Self {
+                Self {$(
+                    $field: get_field_toml::<$ty>(
+                        toml,
+                        error_sink,
+                        stringify!($field),
+                        None$(.or(Some(stringify!($alias))))*,
+                    ),
+                )*}
             }
 
-            #[cfg(test)]
-            fn manual() -> String {
-                manual(&[
+            fn schema_fields(sink: &mut Vec<SchemaField>) {
+                sink.extend_from_slice(&[
                     $({
                         let field = stringify!($field);
                         let ty = stringify!($ty);
+                        let default = default_str!($(@$marker:)? $default, $ty);
 
-                        (field, ty, &[$($doc),*], $default)
+                        (field, ty, &[$($doc),*], default)
                     },)*
                 ])
             }
         }
 
-        #[test]
-        fn fields_are_sorted() {
-            [$(stringify!($field)),*].windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1]));
+        mod $modname {
+            #[test]
+            fn fields_are_sorted() {
+                let field_names: &'static [&'static str] = &[$(stringify!($field)),*];
+                field_names.windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1]));
+            }
         }
     };
 }
 use _config_data as config_data;
 
+#[derive(Default, Debug, Clone)]
+struct DefaultConfigData {
+    global: GlobalDefaultConfigData,
+    local: LocalDefaultConfigData,
+    #[allow(dead_code)]
+    client: ClientDefaultConfigData,
+}
+
+/// All of the config levels, all fields `Option<T>`, to describe fields that are actually set by
+/// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to
+/// all fields being None.
+#[derive(Debug, Clone, Default)]
+struct FullConfigInput {
+    global: GlobalConfigInput,
+    local: LocalConfigInput,
+    #[allow(dead_code)]
+    client: ClientConfigInput,
+}
+
+impl FullConfigInput {
+    fn from_json(
+        mut json: serde_json::Value,
+        error_sink: &mut Vec<(String, serde_json::Error)>,
+    ) -> FullConfigInput {
+        FullConfigInput {
+            global: GlobalConfigInput::from_json(&mut json, error_sink),
+            local: LocalConfigInput::from_json(&mut json, error_sink),
+            client: ClientConfigInput::from_json(&mut json, error_sink),
+        }
+    }
+
+    fn schema_fields() -> Vec<SchemaField> {
+        let mut fields = Vec::new();
+        GlobalConfigInput::schema_fields(&mut fields);
+        LocalConfigInput::schema_fields(&mut fields);
+        ClientConfigInput::schema_fields(&mut fields);
+        // HACK: sort the fields, so the diffs on the generated docs/schema are smaller
+        fields.sort_by_key(|&(x, ..)| x);
+        fields
+    }
+
+    fn json_schema() -> serde_json::Value {
+        schema(&Self::schema_fields())
+    }
+
+    #[cfg(test)]
+    fn manual() -> String {
+        manual(&Self::schema_fields())
+    }
+}
+
+/// All of the config levels, all fields `Option<T>`, to describe fields that are actually set by
+/// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to
+/// all fields being None.
+#[derive(Debug, Clone, Default)]
+struct GlobalLocalConfigInput {
+    global: GlobalConfigInput,
+    local: LocalConfigInput,
+}
+
+impl GlobalLocalConfigInput {
+    #[allow(dead_code)]
+    fn from_toml(
+        mut toml: toml::Table,
+        error_sink: &mut Vec<(String, toml::de::Error)>,
+    ) -> GlobalLocalConfigInput {
+        GlobalLocalConfigInput {
+            global: GlobalConfigInput::from_toml(&mut toml, error_sink),
+            local: LocalConfigInput::from_toml(&mut toml, error_sink),
+        }
+    }
+}
+
+fn get_field_toml<T: DeserializeOwned>(
+    val: &toml::Table,
+    error_sink: &mut Vec<(String, toml::de::Error)>,
+    field: &'static str,
+    alias: Option<&'static str>,
+) -> Option<T> {
+    alias
+        .into_iter()
+        .chain(iter::once(field))
+        .filter_map(move |field| {
+            let subkeys = field.split('_');
+            let mut v = val;
+            for subkey in subkeys {
+                if let Some(val) = v.get(subkey) {
+                    if let Some(map) = val.as_table() {
+                        v = map;
+                    } else {
+                        return Some(toml::Value::try_into(val.clone()).map_err(|e| (e, v)));
+                    }
+                } else {
+                    return None;
+                }
+            }
+            None
+        })
+        .find(Result::is_ok)
+        .and_then(|res| match res {
+            Ok(it) => Some(it),
+            Err((e, pointer)) => {
+                error_sink.push((pointer.to_string(), e));
+                None
+            }
+        })
+}
+
 fn get_field<T: DeserializeOwned>(
     json: &mut serde_json::Value,
     error_sink: &mut Vec<(String, serde_json::Error)>,
     field: &'static str,
     alias: Option<&'static str>,
-    default: &str,
-) -> T {
+) -> Option<T> {
     // XXX: check alias first, to work around the VS Code where it pre-fills the
     // defaults instead of sending an empty object.
     alias
@@ -2232,12 +2616,11 @@
                 None
             }
         })
-        .unwrap_or_else(|| {
-            serde_json::from_str(default).unwrap_or_else(|e| panic!("{e} on: `{default}`"))
-        })
 }
 
-fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value {
+type SchemaField = (&'static str, &'static str, &'static [&'static str], String);
+
+fn schema(fields: &[SchemaField]) -> serde_json::Value {
     let map = fields
         .iter()
         .map(|(field, ty, doc, default)| {
@@ -2288,7 +2671,7 @@
         "FxHashMap<Box<str>, Box<[Box<str>]>>" => set! {
             "type": "object",
         },
-        "FxHashMap<String, SnippetDef>" => set! {
+        "IndexMap<String, SnippetDef>" => set! {
             "type": "object",
         },
         "FxHashMap<String, String>" => set! {
@@ -2297,6 +2680,9 @@
         "FxHashMap<Box<str>, usize>" => set! {
             "type": "object",
         },
+        "FxHashMap<String, Option<String>>" => set! {
+            "type": "object",
+        },
         "Option<usize>" => set! {
             "type": ["null", "integer"],
             "minimum": 0,
@@ -2599,7 +2985,7 @@
 }
 
 #[cfg(test)]
-fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
+fn manual(fields: &[SchemaField]) -> String {
     fields.iter().fold(String::new(), |mut acc, (field, _ty, doc, default)| {
         let name = format!("rust-analyzer.{}", field.replace('_', "."));
         let doc = doc_comment_to_string(doc);
@@ -2694,7 +3080,7 @@
     #[test]
     fn generate_config_documentation() {
         let docs_path = project_root().join("docs/user/generated_config.adoc");
-        let expected = ConfigData::manual();
+        let expected = FullConfigInput::manual();
         ensure_file_contents(&docs_path, &expected);
     }
 
@@ -2766,9 +3152,9 @@
                 "rust": { "analyzerTargetDir": null }
             }))
             .unwrap();
-        assert_eq!(config.data.cargo_targetDir, None);
+        assert_eq!(config.cargo_targetDir(), &None);
         assert!(
-            matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir.is_none())
+            matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none())
         );
     }
 
@@ -2785,9 +3171,9 @@
                 "rust": { "analyzerTargetDir": true }
             }))
             .unwrap();
-        assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true)));
+        assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true)));
         assert!(
-            matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
+            matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
         );
     }
 
@@ -2805,11 +3191,11 @@
             }))
             .unwrap();
         assert_eq!(
-            config.data.cargo_targetDir,
-            Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
+            config.cargo_targetDir(),
+            &Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
         );
         assert!(
-            matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("other_folder")))
+            matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder")))
         );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
index a0a53f5..65a9a49 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
@@ -154,10 +154,12 @@
         .copied()
         .filter_map(|file_id| {
             let line_index = snapshot.file_line_index(file_id).ok()?;
+            let source_root = snapshot.analysis.source_root(file_id).ok()?;
+
             let diagnostics = snapshot
                 .analysis
                 .diagnostics(
-                    &snapshot.config.diagnostics(),
+                    &snapshot.config.diagnostics(Some(source_root)),
                     ide::AssistResolveStrategy::None,
                     file_id,
                 )
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 7c4deac..3d3f944 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -68,8 +68,13 @@
     let range = {
         let position_encoding = snap.config.position_encoding();
         lsp_types::Range::new(
-            position(&position_encoding, span, span.line_start, span.column_start),
-            position(&position_encoding, span, span.line_end, span.column_end),
+            position(
+                &position_encoding,
+                span,
+                span.line_start,
+                span.column_start.saturating_sub(1),
+            ),
+            position(&position_encoding, span, span.line_end, span.column_end.saturating_sub(1)),
         )
     };
     lsp_types::Location::new(uri, range)
@@ -78,10 +83,10 @@
 fn position(
     position_encoding: &PositionEncoding,
     span: &DiagnosticSpan,
-    line_offset: usize,
+    line_number: usize,
     column_offset_utf32: usize,
 ) -> lsp_types::Position {
-    let line_index = line_offset - span.line_start;
+    let line_index = line_number - span.line_start;
 
     let column_offset_encoded = match span.text.get(line_index) {
         // Fast path.
@@ -104,8 +109,8 @@
     };
 
     lsp_types::Position {
-        line: (line_offset as u32).saturating_sub(1),
-        character: (column_offset_encoded as u32).saturating_sub(1),
+        line: (line_number as u32).saturating_sub(1),
+        character: column_offset_encoded as u32,
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 8516ffa..e9bca19 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -72,7 +72,6 @@
 
     // status
     pub(crate) shutdown_requested: bool,
-    pub(crate) send_hint_refresh_query: bool,
     pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
 
     // proc macros
@@ -86,7 +85,10 @@
     pub(crate) last_flycheck_error: Option<String>,
 
     // Test explorer
-    pub(crate) test_run_session: Option<flycheck::CargoTestHandle>,
+    pub(crate) test_run_session: Option<Vec<flycheck::CargoTestHandle>>,
+    pub(crate) test_run_sender: Sender<flycheck::CargoTestMessage>,
+    pub(crate) test_run_receiver: Receiver<flycheck::CargoTestMessage>,
+    pub(crate) test_run_remaining_jobs: usize,
 
     // VFS
     pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
@@ -123,6 +125,7 @@
     /// to invalidate any salsa caches.
     pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
     pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>,
+    pub(crate) detached_files: FxHashSet<vfs::AbsPathBuf>,
 
     // op queues
     pub(crate) fetch_workspaces_queue:
@@ -187,10 +190,11 @@
         };
 
         let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity());
-        if let Some(capacities) = config.lru_query_capacities() {
+        if let Some(capacities) = config.lru_query_capacities_config() {
             analysis_host.update_lru_capacities(capacities);
         }
         let (flycheck_sender, flycheck_receiver) = unbounded();
+        let (test_run_sender, test_run_receiver) = unbounded();
         let mut this = GlobalState {
             sender,
             req_queue: ReqQueue::default(),
@@ -203,7 +207,6 @@
             mem_docs: MemDocs::default(),
             semantic_tokens_cache: Arc::new(Default::default()),
             shutdown_requested: false,
-            send_hint_refresh_query: false,
             last_reported_status: None,
             source_root_config: SourceRootConfig::default(),
             local_roots_parent_map: FxHashMap::default(),
@@ -219,6 +222,9 @@
             last_flycheck_error: None,
 
             test_run_session: None,
+            test_run_sender,
+            test_run_receiver,
+            test_run_remaining_jobs: 0,
 
             vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))),
             vfs_config_version: 0,
@@ -228,6 +234,7 @@
 
             workspaces: Arc::from(Vec::new()),
             crate_graph_file_dependencies: FxHashSet::default(),
+            detached_files: FxHashSet::default(),
             fetch_workspaces_queue: OpQueue::default(),
             fetch_build_data_queue: OpQueue::default(),
             fetch_proc_macros_queue: OpQueue::default(),
@@ -514,7 +521,7 @@
                 cargo.target_by_root(path).map(|it| (cargo, it))
             }
             ProjectWorkspace::Json { .. } => None,
-            ProjectWorkspace::DetachedFiles { .. } => None,
+            ProjectWorkspace::DetachedFile { .. } => None,
         })
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index b5c4a4f..4b8c3d0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -105,7 +105,7 @@
         )
         .into_bytes();
         if *data != new_contents {
-            *data = new_contents.clone();
+            data.clone_from(&new_contents);
             state.vfs.write().0.set_file_contents(path, Some(new_contents));
         }
     }
@@ -154,6 +154,10 @@
                 state
                     .fetch_workspaces_queue
                     .request_op(format!("workspace vfs file change saved {abs_path}"), false);
+            } else if state.detached_files.contains(abs_path) {
+                state
+                    .fetch_workspaces_queue
+                    .request_op(format!("detached file saved {abs_path}"), false);
             }
         }
 
@@ -296,15 +300,15 @@
                         })
                     }
                     project_model::ProjectWorkspace::Json { project, .. } => {
-                        if !project
-                            .crates()
-                            .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c))
-                        {
+                        if !project.crates().any(|(_, krate)| {
+                            crate_root_paths.contains(&krate.root_module.as_path())
+                        }) {
                             return None;
                         }
                         None
                     }
-                    project_model::ProjectWorkspace::DetachedFiles { .. } => return None,
+                    // FIXME
+                    project_model::ProjectWorkspace::DetachedFile { .. } => return None,
                 };
                 Some((idx, package))
             });
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 77692ed..cf97d7d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -101,7 +101,7 @@
             "Workspace root folders: {:?}",
             snap.workspaces
                 .iter()
-                .flat_map(|ws| ws.workspace_definition_path())
+                .map(|ws| ws.workspace_definition_path())
                 .collect::<Vec<&AbsPath>>()
         );
     }
@@ -219,14 +219,28 @@
             .unwrap_or_default(),
         None => "".to_owned(),
     };
-    let handle = if lca.is_empty() {
-        flycheck::CargoTestHandle::new(None)
+    let test_path = if lca.is_empty() {
+        None
     } else if let Some((_, path)) = lca.split_once("::") {
-        flycheck::CargoTestHandle::new(Some(path))
+        Some(path)
     } else {
-        flycheck::CargoTestHandle::new(None)
+        None
     };
-    state.test_run_session = Some(handle?);
+    let mut handles = vec![];
+    for ws in &*state.workspaces {
+        if let ProjectWorkspace::Cargo { cargo, .. } = ws {
+            let handle = flycheck::CargoTestHandle::new(
+                test_path,
+                state.config.cargo_test_options(),
+                cargo.workspace_root(),
+                state.test_run_sender.clone(),
+            )?;
+            handles.push(handle);
+        }
+    }
+    // Each process send finished signal twice, once for stdout and once for stderr
+    state.test_run_remaining_jobs = 2 * handles.len();
+    state.test_run_session = Some(handles);
     Ok(())
 }
 
@@ -355,8 +369,9 @@
 ) -> anyhow::Result<Vec<lsp_types::TextEdit>> {
     let _p = tracing::span!(tracing::Level::INFO, "handle_join_lines").entered();
 
-    let config = snap.config.join_lines();
     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let source_root = snap.analysis.source_root(file_id)?;
+    let config = snap.config.join_lines(Some(source_root));
     let line_index = snap.file_line_index(file_id)?;
 
     let mut res = TextEdit::default();
@@ -923,7 +938,8 @@
     let completion_trigger_character =
         params.context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next());
 
-    let completion_config = &snap.config.completion();
+    let source_root = snap.analysis.source_root(position.file_id)?;
+    let completion_config = &snap.config.completion(Some(source_root));
     let items = match snap.analysis.completions(
         completion_config,
         position,
@@ -964,11 +980,12 @@
     let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
     let line_index = snap.file_line_index(file_id)?;
     let offset = from_proto::offset(&line_index, resolve_data.position.position)?;
+    let source_root = snap.analysis.source_root(file_id)?;
 
     let additional_edits = snap
         .analysis
         .resolve_completion_edits(
-            &snap.config.completion(),
+            &snap.config.completion(Some(source_root)),
             FilePosition { file_id, offset },
             resolve_data
                 .imports
@@ -1038,16 +1055,17 @@
         PositionOrRange::Position(position) => Range::new(position, position),
         PositionOrRange::Range(range) => range,
     };
-
     let file_range = from_proto::file_range(&snap, &params.text_document, range)?;
-    let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
+
+    let hover = snap.config.hover();
+    let info = match snap.analysis.hover(&hover, file_range)? {
         None => return Ok(None),
         Some(info) => info,
     };
 
     let line_index = snap.file_line_index(file_range.file_id)?;
     let range = to_proto::range(&line_index, info.range);
-    let markup_kind = snap.config.hover().format;
+    let markup_kind = hover.format;
     let hover = lsp_ext::Hover {
         hover: lsp_types::Hover {
             contents: HoverContents::Markup(to_proto::markup_content(
@@ -1146,8 +1164,8 @@
                 .flat_map(|(file_id, refs)| {
                     refs.into_iter()
                         .filter(|&(_, category)| {
-                            (!exclude_imports || category != Some(ReferenceCategory::Import))
-                                && (!exclude_tests || category != Some(ReferenceCategory::Test))
+                            (!exclude_imports || !category.contains(ReferenceCategory::IMPORT))
+                                && (!exclude_tests || !category.contains(ReferenceCategory::TEST))
                         })
                         .map(move |(range, _)| FileRange { file_id, range })
                 })
@@ -1191,11 +1209,12 @@
         return Ok(None);
     }
 
-    let line_index =
-        snap.file_line_index(from_proto::file_id(&snap, &params.text_document.uri)?)?;
+    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let line_index = snap.file_line_index(file_id)?;
     let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
+    let source_root = snap.analysis.source_root(file_id)?;
 
-    let mut assists_config = snap.config.assist();
+    let mut assists_config = snap.config.assist(Some(source_root));
     assists_config.allowed = params
         .context
         .only
@@ -1212,7 +1231,7 @@
     };
     let assists = snap.analysis.assists_with_fixes(
         &assists_config,
-        &snap.config.diagnostics(),
+        &snap.config.diagnostics(Some(source_root)),
         resolve,
         frange,
     )?;
@@ -1266,8 +1285,9 @@
     let line_index = snap.file_line_index(file_id)?;
     let range = from_proto::text_range(&line_index, params.code_action_params.range)?;
     let frange = FileRange { file_id, range };
+    let source_root = snap.analysis.source_root(file_id)?;
 
-    let mut assists_config = snap.config.assist();
+    let mut assists_config = snap.config.assist(Some(source_root));
     assists_config.allowed = params
         .code_action_params
         .context
@@ -1290,7 +1310,7 @@
 
     let assists = snap.analysis.assists_with_fixes(
         &assists_config,
-        &snap.config.diagnostics(),
+        &snap.config.diagnostics(Some(source_root)),
         AssistResolveStrategy::Single(assist_resolve),
         frange,
     )?;
@@ -1419,8 +1439,12 @@
     let _p = tracing::span!(tracing::Level::INFO, "handle_document_highlight").entered();
     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
     let line_index = snap.file_line_index(position.file_id)?;
+    let source_root = snap.analysis.source_root(position.file_id)?;
 
-    let refs = match snap.analysis.highlight_related(snap.config.highlight_related(), position)? {
+    let refs = match snap
+        .analysis
+        .highlight_related(snap.config.highlight_related(Some(source_root)), position)?
+    {
         None => return Ok(None),
         Some(refs) => refs,
     };
@@ -1428,7 +1452,7 @@
         .into_iter()
         .map(|ide::HighlightedRange { range, category }| lsp_types::DocumentHighlight {
             range: to_proto::range(&line_index, range),
-            kind: category.and_then(to_proto::document_highlight_kind),
+            kind: to_proto::document_highlight_kind(category),
         })
         .collect();
     Ok(Some(res))
@@ -1466,7 +1490,9 @@
         params.range,
     )?;
     let line_index = snap.file_line_index(file_id)?;
-    let inlay_hints_config = snap.config.inlay_hints();
+    let source_root = snap.analysis.source_root(file_id)?;
+
+    let inlay_hints_config = snap.config.inlay_hints(Some(source_root));
     Ok(Some(
         snap.analysis
             .inlay_hints(&inlay_hints_config, file_id, Some(range))?
@@ -1490,29 +1516,33 @@
 ) -> anyhow::Result<InlayHint> {
     let _p = tracing::span!(tracing::Level::INFO, "handle_inlay_hints_resolve").entered();
 
-    let data = match original_hint.data.take() {
-        Some(it) => it,
-        None => return Ok(original_hint),
-    };
-
+    let Some(data) = original_hint.data.take() else { return Ok(original_hint) };
     let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?;
+    let Some(hash) = resolve_data.hash.parse().ok() else { return Ok(original_hint) };
     let file_id = FileId::from_raw(resolve_data.file_id);
     anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data");
 
     let line_index = snap.file_line_index(file_id)?;
     let hint_position = from_proto::offset(&line_index, original_hint.position)?;
-    let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
+    let source_root = snap.analysis.source_root(file_id)?;
+
+    let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(Some(source_root));
     forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
     let resolve_hints = snap.analysis.inlay_hints_resolve(
         &forced_resolve_inlay_hints_config,
         file_id,
         hint_position,
-        resolve_data.hash,
+        hash,
+        |hint| {
+            std::hash::BuildHasher::hash_one(
+                &std::hash::BuildHasherDefault::<ide_db::FxHasher>::default(),
+                hint,
+            )
+        },
     )?;
 
-    let mut resolved_hints = resolve_hints
-        .into_iter()
-        .filter_map(|it| {
+    Ok(resolve_hints
+        .and_then(|it| {
             to_proto::inlay_hint(
                 &snap,
                 &forced_resolve_inlay_hints_config.fields_to_resolve,
@@ -1523,13 +1553,8 @@
             .ok()
         })
         .filter(|hint| hint.position == original_hint.position)
-        .filter(|hint| hint.kind == original_hint.kind);
-    if let Some(resolved_hint) = resolved_hints.next() {
-        if resolved_hints.next().is_none() {
-            return Ok(resolved_hint);
-        }
-    }
-    Ok(original_hint)
+        .filter(|hint| hint.kind == original_hint.kind)
+        .unwrap_or(original_hint))
 }
 
 pub(crate) fn handle_call_hierarchy_prepare(
@@ -1633,8 +1658,9 @@
     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
     let text = snap.analysis.file_text(file_id)?;
     let line_index = snap.file_line_index(file_id)?;
+    let source_root = snap.analysis.source_root(file_id)?;
 
-    let mut highlight_config = snap.config.highlighting_config();
+    let mut highlight_config = snap.config.highlighting_config(Some(source_root));
     // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
     highlight_config.syntactic_name_ref_highlighting =
         snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -1645,7 +1671,7 @@
         &line_index,
         highlights,
         snap.config.semantics_tokens_augments_syntax_tokens(),
-        snap.config.highlighting_non_standard_tokens(),
+        snap.config.highlighting_non_standard_tokens(Some(source_root)),
     );
 
     // Unconditionally cache the tokens
@@ -1663,8 +1689,9 @@
     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
     let text = snap.analysis.file_text(file_id)?;
     let line_index = snap.file_line_index(file_id)?;
+    let source_root = snap.analysis.source_root(file_id)?;
 
-    let mut highlight_config = snap.config.highlighting_config();
+    let mut highlight_config = snap.config.highlighting_config(Some(source_root));
     // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
     highlight_config.syntactic_name_ref_highlighting =
         snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -1675,7 +1702,7 @@
         &line_index,
         highlights,
         snap.config.semantics_tokens_augments_syntax_tokens(),
-        snap.config.highlighting_non_standard_tokens(),
+        snap.config.highlighting_non_standard_tokens(Some(source_root)),
     );
 
     let cached_tokens = snap.semantic_tokens_cache.lock().remove(&params.text_document.uri);
@@ -1706,8 +1733,9 @@
     let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
     let text = snap.analysis.file_text(frange.file_id)?;
     let line_index = snap.file_line_index(frange.file_id)?;
+    let source_root = snap.analysis.source_root(frange.file_id)?;
 
-    let mut highlight_config = snap.config.highlighting_config();
+    let mut highlight_config = snap.config.highlighting_config(Some(source_root));
     // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
     highlight_config.syntactic_name_ref_highlighting =
         snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -1718,7 +1746,7 @@
         &line_index,
         highlights,
         snap.config.semantics_tokens_augments_syntax_tokens(),
-        snap.config.highlighting_non_standard_tokens(),
+        snap.config.highlighting_non_standard_tokens(Some(source_root)),
     );
     Ok(Some(semantic_tokens.into()))
 }
@@ -1733,7 +1761,9 @@
     let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match ws {
         ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some((cargo, sysroot.as_ref().ok())),
         ProjectWorkspace::Json { .. } => None,
-        ProjectWorkspace::DetachedFiles { .. } => None,
+        ProjectWorkspace::DetachedFile { cargo_script, sysroot, .. } => {
+            cargo_script.as_ref().zip(Some(sysroot.as_ref().ok()))
+        }
     });
 
     let (cargo, sysroot) = match ws_and_sysroot {
@@ -1931,8 +1961,8 @@
     snap: &GlobalStateSnapshot,
     nav_targets: &[HoverGotoTypeData],
 ) -> Option<lsp_ext::CommandLinkGroup> {
-    if !snap.config.hover_actions().goto_type_def
-        || nav_targets.is_empty()
+    if nav_targets.is_empty()
+        || !snap.config.hover_actions().goto_type_def
         || !snap.config.client_commands().goto_location
     {
         return None;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index 2731e84..7b385ca 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -40,7 +40,7 @@
     };
     let load_cargo_config = LoadCargoConfig {
         load_out_dirs_from_check: true,
-        with_proc_macro_server: ProcMacroServerChoice::None,
+        with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: false,
     };
 
@@ -100,7 +100,7 @@
     };
     let load_cargo_config = LoadCargoConfig {
         load_out_dirs_from_check: true,
-        with_proc_macro_server: ProcMacroServerChoice::None,
+        with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: true,
     };
 
@@ -262,7 +262,7 @@
     };
     let load_cargo_config = LoadCargoConfig {
         load_out_dirs_from_check: true,
-        with_proc_macro_server: ProcMacroServerChoice::None,
+        with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: true,
     };
 
@@ -300,7 +300,7 @@
         .diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
         .unwrap();
 
-    let _g = crate::tracing::hprof::init("*>1");
+    let _g = crate::tracing::hprof::init("*");
 
     {
         let _it = stdx::timeit("change");
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
index eac982f..12f8e71 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
@@ -463,13 +463,6 @@
     pub runnable: Runnable,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-pub struct InlayHintsParams {
-    pub text_document: TextDocumentIdentifier,
-    pub range: Option<lsp_types::Range>,
-}
-
 pub enum Ssr {}
 
 impl Request for Ssr {
@@ -801,7 +794,8 @@
 #[derive(Debug, Serialize, Deserialize)]
 pub struct InlayHintResolveData {
     pub file_id: u32,
-    pub hash: u64,
+    // This is a string instead of a u64 as javascript can't represent u64 fully
+    pub hash: String,
 }
 
 #[derive(Debug, Serialize, Deserialize)]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
index 3e00222..991c107 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
@@ -17,15 +17,19 @@
         }
 
     ) => {
-        $(pub(crate) const $standard: SemanticTokenType = SemanticTokenType::$standard;)*
-        $(pub(crate) const $custom: SemanticTokenType = SemanticTokenType::new($string);)*
+        pub(crate) mod types {
+            use super::SemanticTokenType;
+            $(pub(crate) const $standard: SemanticTokenType = SemanticTokenType::$standard;)*
+            $(pub(crate) const $custom: SemanticTokenType = SemanticTokenType::new($string);)*
+        }
 
         pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
             $(SemanticTokenType::$standard,)*
-            $($custom),*
+            $(self::types::$custom),*
         ];
 
         pub(crate) fn standard_fallback_type(token: SemanticTokenType) -> Option<SemanticTokenType> {
+            use self::types::*;
             $(
                 if token == $custom {
                     None $(.or(Some(SemanticTokenType::$fallback)))?
@@ -61,39 +65,41 @@
     custom {
         (ANGLE, "angle"),
         (ARITHMETIC, "arithmetic") => OPERATOR,
-        (ATTRIBUTE, "attribute") => DECORATOR,
         (ATTRIBUTE_BRACKET, "attributeBracket") => DECORATOR,
+        (ATTRIBUTE, "attribute") => DECORATOR,
         (BITWISE, "bitwise") => OPERATOR,
         (BOOLEAN, "boolean"),
         (BRACE, "brace"),
         (BRACKET, "bracket"),
         (BUILTIN_ATTRIBUTE, "builtinAttribute") => DECORATOR,
-        (BUILTIN_TYPE, "builtinType"),
+        (BUILTIN_TYPE, "builtinType") => TYPE,
         (CHAR, "character") => STRING,
         (COLON, "colon"),
         (COMMA, "comma"),
         (COMPARISON, "comparison") => OPERATOR,
         (CONST_PARAMETER, "constParameter"),
-        (DERIVE, "derive") => DECORATOR,
+        (CONST, "const") => VARIABLE,
         (DERIVE_HELPER, "deriveHelper") => DECORATOR,
+        (DERIVE, "derive") => DECORATOR,
         (DOT, "dot"),
         (ESCAPE_SEQUENCE, "escapeSequence") => STRING,
-        (INVALID_ESCAPE_SEQUENCE, "invalidEscapeSequence") => STRING,
         (FORMAT_SPECIFIER, "formatSpecifier") => STRING,
         (GENERIC, "generic") => TYPE_PARAMETER,
+        (INVALID_ESCAPE_SEQUENCE, "invalidEscapeSequence") => STRING,
         (LABEL, "label"),
         (LIFETIME, "lifetime"),
         (LOGICAL, "logical") => OPERATOR,
         (MACRO_BANG, "macroBang") => MACRO,
-        (PROC_MACRO, "procMacro") => MACRO,
         (PARENTHESIS, "parenthesis"),
+        (PROC_MACRO, "procMacro") => MACRO,
         (PUNCTUATION, "punctuation"),
         (SELF_KEYWORD, "selfKeyword") => KEYWORD,
         (SELF_TYPE_KEYWORD, "selfTypeKeyword") => KEYWORD,
         (SEMICOLON, "semicolon"),
-        (TYPE_ALIAS, "typeAlias"),
+        (STATIC, "static") => VARIABLE,
         (TOOL_MODULE, "toolModule") => DECORATOR,
-        (UNION, "union"),
+        (TYPE_ALIAS, "typeAlias") => TYPE,
+        (UNION, "union") => TYPE,
         (UNRESOLVED_REFERENCE, "unresolvedReference"),
     }
 ];
@@ -112,13 +118,16 @@
         }
 
     ) => {
+        pub(crate) mod modifiers {
+            use super::SemanticTokenModifier;
 
-        $(pub(crate) const $standard: SemanticTokenModifier = SemanticTokenModifier::$standard;)*
-        $(pub(crate) const $custom: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
+            $(pub(crate) const $standard: SemanticTokenModifier = SemanticTokenModifier::$standard;)*
+            $(pub(crate) const $custom: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
+        }
 
         pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
             $(SemanticTokenModifier::$standard,)*
-            $($custom),*
+            $(self::modifiers::$custom),*
         ];
 
         const LAST_STANDARD_MOD: usize = count_tts!($($standard)*);
@@ -145,8 +154,8 @@
         (INTRA_DOC_LINK, "intraDocLink"),
         (LIBRARY, "library"),
         (MACRO_MODIFIER, "macro"),
-        (PROC_MACRO_MODIFIER, "proc_macro"),
         (MUTABLE, "mutable"),
+        (PROC_MACRO_MODIFIER, "procMacro"),
         (PUBLIC, "public"),
         (REFERENCE, "reference"),
         (TRAIT_MODIFIER, "trait"),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index d8bb125..d02f461 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -92,12 +92,13 @@
 pub(crate) fn document_highlight_kind(
     category: ReferenceCategory,
 ) -> Option<lsp_types::DocumentHighlightKind> {
-    match category {
-        ReferenceCategory::Read => Some(lsp_types::DocumentHighlightKind::READ),
-        ReferenceCategory::Write => Some(lsp_types::DocumentHighlightKind::WRITE),
-        ReferenceCategory::Import => None,
-        ReferenceCategory::Test => None,
+    if category.contains(ReferenceCategory::WRITE) {
+        return Some(lsp_types::DocumentHighlightKind::WRITE);
     }
+    if category.contains(ReferenceCategory::READ) {
+        return Some(lsp_types::DocumentHighlightKind::READ);
+    }
+    None
 }
 
 pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity {
@@ -233,7 +234,7 @@
         completion_item(&mut res, config, line_index, &tdpp, max_relevance, item);
     }
 
-    if let Some(limit) = config.completion().limit {
+    if let Some(limit) = config.completion(None).limit {
         res.sort_by(|item1, item2| item1.sort_text.cmp(&item2.sort_text));
         res.truncate(limit);
     }
@@ -317,7 +318,7 @@
 
     set_score(&mut lsp_item, max_relevance, item.relevance);
 
-    if config.completion().enable_imports_on_the_fly && !item.import_to_add.is_empty() {
+    if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() {
         let imports = item
             .import_to_add
             .into_iter()
@@ -479,7 +480,11 @@
 
     let data = match resolve_hash {
         Some(hash) if something_to_resolve => Some(
-            to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash }).unwrap(),
+            to_value(lsp_ext::InlayHintResolveData {
+                file_id: file_id.index(),
+                hash: hash.to_string(),
+            })
+            .unwrap(),
         ),
         _ => None,
     };
@@ -650,97 +655,99 @@
 fn semantic_token_type_and_modifiers(
     highlight: Highlight,
 ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {
+    use semantic_tokens::{modifiers as mods, types};
+
     let ty = match highlight.tag {
         HlTag::Symbol(symbol) => match symbol {
-            SymbolKind::Attribute => semantic_tokens::DECORATOR,
-            SymbolKind::Derive => semantic_tokens::DERIVE,
-            SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER,
-            SymbolKind::Module => semantic_tokens::NAMESPACE,
-            SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
-            SymbolKind::Field => semantic_tokens::PROPERTY,
-            SymbolKind::TypeParam => semantic_tokens::TYPE_PARAMETER,
-            SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
-            SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
-            SymbolKind::Label => semantic_tokens::LABEL,
-            SymbolKind::ValueParam => semantic_tokens::PARAMETER,
-            SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
-            SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD,
-            SymbolKind::Local => semantic_tokens::VARIABLE,
-            SymbolKind::Method => semantic_tokens::METHOD,
-            SymbolKind::Function => semantic_tokens::FUNCTION,
-            SymbolKind::Const => semantic_tokens::VARIABLE,
-            SymbolKind::Static => semantic_tokens::VARIABLE,
-            SymbolKind::Struct => semantic_tokens::STRUCT,
-            SymbolKind::Enum => semantic_tokens::ENUM,
-            SymbolKind::Variant => semantic_tokens::ENUM_MEMBER,
-            SymbolKind::Union => semantic_tokens::UNION,
-            SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
-            SymbolKind::Trait => semantic_tokens::INTERFACE,
-            SymbolKind::TraitAlias => semantic_tokens::INTERFACE,
-            SymbolKind::Macro => semantic_tokens::MACRO,
-            SymbolKind::ProcMacro => semantic_tokens::PROC_MACRO,
-            SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
-            SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
+            SymbolKind::Attribute => types::DECORATOR,
+            SymbolKind::Derive => types::DERIVE,
+            SymbolKind::DeriveHelper => types::DERIVE_HELPER,
+            SymbolKind::Module => types::NAMESPACE,
+            SymbolKind::Impl => types::TYPE_ALIAS,
+            SymbolKind::Field => types::PROPERTY,
+            SymbolKind::TypeParam => types::TYPE_PARAMETER,
+            SymbolKind::ConstParam => types::CONST_PARAMETER,
+            SymbolKind::LifetimeParam => types::LIFETIME,
+            SymbolKind::Label => types::LABEL,
+            SymbolKind::ValueParam => types::PARAMETER,
+            SymbolKind::SelfParam => types::SELF_KEYWORD,
+            SymbolKind::SelfType => types::SELF_TYPE_KEYWORD,
+            SymbolKind::Local => types::VARIABLE,
+            SymbolKind::Method => types::METHOD,
+            SymbolKind::Function => types::FUNCTION,
+            SymbolKind::Const => types::CONST,
+            SymbolKind::Static => types::STATIC,
+            SymbolKind::Struct => types::STRUCT,
+            SymbolKind::Enum => types::ENUM,
+            SymbolKind::Variant => types::ENUM_MEMBER,
+            SymbolKind::Union => types::UNION,
+            SymbolKind::TypeAlias => types::TYPE_ALIAS,
+            SymbolKind::Trait => types::INTERFACE,
+            SymbolKind::TraitAlias => types::INTERFACE,
+            SymbolKind::Macro => types::MACRO,
+            SymbolKind::ProcMacro => types::PROC_MACRO,
+            SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE,
+            SymbolKind::ToolModule => types::TOOL_MODULE,
         },
-        HlTag::AttributeBracket => semantic_tokens::ATTRIBUTE_BRACKET,
-        HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
-        HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
-        HlTag::ByteLiteral | HlTag::NumericLiteral => semantic_tokens::NUMBER,
-        HlTag::CharLiteral => semantic_tokens::CHAR,
-        HlTag::Comment => semantic_tokens::COMMENT,
-        HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
-        HlTag::InvalidEscapeSequence => semantic_tokens::INVALID_ESCAPE_SEQUENCE,
-        HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
-        HlTag::Keyword => semantic_tokens::KEYWORD,
-        HlTag::None => semantic_tokens::GENERIC,
+        HlTag::AttributeBracket => types::ATTRIBUTE_BRACKET,
+        HlTag::BoolLiteral => types::BOOLEAN,
+        HlTag::BuiltinType => types::BUILTIN_TYPE,
+        HlTag::ByteLiteral | HlTag::NumericLiteral => types::NUMBER,
+        HlTag::CharLiteral => types::CHAR,
+        HlTag::Comment => types::COMMENT,
+        HlTag::EscapeSequence => types::ESCAPE_SEQUENCE,
+        HlTag::InvalidEscapeSequence => types::INVALID_ESCAPE_SEQUENCE,
+        HlTag::FormatSpecifier => types::FORMAT_SPECIFIER,
+        HlTag::Keyword => types::KEYWORD,
+        HlTag::None => types::GENERIC,
         HlTag::Operator(op) => match op {
-            HlOperator::Bitwise => semantic_tokens::BITWISE,
-            HlOperator::Arithmetic => semantic_tokens::ARITHMETIC,
-            HlOperator::Logical => semantic_tokens::LOGICAL,
-            HlOperator::Comparison => semantic_tokens::COMPARISON,
-            HlOperator::Other => semantic_tokens::OPERATOR,
+            HlOperator::Bitwise => types::BITWISE,
+            HlOperator::Arithmetic => types::ARITHMETIC,
+            HlOperator::Logical => types::LOGICAL,
+            HlOperator::Comparison => types::COMPARISON,
+            HlOperator::Other => types::OPERATOR,
         },
-        HlTag::StringLiteral => semantic_tokens::STRING,
-        HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
+        HlTag::StringLiteral => types::STRING,
+        HlTag::UnresolvedReference => types::UNRESOLVED_REFERENCE,
         HlTag::Punctuation(punct) => match punct {
-            HlPunct::Bracket => semantic_tokens::BRACKET,
-            HlPunct::Brace => semantic_tokens::BRACE,
-            HlPunct::Parenthesis => semantic_tokens::PARENTHESIS,
-            HlPunct::Angle => semantic_tokens::ANGLE,
-            HlPunct::Comma => semantic_tokens::COMMA,
-            HlPunct::Dot => semantic_tokens::DOT,
-            HlPunct::Colon => semantic_tokens::COLON,
-            HlPunct::Semi => semantic_tokens::SEMICOLON,
-            HlPunct::Other => semantic_tokens::PUNCTUATION,
-            HlPunct::MacroBang => semantic_tokens::MACRO_BANG,
+            HlPunct::Bracket => types::BRACKET,
+            HlPunct::Brace => types::BRACE,
+            HlPunct::Parenthesis => types::PARENTHESIS,
+            HlPunct::Angle => types::ANGLE,
+            HlPunct::Comma => types::COMMA,
+            HlPunct::Dot => types::DOT,
+            HlPunct::Colon => types::COLON,
+            HlPunct::Semi => types::SEMICOLON,
+            HlPunct::Other => types::PUNCTUATION,
+            HlPunct::MacroBang => types::MACRO_BANG,
         },
     };
 
     let mut mods = semantic_tokens::ModifierSet::default();
     for modifier in highlight.mods.iter() {
         let modifier = match modifier {
-            HlMod::Associated => semantic_tokens::ASSOCIATED,
-            HlMod::Async => semantic_tokens::ASYNC,
-            HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
-            HlMod::Callable => semantic_tokens::CALLABLE,
-            HlMod::Const => semantic_tokens::CONSTANT,
-            HlMod::Consuming => semantic_tokens::CONSUMING,
-            HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
-            HlMod::CrateRoot => semantic_tokens::CRATE_ROOT,
-            HlMod::DefaultLibrary => semantic_tokens::DEFAULT_LIBRARY,
-            HlMod::Definition => semantic_tokens::DECLARATION,
-            HlMod::Documentation => semantic_tokens::DOCUMENTATION,
-            HlMod::Injected => semantic_tokens::INJECTED,
-            HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
-            HlMod::Library => semantic_tokens::LIBRARY,
-            HlMod::Macro => semantic_tokens::MACRO_MODIFIER,
-            HlMod::ProcMacro => semantic_tokens::PROC_MACRO_MODIFIER,
-            HlMod::Mutable => semantic_tokens::MUTABLE,
-            HlMod::Public => semantic_tokens::PUBLIC,
-            HlMod::Reference => semantic_tokens::REFERENCE,
-            HlMod::Static => semantic_tokens::STATIC,
-            HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
-            HlMod::Unsafe => semantic_tokens::UNSAFE,
+            HlMod::Associated => mods::ASSOCIATED,
+            HlMod::Async => mods::ASYNC,
+            HlMod::Attribute => mods::ATTRIBUTE_MODIFIER,
+            HlMod::Callable => mods::CALLABLE,
+            HlMod::Const => mods::CONSTANT,
+            HlMod::Consuming => mods::CONSUMING,
+            HlMod::ControlFlow => mods::CONTROL_FLOW,
+            HlMod::CrateRoot => mods::CRATE_ROOT,
+            HlMod::DefaultLibrary => mods::DEFAULT_LIBRARY,
+            HlMod::Definition => mods::DECLARATION,
+            HlMod::Documentation => mods::DOCUMENTATION,
+            HlMod::Injected => mods::INJECTED,
+            HlMod::IntraDocLink => mods::INTRA_DOC_LINK,
+            HlMod::Library => mods::LIBRARY,
+            HlMod::Macro => mods::MACRO_MODIFIER,
+            HlMod::ProcMacro => mods::PROC_MACRO_MODIFIER,
+            HlMod::Mutable => mods::MUTABLE,
+            HlMod::Public => mods::PUBLIC,
+            HlMod::Reference => mods::REFERENCE,
+            HlMod::Static => mods::STATIC,
+            HlMod::Trait => mods::TRAIT_MODIFIER,
+            HlMod::Unsafe => mods::UNSAFE,
         };
         mods |= modifier;
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 38df323..f37b25f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -7,7 +7,7 @@
 };
 
 use always_assert::always;
-use crossbeam_channel::{never, select, Receiver};
+use crossbeam_channel::{select, Receiver};
 use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
 use lsp_server::{Connection, Notification, Request};
 use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
@@ -220,7 +220,7 @@
             recv(self.flycheck_receiver) -> task =>
                 Some(Event::Flycheck(task.unwrap())),
 
-            recv(self.test_run_session.as_ref().map(|s| s.receiver()).unwrap_or(&never())) -> task =>
+            recv(self.test_run_receiver) -> task =>
                 Some(Event::TestResult(task.unwrap())),
 
         }
@@ -337,9 +337,7 @@
                         .entered();
                 self.handle_cargo_test_msg(message);
                 // Coalesce many test result event into a single loop turn
-                while let Some(message) =
-                    self.test_run_session.as_ref().and_then(|r| r.receiver().try_recv().ok())
-                {
+                while let Ok(message) = self.test_run_receiver.try_recv() {
                     self.handle_cargo_test_msg(message);
                 }
             }
@@ -350,11 +348,7 @@
         let memdocs_added_or_removed = self.mem_docs.take_changes();
 
         if self.is_quiescent() {
-            let became_quiescent = !(was_quiescent
-                || self.fetch_workspaces_queue.op_requested()
-                || self.fetch_build_data_queue.op_requested()
-                || self.fetch_proc_macros_queue.op_requested());
-
+            let became_quiescent = !was_quiescent;
             if became_quiescent {
                 if self.config.check_on_save() {
                     // Project has loaded properly, kick off initial flycheck
@@ -365,7 +359,7 @@
                 }
             }
 
-            let client_refresh = !was_quiescent || state_changed;
+            let client_refresh = became_quiescent || state_changed;
             if client_refresh {
                 // Refresh semantic tokens if the client supports it.
                 if self.config.semantic_tokens_refresh() {
@@ -379,17 +373,17 @@
                 }
 
                 // Refresh inlay hints if the client supports it.
-                if self.send_hint_refresh_query && self.config.inlay_hints_refresh() {
+                if self.config.inlay_hints_refresh() {
                     self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
-                    self.send_hint_refresh_query = false;
                 }
             }
 
-            let things_changed = !was_quiescent || state_changed || memdocs_added_or_removed;
-            if things_changed && self.config.publish_diagnostics() {
+            let project_or_mem_docs_changed =
+                became_quiescent || state_changed || memdocs_added_or_removed;
+            if project_or_mem_docs_changed && self.config.publish_diagnostics() {
                 self.update_diagnostics();
             }
-            if things_changed && self.config.test_explorer() {
+            if project_or_mem_docs_changed && self.config.test_explorer() {
                 self.update_tests();
             }
         }
@@ -411,7 +405,7 @@
                 // See https://github.com/rust-lang/rust-analyzer/issues/13130
                 let patch_empty = |message: &mut String| {
                     if message.is_empty() {
-                        *message = " ".to_owned();
+                        " ".clone_into(message);
                     }
                 };
 
@@ -434,7 +428,7 @@
             }
         }
 
-        if self.config.cargo_autoreload() {
+        if self.config.cargo_autoreload_config() {
             if let Some((cause, force_crate_graph_reload)) =
                 self.fetch_workspaces_queue.should_start_op()
             {
@@ -643,7 +637,6 @@
                         }
 
                         self.switch_workspaces("fetched build data".to_owned());
-                        self.send_hint_refresh_query = true;
 
                         (Some(Progress::End), None)
                     }
@@ -660,7 +653,6 @@
                     ProcMacroProgress::End(proc_macro_load_result) => {
                         self.fetch_proc_macros_queue.op_completed(true);
                         self.set_proc_macros(proc_macro_load_result);
-                        self.send_hint_refresh_query = true;
                         (Some(Progress::End), None)
                     }
                 };
@@ -792,8 +784,11 @@
             }
             flycheck::CargoTestMessage::Suite => (),
             flycheck::CargoTestMessage::Finished => {
-                self.send_notification::<lsp_ext::EndRunTest>(());
-                self.test_run_session = None;
+                self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1);
+                if self.test_run_remaining_jobs == 0 {
+                    self.send_notification::<lsp_ext::EndRunTest>(());
+                    self.test_run_session = None;
+                }
             }
             flycheck::CargoTestMessage::Custom { text } => {
                 self.send_notification::<lsp_ext::AppendOutputToRunTest>(text);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 771a5599..5d8a66c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -76,9 +76,9 @@
         if self.config.lru_parse_query_capacity() != old_config.lru_parse_query_capacity() {
             self.analysis_host.update_lru_capacity(self.config.lru_parse_query_capacity());
         }
-        if self.config.lru_query_capacities() != old_config.lru_query_capacities() {
+        if self.config.lru_query_capacities_config() != old_config.lru_query_capacities_config() {
             self.analysis_host.update_lru_capacities(
-                &self.config.lru_query_capacities().cloned().unwrap_or_default(),
+                &self.config.lru_query_capacities_config().cloned().unwrap_or_default(),
             );
         }
         if self.config.linked_or_discovered_projects() != old_config.linked_or_discovered_projects()
@@ -153,7 +153,7 @@
         for ws in self.workspaces.iter() {
             let (ProjectWorkspace::Cargo { sysroot, .. }
             | ProjectWorkspace::Json { sysroot, .. }
-            | ProjectWorkspace::DetachedFiles { sysroot, .. }) = ws;
+            | ProjectWorkspace::DetachedFile { sysroot, .. }) = ws;
             match sysroot {
                 Err(None) => (),
                 Err(Some(e)) => {
@@ -234,6 +234,7 @@
                                 it.clone(),
                                 cargo_config.target.as_deref(),
                                 &cargo_config.extra_env,
+                                &cargo_config.cfg_overrides,
                             ))
                         }
                     })
@@ -254,7 +255,7 @@
                 }
 
                 if !detached_files.is_empty() {
-                    workspaces.push(project_model::ProjectWorkspace::load_detached_files(
+                    workspaces.extend(project_model::ProjectWorkspace::load_detached_files(
                         detached_files,
                         &cargo_config,
                     ));
@@ -539,9 +540,6 @@
     }
 
     fn recreate_crate_graph(&mut self, cause: String) {
-        // crate graph construction relies on these paths, record them so when one of them gets
-        // deleted or created we trigger a reconstruction of the crate graph
-        let mut crate_graph_file_dependencies = mem::take(&mut self.crate_graph_file_dependencies);
         self.report_progress(
             "Building CrateGraph",
             crate::lsp::utils::Progress::Begin,
@@ -550,13 +548,25 @@
             None,
         );
 
+        // crate graph construction relies on these paths, record them so when one of them gets
+        // deleted or created we trigger a reconstruction of the crate graph
+        self.crate_graph_file_dependencies.clear();
+        self.detached_files = self
+            .workspaces
+            .iter()
+            .filter_map(|ws| match ws {
+                ProjectWorkspace::DetachedFile { file, .. } => Some(file.clone()),
+                _ => None,
+            })
+            .collect();
+
         let (crate_graph, proc_macro_paths, layouts, toolchains) = {
             // Create crate graph from all the workspaces
             let vfs = &mut self.vfs.write().0;
 
             let load = |path: &AbsPath| {
                 let vfs_path = vfs::VfsPath::from(path.to_path_buf());
-                crate_graph_file_dependencies.insert(vfs_path.clone());
+                self.crate_graph_file_dependencies.insert(vfs_path.clone());
                 vfs.file_id(&vfs_path)
             };
 
@@ -576,7 +586,6 @@
         change.set_target_data_layouts(layouts);
         change.set_toolchains(toolchains);
         self.analysis_host.apply_change(change);
-        self.crate_graph_file_dependencies = crate_graph_file_dependencies;
         self.report_progress(
             "Building CrateGraph",
             crate::lsp::utils::Progress::End,
@@ -673,7 +682,8 @@
                                 _ => None,
                             }
                         }
-                        ProjectWorkspace::DetachedFiles { .. } => None,
+                        // FIXME
+                        ProjectWorkspace::DetachedFile { .. } => None,
                     })
                     .map(|(id, root, sysroot_root)| {
                         let sender = sender.clone();
@@ -712,15 +722,9 @@
         let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, extra_env);
         let num_layouts = layouts.len();
         let num_toolchains = toolchains.len();
-        let (toolchain, layout) = match ws {
-            ProjectWorkspace::Cargo { toolchain, target_layout, .. }
-            | ProjectWorkspace::Json { toolchain, target_layout, .. } => {
-                (toolchain.clone(), target_layout.clone())
-            }
-            ProjectWorkspace::DetachedFiles { .. } => {
-                (None, Err("detached files have no layout".into()))
-            }
-        };
+        let (ProjectWorkspace::Cargo { toolchain, target_layout, .. }
+        | ProjectWorkspace::Json { toolchain, target_layout, .. }
+        | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws;
 
         let mapping = crate_graph.extend(
             other,
@@ -729,7 +733,7 @@
                 // if the newly created crate graph's layout is equal to the crate of the merged graph, then
                 // we can merge the crates.
                 let id = cg_id.into_raw().into_u32() as usize;
-                layouts[id] == layout && toolchains[id] == toolchain && cg_data == o_data
+                layouts[id] == *target_layout && toolchains[id] == *toolchain && cg_data == o_data
             },
         );
         // Populate the side tables for the newly merged crates
@@ -741,13 +745,13 @@
                 if layouts.len() <= idx {
                     layouts.resize(idx + 1, e.clone());
                 }
-                layouts[idx] = layout.clone();
+                layouts[idx].clone_from(target_layout);
             }
             if idx >= num_toolchains {
                 if toolchains.len() <= idx {
                     toolchains.resize(idx + 1, None);
                 }
-                toolchains[idx] = toolchain.clone();
+                toolchains[idx].clone_from(toolchain);
             }
         });
         proc_macro_paths.push(crate_proc_macros);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 439b006..b87f029 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -23,12 +23,12 @@
     notification::DidOpenTextDocument,
     request::{
         CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest,
-        WillRenameFiles, WorkspaceSymbolRequest,
+        InlayHintRequest, InlayHintResolveRequest, WillRenameFiles, WorkspaceSymbolRequest,
     },
     CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
     DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams,
-    PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
-    TextDocumentPositionParams, WorkDoneProgressParams,
+    InlayHint, InlayHintLabel, InlayHintParams, PartialResultParams, Position, Range,
+    RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams,
 };
 use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams, UnindexedProject};
 use serde_json::json;
@@ -76,6 +76,147 @@
 }
 
 #[test]
+fn resolves_inlay_hints() {
+    if skip_slow_tests() {
+        return;
+    }
+
+    let server = Project::with_fixture(
+        r#"
+//- /Cargo.toml
+[package]
+name = "foo"
+version = "0.0.0"
+
+//- /src/lib.rs
+struct Foo;
+fn f() {
+    let x = Foo;
+}
+"#,
+    )
+    .server()
+    .wait_until_workspace_is_loaded();
+
+    let res = server.send_request::<InlayHintRequest>(InlayHintParams {
+        range: Range::new(Position::new(0, 0), Position::new(3, 1)),
+        text_document: server.doc_id("src/lib.rs"),
+        work_done_progress_params: WorkDoneProgressParams::default(),
+    });
+    let mut hints = serde_json::from_value::<Option<Vec<InlayHint>>>(res).unwrap().unwrap();
+    let hint = hints.pop().unwrap();
+    assert!(hint.data.is_some());
+    assert!(
+        matches!(&hint.label, InlayHintLabel::LabelParts(parts) if parts[1].location.is_none())
+    );
+    let res = server.send_request::<InlayHintResolveRequest>(hint);
+    let hint = serde_json::from_value::<InlayHint>(res).unwrap();
+    assert!(hint.data.is_none());
+    assert!(
+        matches!(&hint.label, InlayHintLabel::LabelParts(parts) if parts[1].location.is_some())
+    );
+}
+
+#[test]
+fn completes_items_from_standard_library_in_cargo_script() {
+    // this test requires nightly so CI can't run it
+    if skip_slow_tests() || std::env::var("CI").is_ok() {
+        return;
+    }
+
+    let server = Project::with_fixture(
+        r#"
+//- /dependency/Cargo.toml
+[package]
+name = "dependency"
+version = "0.1.0"
+//- /dependency/src/lib.rs
+pub struct SpecialHashMap;
+//- /dependency2/Cargo.toml
+[package]
+name = "dependency2"
+version = "0.1.0"
+//- /dependency2/src/lib.rs
+pub struct SpecialHashMap2;
+//- /src/lib.rs
+#!/usr/bin/env -S cargo +nightly -Zscript
+---
+[dependencies]
+dependency = { path = "../dependency" }
+---
+use dependency::Spam;
+use dependency2::Spam;
+"#,
+    )
+    .with_config(serde_json::json!({
+        "cargo": { "sysroot": null },
+    }))
+    .server()
+    .wait_until_workspace_is_loaded();
+
+    let res = server.send_request::<Completion>(CompletionParams {
+        text_document_position: TextDocumentPositionParams::new(
+            server.doc_id("src/lib.rs"),
+            Position::new(5, 18),
+        ),
+        context: None,
+        partial_result_params: PartialResultParams::default(),
+        work_done_progress_params: WorkDoneProgressParams::default(),
+    });
+    assert!(res.to_string().contains("SpecialHashMap"), "{}", res.to_string());
+
+    let res = server.send_request::<Completion>(CompletionParams {
+        text_document_position: TextDocumentPositionParams::new(
+            server.doc_id("src/lib.rs"),
+            Position::new(6, 18),
+        ),
+        context: None,
+        partial_result_params: PartialResultParams::default(),
+        work_done_progress_params: WorkDoneProgressParams::default(),
+    });
+    assert!(!res.to_string().contains("SpecialHashMap"));
+
+    server.write_file_and_save(
+        "src/lib.rs",
+        r#"#!/usr/bin/env -S cargo +nightly -Zscript
+---
+[dependencies]
+dependency2 = { path = "../dependency2" }
+---
+use dependency::Spam;
+use dependency2::Spam;
+"#
+        .to_owned(),
+    );
+
+    let server = server.wait_until_workspace_is_loaded();
+
+    std::thread::sleep(std::time::Duration::from_secs(3));
+
+    let res = server.send_request::<Completion>(CompletionParams {
+        text_document_position: TextDocumentPositionParams::new(
+            server.doc_id("src/lib.rs"),
+            Position::new(5, 18),
+        ),
+        context: None,
+        partial_result_params: PartialResultParams::default(),
+        work_done_progress_params: WorkDoneProgressParams::default(),
+    });
+    assert!(!res.to_string().contains("SpecialHashMap"));
+
+    let res = server.send_request::<Completion>(CompletionParams {
+        text_document_position: TextDocumentPositionParams::new(
+            server.doc_id("src/lib.rs"),
+            Position::new(6, 18),
+        ),
+        context: None,
+        partial_result_params: PartialResultParams::default(),
+        work_done_progress_params: WorkDoneProgressParams::default(),
+    });
+    assert!(res.to_string().contains("SpecialHashMap"));
+}
+
+#[test]
 fn test_runnables_project() {
     if skip_slow_tests() {
         return;
@@ -115,7 +256,7 @@
           {
             "args": {
               "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
-              "executableArgs": ["test_eggs", "--exact", "--nocapture"],
+              "executableArgs": ["test_eggs", "--exact", "--show-output"],
               "cargoExtraArgs": [],
               "overrideCargo": null,
               "workspaceRoot": server.path().join("foo")
@@ -148,7 +289,7 @@
               "cargoExtraArgs": [],
               "executableArgs": [
                 "",
-                "--nocapture"
+                "--show-output"
               ]
             },
             "kind": "cargo",
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index 8bbe6ff..f04962a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -125,7 +125,7 @@
         }
 
         let mut config = Config::new(
-            tmp_dir_path,
+            tmp_dir_path.clone(),
             lsp_types::ClientCapabilities {
                 workspace: Some(lsp_types::WorkspaceClientCapabilities {
                     did_change_watched_files: Some(
@@ -159,6 +159,18 @@
                         content_format: Some(vec![lsp_types::MarkupKind::Markdown]),
                         ..Default::default()
                     }),
+                    inlay_hint: Some(lsp_types::InlayHintClientCapabilities {
+                        resolve_support: Some(lsp_types::InlayHintResolveClientCapabilities {
+                            properties: vec![
+                                "textEdits".to_owned(),
+                                "tooltip".to_owned(),
+                                "label.tooltip".to_owned(),
+                                "label.location".to_owned(),
+                                "label.command".to_owned(),
+                            ],
+                        }),
+                        ..Default::default()
+                    }),
                     ..Default::default()
                 }),
                 window: Some(lsp_types::WindowClientCapabilities {
@@ -173,10 +185,14 @@
             roots,
             None,
         );
-        config.update(self.config).expect("invalid config");
+        // TODO: don't hardcode src/lib.rs as detached file
+        let mut c = self.config;
+        let p = tmp_dir_path.join("src/lib.rs").to_string();
+        c["detachedFiles"] = serde_json::json!([p]);
+        config.update(c).expect("invalid config");
         config.rediscover_workspaces();
 
-        Server::new(tmp_dir, config)
+        Server::new(tmp_dir.keep(), config)
     }
 }
 
@@ -271,6 +287,7 @@
         }
     }
 
+    #[track_caller]
     pub(crate) fn send_request<R>(&self, params: R::Params) -> Value
     where
         R: lsp_types::request::Request,
@@ -282,6 +299,7 @@
         let r = Request::new(id.into(), R::METHOD.to_owned(), params);
         self.send_request_(r)
     }
+    #[track_caller]
     fn send_request_(&self, r: Request) -> Value {
         let id = r.id.clone();
         self.client.sender.send(r.clone().into()).unwrap();
@@ -362,6 +380,16 @@
     pub(crate) fn path(&self) -> &Utf8Path {
         self.dir.path()
     }
+
+    pub(crate) fn write_file_and_save(&self, path: &str, text: String) {
+        fs::write(self.dir.path().join(path), &text).unwrap();
+        self.notification::<lsp_types::notification::DidSaveTextDocument>(
+            lsp_types::DidSaveTextDocumentParams {
+                text_document: self.doc_id(path),
+                text: Some(text),
+            },
+        )
+    }
 }
 
 impl Drop for Server {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
index 78da448..3443939 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
@@ -243,7 +243,7 @@
 impl TidyDocs {
     fn visit(&mut self, path: &Path, text: &str) {
         // Tests and diagnostic fixes don't need module level comments.
-        if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar"]) {
+        if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar", "salsa"]) {
             return;
         }
 
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs
index 223da9b..14238e2 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs
+++ b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs
@@ -1,4 +1,5 @@
-//!
+//! Implementation for `[salsa::database]` decorator.
+
 use heck::ToSnakeCase;
 use proc_macro::TokenStream;
 use syn::parse::{Parse, ParseStream};
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs
index 9df41e0..5ecd1b8 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs
+++ b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs
@@ -1,4 +1,4 @@
-//!
+//! Parenthesis helper
 pub(crate) struct Parenthesized<T>(pub(crate) T);
 
 impl<T> syn::parse::Parse for Parenthesized<T>
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs
index 5983765..659797d 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs
+++ b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs
@@ -1,4 +1,4 @@
-//!
+//! Implementation for `[salsa::query_group]` decorator.
 
 use crate::parenthesized::Parenthesized;
 use heck::ToUpperCamelCase;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived.rs b/src/tools/rust-analyzer/crates/salsa/src/derived.rs
index 3b5bd7f..fd31ab2 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/derived.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/derived.rs
@@ -1,4 +1,3 @@
-//!
 use crate::debug::TableEntry;
 use crate::durability::Durability;
 use crate::hash::FxIndexMap;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs b/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs
index 75204c8..cfafa40 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs
@@ -1,4 +1,3 @@
-//!
 use crate::debug::TableEntry;
 use crate::derived::MemoizationPolicy;
 use crate::durability::Durability;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/durability.rs b/src/tools/rust-analyzer/crates/salsa/src/durability.rs
index 44abae3..7b8e684 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/durability.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/durability.rs
@@ -1,4 +1,3 @@
-//!
 /// Describes how likely a value is to change -- how "durable" it is.
 /// By default, inputs have `Durability::LOW` and interned values have
 /// `Durability::HIGH`. But inputs can be explicitly set with other
diff --git a/src/tools/rust-analyzer/crates/salsa/src/hash.rs b/src/tools/rust-analyzer/crates/salsa/src/hash.rs
index 47a2dd1..3b2d7df 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/hash.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/hash.rs
@@ -1,4 +1,3 @@
-//!
 pub(crate) type FxHasher = std::hash::BuildHasherDefault<rustc_hash::FxHasher>;
 pub(crate) type FxIndexSet<K> = indexmap::IndexSet<K, FxHasher>;
 pub(crate) type FxIndexMap<K, V> = indexmap::IndexMap<K, V, FxHasher>;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/input.rs b/src/tools/rust-analyzer/crates/salsa/src/input.rs
index 922ec5a..f04f48e 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/input.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/input.rs
@@ -1,4 +1,3 @@
-//!
 use crate::debug::TableEntry;
 use crate::durability::Durability;
 use crate::hash::FxIndexMap;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs b/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs
index a7bbc08..b060d8a 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs
@@ -1,4 +1,3 @@
-//!
 use std::fmt;
 use std::num::NonZeroU32;
 
diff --git a/src/tools/rust-analyzer/crates/salsa/src/interned.rs b/src/tools/rust-analyzer/crates/salsa/src/interned.rs
index c065e7e..bfa9cc0 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/interned.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/interned.rs
@@ -1,4 +1,3 @@
-//!
 use crate::debug::TableEntry;
 use crate::durability::Durability;
 use crate::intern_id::InternId;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/lib.rs b/src/tools/rust-analyzer/crates/salsa/src/lib.rs
index fe80759..f86683e 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/lib.rs
@@ -1,8 +1,7 @@
-//!
 #![allow(clippy::type_complexity)]
 #![allow(clippy::question_mark)]
+#![allow(missing_docs)]
 #![warn(rust_2018_idioms)]
-#![warn(missing_docs)]
 
 //! The salsa crate is a crate for incremental recomputation.  It
 //! permits you to define a "database" of queries with both inputs and
@@ -124,9 +123,9 @@
 impl Event {
     /// Returns a type that gives a user-readable debug output.
     /// Use like `println!("{:?}", index.debug(db))`.
-    pub fn debug<'me, D: ?Sized>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me
+    pub fn debug<'me, D>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me
     where
-        D: plumbing::DatabaseOps,
+        D: ?Sized + plumbing::DatabaseOps,
     {
         EventDebug { event: self, db }
     }
@@ -206,9 +205,9 @@
 impl EventKind {
     /// Returns a type that gives a user-readable debug output.
     /// Use like `println!("{:?}", index.debug(db))`.
-    pub fn debug<'me, D: ?Sized>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me
+    pub fn debug<'me, D>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me
     where
-        D: plumbing::DatabaseOps,
+        D: ?Sized + plumbing::DatabaseOps,
     {
         EventKindDebug { kind: self, db }
     }
@@ -400,9 +399,9 @@
 
     /// Returns a type that gives a user-readable debug output.
     /// Use like `println!("{:?}", index.debug(db))`.
-    pub fn debug<D: ?Sized>(self, db: &D) -> impl std::fmt::Debug + '_
+    pub fn debug<D>(self, db: &D) -> impl std::fmt::Debug + '_
     where
-        D: plumbing::DatabaseOps,
+        D: ?Sized + plumbing::DatabaseOps,
     {
         DatabaseKeyIndexDebug { index: self, db }
     }
diff --git a/src/tools/rust-analyzer/crates/salsa/src/lru.rs b/src/tools/rust-analyzer/crates/salsa/src/lru.rs
index 1ff85a3..edad551 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/lru.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/lru.rs
@@ -1,4 +1,3 @@
-//!
 use oorandom::Rand64;
 use parking_lot::Mutex;
 use std::fmt::Debug;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs b/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs
index 1a8ff33..1dfde63 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs
@@ -1,4 +1,3 @@
-//!
 #![allow(missing_docs)]
 
 use crate::debug::TableEntry;
diff --git a/src/tools/rust-analyzer/crates/salsa/src/revision.rs b/src/tools/rust-analyzer/crates/salsa/src/revision.rs
index 559b033..204c088 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/revision.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/revision.rs
@@ -1,4 +1,3 @@
-//!
 use std::num::NonZeroU32;
 use std::sync::atomic::{AtomicU32, Ordering};
 
diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs b/src/tools/rust-analyzer/crates/salsa/src/runtime.rs
index e11cabf..4f3341f 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/runtime.rs
@@ -1,4 +1,3 @@
-//!
 use crate::durability::Durability;
 use crate::hash::FxIndexSet;
 use crate::plumbing::CycleRecoveryStrategy;
@@ -605,7 +604,7 @@
     pub(crate) fn take_inputs_from(&mut self, cycle_query: &ActiveQuery) {
         self.changed_at = cycle_query.changed_at;
         self.durability = cycle_query.durability;
-        self.dependencies = cycle_query.dependencies.clone();
+        self.dependencies.clone_from(&cycle_query.dependencies);
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs b/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs
index dd223ee..ed1d499 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs
@@ -1,4 +1,3 @@
-//!
 use triomphe::Arc;
 
 use crate::{DatabaseKeyIndex, RuntimeId};
diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs b/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs
index 7ac21de..0dbea1d 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs
@@ -1,4 +1,3 @@
-//!
 use tracing::debug;
 use triomphe::ThinArc;
 
diff --git a/src/tools/rust-analyzer/crates/salsa/src/storage.rs b/src/tools/rust-analyzer/crates/salsa/src/storage.rs
index c0e6416..e0acf44 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/storage.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/storage.rs
@@ -1,4 +1,3 @@
-//!
 use crate::{plumbing::DatabaseStorageTypes, Runtime};
 use triomphe::Arc;
 
diff --git a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
index 295b716..829b4d5 100644
--- a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
@@ -69,7 +69,7 @@
                 panic!("Use plain (non-doc) comments with tags like {tag}:\n    {first}");
             }
 
-            block.id = id.trim().to_owned();
+            id.trim().clone_into(&mut block.id);
             true
         });
         blocks
diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml
index cbda91f..9f85f01 100644
--- a/src/tools/rust-analyzer/crates/span/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/span/Cargo.toml
@@ -14,6 +14,7 @@
 salsa.workspace = true
 rustc-hash.workspace = true
 hashbrown.workspace = true
+text-size.workspace = true
 
 # local deps
 vfs.workspace = true
diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs
index c9109c7..8ca7bc2 100644
--- a/src/tools/rust-analyzer/crates/span/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/span/src/lib.rs
@@ -13,59 +13,10 @@
     map::{RealSpanMap, SpanMap},
 };
 
-pub use syntax::{TextRange, TextSize};
+pub use syntax::Edition;
+pub use text_size::{TextRange, TextSize};
 pub use vfs::FileId;
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Edition {
-    Edition2015,
-    Edition2018,
-    Edition2021,
-    Edition2024,
-}
-
-impl Edition {
-    pub const CURRENT: Edition = Edition::Edition2021;
-    pub const DEFAULT: Edition = Edition::Edition2015;
-}
-
-#[derive(Debug)]
-pub struct ParseEditionError {
-    invalid_input: String,
-}
-
-impl std::error::Error for ParseEditionError {}
-impl fmt::Display for ParseEditionError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "invalid edition: {:?}", self.invalid_input)
-    }
-}
-
-impl std::str::FromStr for Edition {
-    type Err = ParseEditionError;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let res = match s {
-            "2015" => Edition::Edition2015,
-            "2018" => Edition::Edition2018,
-            "2021" => Edition::Edition2021,
-            "2024" => Edition::Edition2024,
-            _ => return Err(ParseEditionError { invalid_input: s.to_owned() }),
-        };
-        Ok(res)
-    }
-}
-
-impl fmt::Display for Edition {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(match self {
-            Edition::Edition2015 => "2015",
-            Edition::Edition2018 => "2018",
-            Edition::Edition2021 => "2021",
-            Edition::Edition2024 => "2024",
-        })
-    }
-}
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct FilePosition {
     pub file_id: FileId,
diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs
index 1f396a1..81fc56c 100644
--- a/src/tools/rust-analyzer/crates/span/src/map.rs
+++ b/src/tools/rust-analyzer/crates/span/src/map.rs
@@ -4,17 +4,20 @@
 use std::{fmt, hash::Hash};
 
 use stdx::{always, itertools::Itertools};
-use syntax::{TextRange, TextSize};
 use vfs::FileId;
 
 use crate::{
-    ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID,
+    ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, TextRange, TextSize,
+    ROOT_ERASED_FILE_AST_ID,
 };
 
 /// Maps absolute text ranges for the corresponding file to the relevant span data.
 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
 pub struct SpanMap<S> {
     spans: Vec<(TextSize, SpanData<S>)>,
+    /// Index of the matched macro arm on successful expansion for declarative macros.
+    // FIXME: Does it make sense to have this here?
+    pub matched_arm: Option<u32>,
 }
 
 impl<S> SpanMap<S>
@@ -23,7 +26,7 @@
 {
     /// Creates a new empty [`SpanMap`].
     pub fn empty() -> Self {
-        Self { spans: Vec::new() }
+        Self { spans: Vec::new(), matched_arm: None }
     }
 
     /// Finalizes the [`SpanMap`], shrinking its backing storage and validating that the offsets are
diff --git a/src/tools/rust-analyzer/crates/syntax/src/algo.rs b/src/tools/rust-analyzer/crates/syntax/src/algo.rs
index 01f2af4..0e62de5 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/algo.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/algo.rs
@@ -255,7 +255,7 @@
 mod tests {
     use expect_test::{expect, Expect};
     use itertools::Itertools;
-    use parser::SyntaxKind;
+    use parser::{Edition, SyntaxKind};
     use text_edit::TextEdit;
 
     use crate::{AstNode, SyntaxElement};
@@ -607,8 +607,8 @@
     }
 
     fn check_diff(from: &str, to: &str, expected_diff: Expect) {
-        let from_node = crate::SourceFile::parse(from).tree().syntax().clone();
-        let to_node = crate::SourceFile::parse(to).tree().syntax().clone();
+        let from_node = crate::SourceFile::parse(from, Edition::CURRENT).tree().syntax().clone();
+        let to_node = crate::SourceFile::parse(to, Edition::CURRENT).tree().syntax().clone();
         let diff = super::diff(&from_node, &to_node);
 
         let line_number =
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
index e9ab7a4..168ca9f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
@@ -174,6 +174,7 @@
         // non-doc
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -189,6 +190,7 @@
         // non-doc
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -204,6 +206,7 @@
         // non-doc
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -218,6 +221,7 @@
         /// Number of levels
         static LEVELS: i32 = 0;
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -237,6 +241,7 @@
         /// ```
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -257,6 +262,7 @@
         /// foo
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -271,6 +277,7 @@
         /** this is mod foo*/
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -285,6 +292,7 @@
         /** this is mod foo */
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -303,6 +311,7 @@
         */
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -316,7 +325,7 @@
 #[test]
 fn test_comments_preserve_trailing_whitespace() {
     let file = SourceFile::parse(
-        "\n/// Representation of a Realm.   \n/// In the specification these are called Realm Records.\nstruct Realm {}",
+        "\n/// Representation of a Realm.   \n/// In the specification these are called Realm Records.\nstruct Realm {}", parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -335,6 +344,7 @@
         /// doc comment
         mod foo {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
@@ -360,6 +370,7 @@
    for<'a> F: Fn(&'a str)
 {}
         "#,
+        parser::Edition::CURRENT,
     )
     .ok()
     .unwrap();
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index 41d33c4..2445e4f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -1054,6 +1054,7 @@
 mod tests {
     use std::fmt;
 
+    use parser::Edition;
     use stdx::trim_indent;
     use test_utils::assert_eq_text;
 
@@ -1062,7 +1063,7 @@
     use super::*;
 
     fn ast_mut_from_text<N: AstNode>(text: &str) -> N {
-        let parse = SourceFile::parse(text);
+        let parse = SourceFile::parse(text, Edition::CURRENT);
         parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update()
     }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
index 18a56e2..28a9dad 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
@@ -89,6 +89,7 @@
             else { "else" }
         }
         "#,
+        parser::Edition::CURRENT,
     );
     let if_ = parse.tree().syntax().descendants().find_map(ast::IfExpr::cast).unwrap();
     assert_eq!(if_.then_branch().unwrap().syntax().text(), r#"{ "if" }"#);
@@ -123,6 +124,7 @@
             else { "else" }
         }
         "#,
+        parser::Edition::CURRENT,
     );
     let if_ = parse.tree().syntax().descendants().find_map(ast::IfExpr::cast).unwrap();
     assert_eq!(if_.then_branch().unwrap().syntax().text(), r#"{ "if" }"#);
@@ -386,7 +388,8 @@
 
 #[test]
 fn test_literal_with_attr() {
-    let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#);
+    let parse =
+        ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#, parser::Edition::CURRENT);
     let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap();
     assert_eq!(lit.token().text(), r#""Hello""#);
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index ff18fee..186f1b0 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -11,7 +11,7 @@
 //! term, it will be replaced with direct tree manipulation.
 
 use itertools::Itertools;
-use parser::T;
+use parser::{Edition, T};
 use rowan::NodeOrToken;
 use stdx::{format_to, format_to_acc, never};
 
@@ -1127,7 +1127,7 @@
 
 #[track_caller]
 fn ast_from_text<N: AstNode>(text: &str) -> N {
-    let parse = SourceFile::parse(text);
+    let parse = SourceFile::parse(text, Edition::CURRENT);
     let node = match parse.tree().syntax().descendants().find_map(N::cast) {
         Some(it) => it,
         None => {
@@ -1153,12 +1153,13 @@
 
 pub mod tokens {
     use once_cell::sync::Lazy;
+    use parser::Edition;
 
     use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken};
 
     pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
         SourceFile::parse(
-            "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\nimpl A for B where: {}",
+            "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT,
         )
     });
 
@@ -1186,13 +1187,13 @@
 
     pub fn whitespace(text: &str) -> SyntaxToken {
         assert!(text.trim().is_empty());
-        let sf = SourceFile::parse(text).ok().unwrap();
+        let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap();
         sf.syntax().clone_for_update().first_child_or_token().unwrap().into_token().unwrap()
     }
 
     pub fn doc_comment(text: &str) -> SyntaxToken {
         assert!(!text.trim().is_empty());
-        let sf = SourceFile::parse(text).ok().unwrap();
+        let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap();
         sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
     }
 
@@ -1240,7 +1241,7 @@
 
     impl WsBuilder {
         pub fn new(text: &str) -> WsBuilder {
-            WsBuilder(SourceFile::parse(text).ok().unwrap())
+            WsBuilder(SourceFile::parse(text, Edition::CURRENT).ok().unwrap())
         }
         pub fn ws(&self) -> SyntaxToken {
             self.0.syntax().first_child_or_token().unwrap().into_token().unwrap()
diff --git a/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs b/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs
index 2873867..682dcd7 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs
@@ -4,6 +4,7 @@
 
 use std::str::{self, FromStr};
 
+use parser::Edition;
 use text_edit::Indel;
 
 use crate::{validation, AstNode, SourceFile, TextRange};
@@ -14,7 +15,7 @@
 }
 
 pub fn check_parser(text: &str) {
-    let file = SourceFile::parse(text);
+    let file = SourceFile::parse(text, Edition::CURRENT);
     check_file_invariants(&file.tree());
 }
 
@@ -48,11 +49,11 @@
 
     #[allow(clippy::print_stderr)]
     pub fn run(&self) {
-        let parse = SourceFile::parse(&self.text);
-        let new_parse = parse.reparse(&self.edit);
+        let parse = SourceFile::parse(&self.text, Edition::CURRENT);
+        let new_parse = parse.reparse(&self.edit, Edition::CURRENT);
         check_file_invariants(&new_parse.tree());
         assert_eq!(&new_parse.tree().syntax().text().to_string(), &self.edited_text);
-        let full_reparse = SourceFile::parse(&self.edited_text);
+        let full_reparse = SourceFile::parse(&self.edited_text, Edition::CURRENT);
         for (a, b) in
             new_parse.tree().syntax().descendants().zip(full_reparse.tree().syntax().descendants())
         {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
index a3023c3..36615d1 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
@@ -2,11 +2,13 @@
 //!
 //! Please avoid adding new usages of the functions in this module
 
+use parser::Edition;
+
 use crate::{ast, AstNode};
 
 pub fn parse_expr_from_str(s: &str) -> Option<ast::Expr> {
     let s = s.trim();
-    let file = ast::SourceFile::parse(&format!("const _: () = {s};"));
+    let file = ast::SourceFile::parse(&format!("const _: () = {s};"), Edition::CURRENT);
     let expr = file.syntax_node().descendants().find_map(ast::Expr::cast)?;
     if expr.syntax().text() != s {
         return None;
diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
index 1bb82cc..3a9ebaf 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
@@ -60,11 +60,12 @@
     },
     token_text::TokenText,
 };
-pub use parser::{SyntaxKind, T};
+pub use parser::{Edition, SyntaxKind, T};
 pub use rowan::{
     api::Preorder, Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize,
     TokenAtOffset, WalkEvent,
 };
+pub use rustc_lexer::unescape;
 pub use smol_str::{format_smolstr, SmolStr};
 
 /// `Parse` is the result of the parsing: a syntax tree and a collection of
@@ -141,8 +142,8 @@
         buf
     }
 
-    pub fn reparse(&self, indel: &Indel) -> Parse<SourceFile> {
-        self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel))
+    pub fn reparse(&self, indel: &Indel, edition: Edition) -> Parse<SourceFile> {
+        self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel, edition))
     }
 
     fn incremental_reparse(&self, indel: &Indel) -> Option<Parse<SourceFile>> {
@@ -159,10 +160,10 @@
         })
     }
 
-    fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> {
+    fn full_reparse(&self, indel: &Indel, edition: Edition) -> Parse<SourceFile> {
         let mut text = self.tree().syntax().text().to_string();
         indel.apply(&mut text);
-        SourceFile::parse(&text)
+        SourceFile::parse(&text, edition)
     }
 }
 
@@ -170,9 +171,9 @@
 pub use crate::ast::SourceFile;
 
 impl SourceFile {
-    pub fn parse(text: &str) -> Parse<SourceFile> {
+    pub fn parse(text: &str, edition: Edition) -> Parse<SourceFile> {
         let _p = tracing::span!(tracing::Level::INFO, "SourceFile::parse").entered();
-        let (green, errors) = parsing::parse_text(text);
+        let (green, errors) = parsing::parse_text(text, edition);
         let root = SyntaxNode::new_root(green.clone());
 
         assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
@@ -185,7 +186,10 @@
 }
 
 impl ast::TokenTree {
-    pub fn reparse_as_comma_separated_expr(self) -> Parse<ast::MacroEagerInput> {
+    pub fn reparse_as_comma_separated_expr(
+        self,
+        edition: parser::Edition,
+    ) -> Parse<ast::MacroEagerInput> {
         let tokens = self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token);
 
         let mut parser_input = parser::Input::default();
@@ -219,7 +223,7 @@
             }
         }
 
-        let parser_output = parser::TopEntryPoint::MacroEagerInput.parse(&parser_input);
+        let parser_output = parser::TopEntryPoint::MacroEagerInput.parse(&parser_input, edition);
 
         let mut tokens =
             self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token);
@@ -337,7 +341,7 @@
     //
     // The `parse` method returns a `Parse` -- a pair of syntax tree and a list
     // of errors. That is, syntax tree is constructed even in presence of errors.
-    let parse = SourceFile::parse(source_code);
+    let parse = SourceFile::parse(source_code, parser::Edition::CURRENT);
     assert!(parse.errors().is_empty());
 
     // The `tree` method returns an owned syntax node of type `SourceFile`.
diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing.rs
index d750476..420f493 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/parsing.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/parsing.rs
@@ -9,11 +9,11 @@
 
 pub(crate) use crate::parsing::reparsing::incremental_reparse;
 
-pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
+pub(crate) fn parse_text(text: &str, edition: parser::Edition) -> (GreenNode, Vec<SyntaxError>) {
     let _p = tracing::span!(tracing::Level::INFO, "parse_text").entered();
     let lexed = parser::LexedStr::new(text);
     let parser_input = lexed.to_input();
-    let parser_output = parser::TopEntryPoint::SourceFile.parse(&parser_input);
+    let parser_output = parser::TopEntryPoint::SourceFile.parse(&parser_input, edition);
     let (node, errors, _eof) = build_tree(lexed, parser_output);
     (node, errors)
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
index 14715b5..354b89f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
@@ -26,7 +26,9 @@
         return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
     }
 
-    if let Some((green, new_errors, old_range)) = reparse_block(node, edit) {
+    if let Some((green, new_errors, old_range)) =
+        reparse_block(node, edit, parser::Edition::CURRENT)
+    {
         return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
     }
     None
@@ -84,6 +86,7 @@
 fn reparse_block(
     root: &SyntaxNode,
     edit: &Indel,
+    edition: parser::Edition,
 ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
     let (node, reparser) = find_reparsable_node(root, edit.delete)?;
     let text = get_text_after_edit(node.clone().into(), edit);
@@ -94,7 +97,7 @@
         return None;
     }
 
-    let tree_traversal = reparser.parse(&parser_input);
+    let tree_traversal = reparser.parse(&parser_input, edition);
 
     let (green, new_parser_errors, _eof) = build_tree(lexed, tree_traversal);
 
@@ -174,6 +177,7 @@
 
 #[cfg(test)]
 mod tests {
+    use parser::Edition;
     use test_utils::{assert_eq_text, extract_range};
 
     use super::*;
@@ -188,9 +192,9 @@
             after
         };
 
-        let fully_reparsed = SourceFile::parse(&after);
+        let fully_reparsed = SourceFile::parse(&after, Edition::CURRENT);
         let incrementally_reparsed: Parse<SourceFile> = {
-            let before = SourceFile::parse(&before);
+            let before = SourceFile::parse(&before, Edition::CURRENT);
             let (green, new_errors, range) = incremental_reparse(
                 before.tree().syntax(),
                 &edit,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
index fb8aee9..ed4894f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
@@ -120,7 +120,7 @@
 fn test_local_syntax_ptr() {
     use crate::{ast, AstNode, SourceFile};
 
-    let file = SourceFile::parse("struct Foo { f: u32, }").ok().unwrap();
+    let file = SourceFile::parse("struct Foo { f: u32, }", parser::Edition::CURRENT).ok().unwrap();
     let field = file.syntax().descendants().find_map(ast::RecordField::cast).unwrap();
     let ptr = SyntaxNodePtr::new(field.syntax());
     let field_syntax = ptr.to_node(file.syntax());
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests.rs b/src/tools/rust-analyzer/crates/syntax/src/tests.rs
index 439daa3..f0d58ef 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests.rs
@@ -5,6 +5,7 @@
 
 use ast::HasName;
 use expect_test::expect_file;
+use parser::Edition;
 use rayon::prelude::*;
 use stdx::format_to_acc;
 use test_utils::{bench, bench_fixture, project_root};
@@ -19,7 +20,7 @@
 }
     "#;
 
-    let parse = SourceFile::parse(code);
+    let parse = SourceFile::parse(code, Edition::CURRENT);
     // eprintln!("{:#?}", parse.syntax_node());
     assert!(parse.ok().is_ok());
 }
@@ -33,7 +34,7 @@
     let data = bench_fixture::glorious_old_parser();
     let tree = {
         let _b = bench("parsing");
-        let p = SourceFile::parse(&data);
+        let p = SourceFile::parse(&data, Edition::CURRENT);
         assert!(p.errors().is_empty());
         assert_eq!(p.tree().syntax.text_range().len(), 352474.into());
         p.tree()
@@ -50,7 +51,7 @@
 #[test]
 fn validation_tests() {
     dir_tests(&test_data_dir(), &["parser/validation"], "rast", |text, path| {
-        let parse = SourceFile::parse(text);
+        let parse = SourceFile::parse(text, Edition::CURRENT);
         let errors = parse.errors();
         assert_errors_are_present(&errors, path);
         parse.debug_dump()
@@ -110,7 +111,7 @@
         .into_par_iter()
         .filter_map(|file| {
             let text = read_text(&file);
-            match SourceFile::parse(&text).ok() {
+            match SourceFile::parse(&text, Edition::CURRENT).ok() {
                 Ok(_) => None,
                 Err(err) => Some((file, err)),
             }
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index c8d785f..89ed6a6 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -189,8 +189,8 @@
                     meta.edition,
                     Some(crate_name.clone().into()),
                     version,
-                    meta.cfg.clone(),
-                    Some(meta.cfg),
+                    From::from(meta.cfg.clone()),
+                    Some(From::from(meta.cfg)),
                     meta.env,
                     false,
                     origin,
@@ -209,7 +209,7 @@
                 assert!(default_crate_root.is_none());
                 default_crate_root = Some(file_id);
                 default_cfg.extend(meta.cfg.into_iter());
-                default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
+                default_env.extend_from_other(&meta.env);
             }
 
             source_change.change_file(file_id, Some(text));
@@ -227,8 +227,8 @@
                 Edition::CURRENT,
                 Some(CrateName::new("test").unwrap().into()),
                 None,
-                default_cfg.clone(),
-                Some(default_cfg),
+                From::from(default_cfg.clone()),
+                Some(From::from(default_cfg)),
                 default_env,
                 false,
                 CrateOrigin::Local { repo: None, name: None },
@@ -260,7 +260,7 @@
 
             let core_crate = crate_graph.add_crate_root(
                 core_file,
-                Edition::Edition2021,
+                Edition::CURRENT,
                 Some(CrateDisplayName::from_canonical_name("core".to_owned())),
                 None,
                 Default::default(),
@@ -299,7 +299,7 @@
 
             let proc_macros_crate = crate_graph.add_crate_root(
                 proc_lib_file,
-                Edition::Edition2021,
+                Edition::CURRENT,
                 Some(CrateDisplayName::from_canonical_name("proc_macros".to_owned())),
                 None,
                 Default::default(),
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index 7e34c36..aafe4fb 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -186,7 +186,7 @@
 
         if let Some(meta) = fixture.strip_prefix("//- target_data_layout:") {
             let (meta, remain) = meta.split_once('\n').unwrap();
-            target_data_layout = meta.trim().to_owned();
+            meta.trim().clone_into(&mut target_data_layout);
             fixture = remain;
         }
 
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
index 4cfdec2..45bb777 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
@@ -9,7 +9,10 @@
 
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
-use std::fs;
+use std::{
+    fs,
+    path::{Component, Path},
+};
 
 use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
 use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
@@ -206,6 +209,11 @@
                                 return true;
                             }
                             let path = entry.path();
+
+                            if path_is_parent_symlink(path) {
+                                return false;
+                            }
+
                             root == path
                                 || dirs.exclude.iter().chain(&dirs.include).all(|it| it != path)
                         });
@@ -258,3 +266,21 @@
 fn log_notify_error<T>(res: notify::Result<T>) -> Option<T> {
     res.map_err(|err| tracing::warn!("notify error: {}", err)).ok()
 }
+
+/// Is `path` a symlink to a parent directory?
+///
+/// Including this path is guaranteed to cause an infinite loop. This
+/// heuristic is not sufficient to catch all symlink cycles (it's
+/// possible to construct cycle using two or more symlinks), but it
+/// catches common cases.
+fn path_is_parent_symlink(path: &Path) -> bool {
+    let Ok(destination) = std::fs::read_link(path) else {
+        return false;
+    };
+
+    // If the symlink is of the form "../..", it's a parent symlink.
+    let is_relative_parent =
+        destination.components().all(|c| matches!(c, Component::CurDir | Component::ParentDir));
+
+    is_relative_parent || path.starts_with(destination)
+}
diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
index 7eeb10d..d7d283c 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
@@ -132,6 +132,10 @@
     ///
     /// `scratch_space` is used as a buffer and will be entirely replaced.
     fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize {
+        // `path` is a file, but r-a only cares about the containing directory. We don't
+        // want `/foo/bar_baz.rs` to be attributed to source root directory `/foo/bar`.
+        let path = path.parent().unwrap_or_else(|| path.clone());
+
         scratch_space.clear();
         path.encode(scratch_space);
         let automaton = PrefixOf::new(scratch_space.as_slice());
diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs
index 2146df1..3cdb60d 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs
@@ -40,3 +40,26 @@
     let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
     assert_eq!(partition, vec![1, 1, 0]);
 }
+
+/// Ensure that we don't consider `/foo/bar_baz.rs` to be in the
+/// `/foo/bar/` root.
+#[test]
+fn name_prefix_partially_matches() {
+    let mut file_set = FileSetConfig::builder();
+    file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]);
+    file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar".into())]);
+    let file_set = file_set.build();
+
+    let mut vfs = Vfs::default();
+
+    // These two are both in /foo.
+    vfs.set_file_contents(VfsPath::new_virtual_path("/foo/lib.rs".into()), Some(Vec::new()));
+    vfs.set_file_contents(VfsPath::new_virtual_path("/foo/bar_baz.rs".into()), Some(Vec::new()));
+
+    // Only this file is in /foo/bar.
+    vfs.set_file_contents(VfsPath::new_virtual_path("/foo/bar/biz.rs".into()), Some(Vec::new()));
+
+    let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
+
+    assert_eq!(partition, vec![2, 1, 0]);
+}
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index 939b181..f181508 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp/ext.rs hash: 223f48a89a5126a0
+lsp/ext.rs hash: dd51139b0530147e
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -444,7 +444,7 @@
     // For each file which its uri is in this list, the response
     // contains all tests that are located in this file, and
     // client should remove old tests not included in the response.
-    scopeFile: lc.TextDocumentIdentifier[] | undefined;    
+    scopeFile: lc.TextDocumentIdentifier[] | undefined;
 }
 ```
 
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index c4024f6..a03ab00 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -19,6 +19,11 @@
 --
 How many worker threads to handle priming caches. The default `0` means to pick automatically.
 --
+[[rust-analyzer.cargo.allTargets]]rust-analyzer.cargo.allTargets (default: `true`)::
++
+--
+Pass `--all-targets` to cargo invocation.
+--
 [[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`)::
 +
 --
@@ -83,10 +88,18 @@
 Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
 avoid checking unnecessary things.
 --
-[[rust-analyzer.cargo.cfgs]]rust-analyzer.cargo.cfgs (default: `{}`)::
+[[rust-analyzer.cargo.cfgs]]rust-analyzer.cargo.cfgs::
 +
 --
+Default:
+----
+{
+  "debug_assertions": null,
+  "miri": null
+}
+----
 List of cfg options to enable with the given values.
+
 --
 [[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`)::
 +
@@ -154,20 +167,16 @@
 Set to `true` to use a subdirectory of the existing target directory or
 set to a path relative to the workspace to use that path.
 --
-[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
-+
---
-Unsets the implicit `#[cfg(test)]` for the specified crates.
---
 [[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`)::
 +
 --
 Run the check command for diagnostics on save.
 --
-[[rust-analyzer.check.allTargets]]rust-analyzer.check.allTargets (default: `true`)::
+[[rust-analyzer.check.allTargets]]rust-analyzer.check.allTargets (default: `null`)::
 +
 --
-Check all targets and tests (`--all-targets`).
+Check all targets and tests (`--all-targets`). Defaults to
+`#rust-analyzer.cargo.allTargets#`.
 --
 [[rust-analyzer.check.command]]rust-analyzer.check.command (default: `"check"`)::
 +
@@ -315,46 +324,46 @@
 Default:
 ----
 {
-            "Arc::new": {
-                "postfix": "arc",
-                "body": "Arc::new(${receiver})",
-                "requires": "std::sync::Arc",
-                "description": "Put the expression into an `Arc`",
-                "scope": "expr"
-            },
-            "Rc::new": {
-                "postfix": "rc",
-                "body": "Rc::new(${receiver})",
-                "requires": "std::rc::Rc",
-                "description": "Put the expression into an `Rc`",
-                "scope": "expr"
-            },
-            "Box::pin": {
-                "postfix": "pinbox",
-                "body": "Box::pin(${receiver})",
-                "requires": "std::boxed::Box",
-                "description": "Put the expression into a pinned `Box`",
-                "scope": "expr"
-            },
-            "Ok": {
-                "postfix": "ok",
-                "body": "Ok(${receiver})",
-                "description": "Wrap the expression in a `Result::Ok`",
-                "scope": "expr"
-            },
-            "Err": {
-                "postfix": "err",
-                "body": "Err(${receiver})",
-                "description": "Wrap the expression in a `Result::Err`",
-                "scope": "expr"
-            },
-            "Some": {
-                "postfix": "some",
-                "body": "Some(${receiver})",
-                "description": "Wrap the expression in an `Option::Some`",
-                "scope": "expr"
-            }
-        }
+  "Arc::new": {
+    "postfix": "arc",
+    "body": "Arc::new(${receiver})",
+    "requires": "std::sync::Arc",
+    "description": "Put the expression into an `Arc`",
+    "scope": "expr"
+  },
+  "Rc::new": {
+    "postfix": "rc",
+    "body": "Rc::new(${receiver})",
+    "requires": "std::rc::Rc",
+    "description": "Put the expression into an `Rc`",
+    "scope": "expr"
+  },
+  "Box::pin": {
+    "postfix": "pinbox",
+    "body": "Box::pin(${receiver})",
+    "requires": "std::boxed::Box",
+    "description": "Put the expression into a pinned `Box`",
+    "scope": "expr"
+  },
+  "Ok": {
+    "postfix": "ok",
+    "body": "Ok(${receiver})",
+    "description": "Wrap the expression in a `Result::Ok`",
+    "scope": "expr"
+  },
+  "Err": {
+    "postfix": "err",
+    "body": "Err(${receiver})",
+    "description": "Wrap the expression in a `Result::Err`",
+    "scope": "expr"
+  },
+  "Some": {
+    "postfix": "some",
+    "body": "Some(${receiver})",
+    "description": "Wrap the expression in an `Option::Some`",
+    "scope": "expr"
+  }
+}
 ----
 Custom completion snippets.
 
@@ -839,6 +848,24 @@
 Additional arguments to be passed to cargo for runnables such as
 tests or binaries. For example, it may be `--release`.
 --
+[[rust-analyzer.runnables.extraTestBinaryArgs]]rust-analyzer.runnables.extraTestBinaryArgs::
++
+--
+Default:
+----
+[
+  "--show-output"
+]
+----
+Additional arguments to be passed through Cargo to launched tests, benchmarks, or
+doc-tests.
+
+Unless the launched target uses a
+[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),
+they will end up being interpreted as options to
+[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).
+
+--
 [[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`)::
 +
 --
diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc
index 8bc11fd..6521b6d 100644
--- a/src/tools/rust-analyzer/docs/user/manual.adoc
+++ b/src/tools/rust-analyzer/docs/user/manual.adoc
@@ -210,7 +210,10 @@
 This prevents access to the system's C compiler, a system-wide installation of Rust, or any other libraries you might want to link to.
 Some compilers and libraries can be acquired as Flatpak SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or `org.freedesktop.Sdk.Extension.llvm15`.
 
-If you use a Flatpak SDK for Rust, there should be no extra steps necessary.
+If you use a Flatpak SDK for Rust, it must be in your `PATH`:
+
+ * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08`
+ * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`)
 
 If you want to use Flatpak in combination with `rustup`, the following steps might help:
 
diff --git a/src/tools/rust-analyzer/editors/code/language-configuration.json b/src/tools/rust-analyzer/editors/code/language-configuration.json
index bdae0e6..6619d0c 100644
--- a/src/tools/rust-analyzer/editors/code/language-configuration.json
+++ b/src/tools/rust-analyzer/editors/code/language-configuration.json
@@ -19,7 +19,8 @@
         { "open": "(", "close": ")" },
         { "open": "\"", "close": "\"", "notIn": ["string"] },
         { "open": "/*", "close": " */", "notIn": ["string"] },
-        { "open": "`", "close": "`", "notIn": ["string"] }
+        { "open": "`", "close": "`", "notIn": ["string"] },
+        { "open": "```", "close": "```", "notIn": ["string"] }
     ],
     "autoCloseBefore": ";:.,=}])> \n\t",
     "surroundingPairs": [
@@ -29,7 +30,8 @@
         ["<", ">"],
         ["\"", "\""],
         ["'", "'"],
-        ["`", "`"]
+        ["`", "`"],
+        ["```", "```"]
     ],
     "indentationRules": {
         "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$",
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index c3ea1ce..389e1b8 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -546,6 +546,11 @@
                     "minimum": 0,
                     "maximum": 255
                 },
+                "rust-analyzer.cargo.allTargets": {
+                    "markdownDescription": "Pass `--all-targets` to cargo invocation.",
+                    "default": true,
+                    "type": "boolean"
+                },
                 "rust-analyzer.cargo.autoreload": {
                     "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` or `.cargo/config.toml` changes.",
                     "default": true,
@@ -605,7 +610,10 @@
                 },
                 "rust-analyzer.cargo.cfgs": {
                     "markdownDescription": "List of cfg options to enable with the given values.",
-                    "default": {},
+                    "default": {
+                        "debug_assertions": null,
+                        "miri": null
+                    },
                     "type": "object"
                 },
                 "rust-analyzer.cargo.extraArgs": {
@@ -691,25 +699,18 @@
                         }
                     ]
                 },
-                "rust-analyzer.cargo.unsetTest": {
-                    "markdownDescription": "Unsets the implicit `#[cfg(test)]` for the specified crates.",
-                    "default": [
-                        "core"
-                    ],
-                    "type": "array",
-                    "items": {
-                        "type": "string"
-                    }
-                },
                 "rust-analyzer.checkOnSave": {
                     "markdownDescription": "Run the check command for diagnostics on save.",
                     "default": true,
                     "type": "boolean"
                 },
                 "rust-analyzer.check.allTargets": {
-                    "markdownDescription": "Check all targets and tests (`--all-targets`).",
-                    "default": true,
-                    "type": "boolean"
+                    "markdownDescription": "Check all targets and tests (`--all-targets`). Defaults to\n`#rust-analyzer.cargo.allTargets#`.",
+                    "default": null,
+                    "type": [
+                        "null",
+                        "boolean"
+                    ]
                 },
                 "rust-analyzer.check.command": {
                     "markdownDescription": "Cargo command to use for `cargo check`.",
@@ -1586,6 +1587,16 @@
                         "type": "string"
                     }
                 },
+                "rust-analyzer.runnables.extraTestBinaryArgs": {
+                    "markdownDescription": "Additional arguments to be passed through Cargo to launched tests, benchmarks, or\ndoc-tests.\n\nUnless the launched target uses a\n[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),\nthey will end up being interpreted as options to\n[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).",
+                    "default": [
+                        "--show-output"
+                    ],
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
                 "rust-analyzer.rustc.source": {
                     "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.",
                     "default": null,
@@ -1927,6 +1938,11 @@
                 "description": "Style for const generics"
             },
             {
+                "id": "const",
+                "description": "Style for consts",
+                "superType": "variable"
+            },
+            {
                 "id": "derive",
                 "description": "Style for derives",
                 "superType": "attribute"
@@ -1972,20 +1988,25 @@
                 "superType": "punctuation"
             },
             {
-                "id": "operator",
-                "description": "Style for operators",
-                "superType": "punctuation"
-            },
-            {
                 "id": "parenthesis",
                 "description": "Style for ( or )",
                 "superType": "punctuation"
             },
             {
+                "id": "procMacro",
+                "description": "Style for proc macro code",
+                "superType": "macro"
+            },
+            {
                 "id": "punctuation",
                 "description": "Style for generic punctuation"
             },
             {
+                "id": "operator",
+                "description": "Style for operators",
+                "superType": "punctuation"
+            },
+            {
                 "id": "selfKeyword",
                 "description": "Style for the self keyword",
                 "superType": "keyword"
@@ -2001,6 +2022,16 @@
                 "superType": "punctuation"
             },
             {
+                "id": "static",
+                "description": "Style for statics",
+                "superType": "variable"
+            },
+            {
+                "id": "toolModule",
+                "description": "Style for tool module attributes",
+                "superType": "decorator"
+            },
+            {
                 "id": "typeAlias",
                 "description": "Style for type aliases",
                 "superType": "type"
@@ -2057,10 +2088,18 @@
                 "description": "Style for items that are defined outside of the current crate"
             },
             {
+                "id": "macro",
+                "description": "Style for tokens inside of macro calls"
+            },
+            {
                 "id": "mutable",
                 "description": "Style for mutable locals and statics as well as functions taking `&mut self`"
             },
             {
+                "id": "procMacro",
+                "description": "Style for tokens inside of proc-macro calls"
+            },
+            {
                 "id": "public",
                 "description": "Style for items that are from the current crate and are `pub`"
             },
diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts
index 1cbf247..372dc8b 100644
--- a/src/tools/rust-analyzer/editors/code/src/client.ts
+++ b/src/tools/rust-analyzer/editors/code/src/client.ts
@@ -131,7 +131,10 @@
                             ? diag.code
                             : diag.code?.value;
                     if (
-                        value === "unlinked-file" &&
+                        // FIXME: We currently emit this diagnostic way too early, before we have
+                        // loaded the project fully
+                        // value === "unlinked-file" &&
+                        value === "temporary-disabled" &&
                         !unlinkedFiles.includes(uri) &&
                         diag.message !== "file not included in module tree"
                     ) {
diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts
index 02ccbb6..4470689 100644
--- a/src/tools/rust-analyzer/editors/code/src/run.ts
+++ b/src/tools/rust-analyzer/editors/code/src/run.ts
@@ -2,7 +2,6 @@
 import type * as lc from "vscode-languageclient";
 import * as ra from "./lsp_ext";
 import * as tasks from "./tasks";
-import * as toolchain from "./toolchain";
 
 import type { CtxInit } from "./ctx";
 import { makeDebugConfig } from "./debug";
@@ -112,22 +111,12 @@
         throw `Unexpected runnable kind: ${runnable.kind}`;
     }
 
-    let program: string;
-    let args = createArgs(runnable);
-    if (runnable.args.overrideCargo) {
-        // Split on spaces to allow overrides like "wrapper cargo".
-        const cargoParts = runnable.args.overrideCargo.split(" ");
+    const args = createArgs(runnable);
 
-        program = unwrapUndefinable(cargoParts[0]);
-        args = [...cargoParts.slice(1), ...args];
-    } else {
-        program = await toolchain.cargoPath();
-    }
-
-    const definition: tasks.RustTargetDefinition = {
+    const definition: tasks.CargoTaskDefinition = {
         type: tasks.TASK_TYPE,
-        program,
-        args,
+        command: unwrapUndefinable(args[0]), // run, test, etc...
+        args: args.slice(1),
         cwd: runnable.args.workspaceRoot || ".",
         env: prepareEnv(runnable, config.runnablesExtraEnv),
         overrideCargo: runnable.args.overrideCargo,
diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts
index 89abb37..2b3abc5 100644
--- a/src/tools/rust-analyzer/editors/code/src/tasks.ts
+++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts
@@ -2,17 +2,26 @@
 import * as toolchain from "./toolchain";
 import type { Config } from "./config";
 import { log } from "./util";
+import { unwrapUndefinable } from "./undefinable";
 
 // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
 // our configuration should be compatible with it so use the same key.
 export const TASK_TYPE = "cargo";
+
 export const TASK_SOURCE = "rust";
 
-export interface RustTargetDefinition extends vscode.TaskDefinition {
-    program: string;
-    args: string[];
+export interface CargoTaskDefinition extends vscode.TaskDefinition {
+    // The cargo command, such as "run" or "check".
+    command: string;
+    // Additional arguments passed to the cargo command.
+    args?: string[];
+    // The working directory to run the cargo command in.
     cwd?: string;
+    // The shell environment.
     env?: { [key: string]: string };
+    // Override the cargo executable name, such as
+    // "my_custom_cargo_bin".
+    overrideCargo?: string;
 }
 
 class RustTaskProvider implements vscode.TaskProvider {
@@ -37,14 +46,12 @@
             { command: "run", group: undefined },
         ];
 
-        const cargoPath = await toolchain.cargoPath();
-
         const tasks: vscode.Task[] = [];
         for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
             for (const def of defs) {
                 const vscodeTask = await buildRustTask(
                     workspaceTarget,
-                    { type: TASK_TYPE, program: cargoPath, args: [def.command] },
+                    { type: TASK_TYPE, command: def.command },
                     `cargo ${def.command}`,
                     this.config.problemMatcher,
                     this.config.cargoRunner,
@@ -62,7 +69,7 @@
         // we need to inform VSCode how to execute that command by creating
         // a ShellExecution for it.
 
-        const definition = task.definition as RustTargetDefinition;
+        const definition = task.definition as CargoTaskDefinition;
 
         if (definition.type === TASK_TYPE) {
             return await buildRustTask(
@@ -80,16 +87,34 @@
 
 export async function buildRustTask(
     scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined,
-    definition: RustTargetDefinition,
+    definition: CargoTaskDefinition,
     name: string,
     problemMatcher: string[],
     customRunner?: string,
     throwOnError: boolean = false,
 ): Promise<vscode.Task> {
-    let exec: vscode.ProcessExecution | vscode.ShellExecution | undefined = undefined;
+    const exec = await cargoToExecution(definition, customRunner, throwOnError);
 
+    return new vscode.Task(
+        definition,
+        // scope can sometimes be undefined. in these situations we default to the workspace taskscope as
+        // recommended by the official docs: https://code.visualstudio.com/api/extension-guides/task-provider#task-provider)
+        scope ?? vscode.TaskScope.Workspace,
+        name,
+        TASK_SOURCE,
+        exec,
+        problemMatcher,
+    );
+}
+
+async function cargoToExecution(
+    definition: CargoTaskDefinition,
+    customRunner: string | undefined,
+    throwOnError: boolean,
+): Promise<vscode.ProcessExecution | vscode.ShellExecution> {
     if (customRunner) {
         const runnerCommand = `${customRunner}.buildShellExecution`;
+
         try {
             const runnerArgs = {
                 kind: TASK_TYPE,
@@ -100,7 +125,7 @@
             const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs);
             if (customExec) {
                 if (customExec instanceof vscode.ShellExecution) {
-                    exec = customExec;
+                    return customExec;
                 } else {
                     log.debug("Invalid cargo ShellExecution", customExec);
                     throw "Invalid cargo ShellExecution.";
@@ -113,20 +138,20 @@
         }
     }
 
-    if (!exec) {
-        exec = new vscode.ProcessExecution(definition.program, definition.args, definition);
-    }
+    // Check whether we must use a user-defined substitute for cargo.
+    // Split on spaces to allow overrides like "wrapper cargo".
+    const cargoPath = await toolchain.cargoPath();
+    const cargoCommand = definition.overrideCargo?.split(" ") ?? [cargoPath];
 
-    return new vscode.Task(
-        definition,
-        // scope can sometimes be undefined. in these situations we default to the workspace taskscope as
-        // recommended by the official docs: https://code.visualstudio.com/api/extension-guides/task-provider#task-provider)
-        scope ?? vscode.TaskScope.Workspace,
-        name,
-        TASK_SOURCE,
-        exec,
-        problemMatcher,
-    );
+    const args = [definition.command].concat(definition.args ?? []);
+    const fullCommand = [...cargoCommand, ...args];
+
+    const processName = unwrapUndefinable(fullCommand[0]);
+
+    return new vscode.ProcessExecution(processName, fullCommand.slice(1), {
+        cwd: definition.cwd,
+        env: definition.env,
+    });
 }
 
 export function activateTaskProvider(config: Config): vscode.Disposable {
diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs
index 7dc1b40..b23d700 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen.rs
@@ -84,7 +84,7 @@
                 panic!("Use plain (non-doc) comments with tags like {tag}:\n    {first}");
             }
 
-            block.id = id.trim().to_owned();
+            id.trim().clone_into(&mut block.id);
             true
         });
         blocks
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
index 8221c57..c246ee9 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
@@ -65,11 +65,11 @@
         (">>=", "SHREQ"),
     ],
     keywords: &[
-        "as", "async", "await", "box", "break", "const", "continue", "crate", "do", "dyn", "else",
-        "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "macro",
-        "match", "mod", "move", "mut", "pub", "ref", "return", "become", "self", "Self", "static",
-        "struct", "super", "trait", "true", "try", "type", "unsafe", "use", "where", "while",
-        "yield",
+        "abstract", "as", "async", "await", "become", "box", "break", "const", "continue", "crate",
+        "do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in",
+        "let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", "pub", "ref",
+        "return", "self", "Self", "static", "struct", "super", "trait", "true", "try", "type",
+        "typeof", "unsafe", "unsized", "use", "virtual", "where", "while", "yield",
     ],
     contextual_keywords: &[
         "auto",
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/lints.rs b/src/tools/rust-analyzer/xtask/src/codegen/lints.rs
index 63abcfc..6975f93 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/lints.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/lints.rs
@@ -280,7 +280,7 @@
             let line = &line[..up_to];
 
             let clippy_lint = clippy_lints.last_mut().expect("clippy lint must already exist");
-            clippy_lint.help = unescape(line).trim().to_owned();
+            unescape(line).trim().clone_into(&mut clippy_lint.help);
         }
     }
     clippy_lints.sort_by(|lint, lint2| lint.id.cmp(&lint2.id));
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index 820eccb..d8cb26a 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -1,6 +1,4 @@
-use rustc_ast::ast::{
-    self, BindingAnnotation, ByRef, Pat, PatField, PatKind, RangeEnd, RangeSyntax,
-};
+use rustc_ast::ast::{self, BindingMode, ByRef, Pat, PatField, PatKind, RangeEnd, RangeSyntax};
 use rustc_ast::ptr;
 use rustc_span::{BytePos, Span};
 
@@ -106,7 +104,7 @@
                 write_list(&items, &fmt)
             }
             PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape),
-            PatKind::Ident(BindingAnnotation(by_ref, mutability), ident, ref sub_pat) => {
+            PatKind::Ident(BindingMode(by_ref, mutability), ident, ref sub_pat) => {
                 let mut_prefix = format_mutability(mutability).trim();
 
                 let (ref_kw, mut_infix) = match by_ref {
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index 58302b8..96866e7 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -13,7 +13,9 @@
 ignore = "0.4.18"
 semver = "1.0"
 termcolor = "1.1.3"
+rustc-hash = "1.1.0"
 
 [[bin]]
 name = "rust-tidy"
 path = "src/main.rs"
+test = false
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 211dc34..dff1526 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -74,8 +74,12 @@
 ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.rs
 ui/associated-types/issue-18655.rs
 ui/associated-types/issue-19081.rs
+ui/associated-types/issue-19129-1.rs
+ui/associated-types/issue-19129-2.rs
 ui/associated-types/issue-19883.rs
 ui/associated-types/issue-20005.rs
+ui/associated-types/issue-20763-1.rs
+ui/associated-types/issue-20763-2.rs
 ui/associated-types/issue-20825-2.rs
 ui/associated-types/issue-20825.rs
 ui/associated-types/issue-21363.rs
@@ -281,6 +285,8 @@
 ui/auxiliary/issue-24106.rs
 ui/auxiliary/issue-76387.rs
 ui/bench/issue-32062.rs
+ui/binding/issue-40402-1.rs
+ui/binding/issue-40402-2.rs
 ui/binding/issue-53114-borrow-checks.rs
 ui/binding/issue-53114-safety-checks.rs
 ui/binop/issue-25916.rs
@@ -431,12 +437,16 @@
 ui/closures/issue-113087.rs
 ui/closures/issue-11873.rs
 ui/closures/issue-1460.rs
+ui/closures/issue-22864-1.rs
+ui/closures/issue-22864-2.rs
 ui/closures/issue-23012-supertrait-signature-inference.rs
 ui/closures/issue-25439.rs
 ui/closures/issue-41366.rs
 ui/closures/issue-42463.rs
 ui/closures/issue-46742.rs
 ui/closures/issue-48109.rs
+ui/closures/issue-5239-1.rs
+ui/closures/issue-5239-2.rs
 ui/closures/issue-52437.rs
 ui/closures/issue-67123.rs
 ui/closures/issue-6801.rs
@@ -482,6 +492,8 @@
 ui/coercion/issue-14589.rs
 ui/coercion/issue-26905-rpass.rs
 ui/coercion/issue-26905.rs
+ui/coercion/issue-32122-1.rs
+ui/coercion/issue-32122-2.rs
 ui/coercion/issue-36007.rs
 ui/coercion/issue-37655.rs
 ui/coercion/issue-3794.rs
@@ -721,6 +733,8 @@
 ui/consts/issue-17718.rs
 ui/consts/issue-17756.rs
 ui/consts/issue-18294.rs
+ui/consts/issue-19244-1.rs
+ui/consts/issue-19244-2.rs
 ui/consts/issue-19244.rs
 ui/consts/issue-21562.rs
 ui/consts/issue-21721.rs
@@ -845,6 +859,8 @@
 ui/debuginfo/issue-105386-debuginfo-ub.rs
 ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.rs
 ui/deprecation/issue-84637-deprecated-associated-function.rs
+ui/deref-patterns/issue-71676-1.rs
+ui/deref-patterns/issue-71676-2.rs
 ui/derived-errors/issue-30580.rs
 ui/derived-errors/issue-31997-1.rs
 ui/derived-errors/issue-31997.rs
@@ -952,7 +968,12 @@
 ui/enum-discriminant/issue-70509-partial_eq.rs
 ui/enum-discriminant/issue-72554.rs
 ui/enum-discriminant/issue-90038.rs
+ui/enum/auxiliary/issue-19340-1.rs
 ui/enum/issue-1821.rs
+ui/enum/issue-19340-1.rs
+ui/enum/issue-19340-2.rs
+ui/enum/issue-23304-1.rs
+ui/enum/issue-23304-2.rs
 ui/enum/issue-42747.rs
 ui/enum/issue-67945-1.rs
 ui/enum/issue-67945-2.rs
@@ -965,6 +986,8 @@
 ui/errors/issue-89280-emitter-overflow-splice-lines.rs
 ui/errors/issue-99572-impl-trait-on-pointer.rs
 ui/expr/if/issue-4201.rs
+ui/expr/issue-22933-1.rs
+ui/expr/issue-22933-2.rs
 ui/extern/auxiliary/issue-80074-macro-2.rs
 ui/extern/auxiliary/issue-80074-macro.rs
 ui/extern/issue-10025.rs
@@ -1400,7 +1423,6 @@
 ui/issues/auxiliary/issue-18913-1.rs
 ui/issues/auxiliary/issue-18913-2.rs
 ui/issues/auxiliary/issue-19293.rs
-ui/issues/auxiliary/issue-19340-1.rs
 ui/issues/auxiliary/issue-20389.rs
 ui/issues/auxiliary/issue-21202.rs
 ui/issues/auxiliary/issue-2170-lib.rs
@@ -1492,8 +1514,6 @@
 ui/issues/issue-11677.rs
 ui/issues/issue-11680.rs
 ui/issues/issue-11681.rs
-ui/issues/issue-11692-1.rs
-ui/issues/issue-11692-2.rs
 ui/issues/issue-11709.rs
 ui/issues/issue-11740.rs
 ui/issues/issue-11771.rs
@@ -1504,8 +1524,6 @@
 ui/issues/issue-12033.rs
 ui/issues/issue-12041.rs
 ui/issues/issue-12127.rs
-ui/issues/issue-12187-1.rs
-ui/issues/issue-12187-2.rs
 ui/issues/issue-12285.rs
 ui/issues/issue-12567.rs
 ui/issues/issue-12612.rs
@@ -1713,14 +1731,8 @@
 ui/issues/issue-19098.rs
 ui/issues/issue-19100.rs
 ui/issues/issue-19127.rs
-ui/issues/issue-19129-1.rs
-ui/issues/issue-19129-2.rs
 ui/issues/issue-19135.rs
-ui/issues/issue-19244-1.rs
-ui/issues/issue-19244-2.rs
 ui/issues/issue-19293.rs
-ui/issues/issue-19340-1.rs
-ui/issues/issue-19340-2.rs
 ui/issues/issue-19367.rs
 ui/issues/issue-19380.rs
 ui/issues/issue-19398.rs
@@ -1761,8 +1773,6 @@
 ui/issues/issue-20676.rs
 ui/issues/issue-20714.rs
 ui/issues/issue-2074.rs
-ui/issues/issue-20763-1.rs
-ui/issues/issue-20763-2.rs
 ui/issues/issue-20772.rs
 ui/issues/issue-20797.rs
 ui/issues/issue-20803.rs
@@ -1835,15 +1845,11 @@
 ui/issues/issue-2281-part1.rs
 ui/issues/issue-22814.rs
 ui/issues/issue-2284.rs
-ui/issues/issue-22864-1.rs
-ui/issues/issue-22864-2.rs
 ui/issues/issue-22872.rs
 ui/issues/issue-22874.rs
 ui/issues/issue-2288.rs
 ui/issues/issue-22886.rs
 ui/issues/issue-22894.rs
-ui/issues/issue-22933-1.rs
-ui/issues/issue-22933-2.rs
 ui/issues/issue-22992-2.rs
 ui/issues/issue-22992.rs
 ui/issues/issue-23024.rs
@@ -1854,8 +1860,6 @@
 ui/issues/issue-2311-2.rs
 ui/issues/issue-2311.rs
 ui/issues/issue-2312.rs
-ui/issues/issue-23122-1.rs
-ui/issues/issue-23122-2.rs
 ui/issues/issue-2316-c.rs
 ui/issues/issue-23173.rs
 ui/issues/issue-23189.rs
@@ -1863,8 +1867,6 @@
 ui/issues/issue-23253.rs
 ui/issues/issue-23261.rs
 ui/issues/issue-23281.rs
-ui/issues/issue-23304-1.rs
-ui/issues/issue-23304-2.rs
 ui/issues/issue-23311.rs
 ui/issues/issue-23336.rs
 ui/issues/issue-23354-2.rs
@@ -2092,9 +2094,6 @@
 ui/issues/issue-32004.rs
 ui/issues/issue-32008.rs
 ui/issues/issue-32086.rs
-ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.rs
-ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.rs
-ui/issues/issue-3214.rs
 ui/issues/issue-3220.rs
 ui/issues/issue-32292.rs
 ui/issues/issue-32324.rs
@@ -2243,8 +2242,6 @@
 ui/issues/issue-40288-2.rs
 ui/issues/issue-40288.rs
 ui/issues/issue-40350.rs
-ui/issues/issue-40402-ref-hints/issue-40402-1.rs
-ui/issues/issue-40402-ref-hints/issue-40402-2.rs
 ui/issues/issue-40408.rs
 ui/issues/issue-40610.rs
 ui/issues/issue-40749.rs
@@ -2438,8 +2435,6 @@
 ui/issues/issue-52049.rs
 ui/issues/issue-52126-assign-op-invariance.rs
 ui/issues/issue-52262.rs
-ui/issues/issue-5239-1.rs
-ui/issues/issue-5239-2.rs
 ui/issues/issue-52489.rs
 ui/issues/issue-52533.rs
 ui/issues/issue-52717.rs
@@ -2491,8 +2486,6 @@
 ui/issues/issue-5718.rs
 ui/issues/issue-57198-pass.rs
 ui/issues/issue-57271.rs
-ui/issues/issue-57362-1.rs
-ui/issues/issue-57362-2.rs
 ui/issues/issue-57399-self-return-impl-trait.rs
 ui/issues/issue-5741.rs
 ui/issues/issue-5754.rs
@@ -2578,8 +2571,6 @@
 ui/issues/issue-70746.rs
 ui/issues/issue-7092.rs
 ui/issues/issue-71406.rs
-ui/issues/issue-71676-suggest-deref/issue-71676-1.rs
-ui/issues/issue-71676-suggest-deref/issue-71676-2.rs
 ui/issues/issue-7178.rs
 ui/issues/issue-72002.rs
 ui/issues/issue-72076.rs
@@ -2600,8 +2591,6 @@
 ui/issues/issue-75704.rs
 ui/issues/issue-7575.rs
 ui/issues/issue-76042.rs
-ui/issues/issue-7607-1.rs
-ui/issues/issue-7607-2.rs
 ui/issues/issue-76077-inaccesible-private-fields/issue-76077-1.rs
 ui/issues/issue-76077-inaccesible-private-fields/issue-76077.rs
 ui/issues/issue-76191.rs
@@ -2851,6 +2840,8 @@
 ui/macros/issue-111749.rs
 ui/macros/issue-112342-1.rs
 ui/macros/issue-112342-2.rs
+ui/macros/issue-11692-1.rs
+ui/macros/issue-11692-2.rs
 ui/macros/issue-118048.rs
 ui/macros/issue-118786.rs
 ui/macros/issue-16098.rs
@@ -3162,6 +3153,8 @@
 ui/nll/issue-57280-1-flipped.rs
 ui/nll/issue-57280-1.rs
 ui/nll/issue-57280.rs
+ui/nll/issue-57362-1.rs
+ui/nll/issue-57362-2.rs
 ui/nll/issue-57642-higher-ranked-subtype.rs
 ui/nll/issue-57843.rs
 ui/nll/issue-57960.rs
@@ -3218,6 +3211,8 @@
 ui/packed/issue-46152.rs
 ui/panics/issue-47429-short-backtraces.rs
 ui/parser/issue-116781.rs
+ui/parser/issue-12187-1.rs
+ui/parser/issue-12187-2.rs
 ui/parser/issues/auxiliary/issue-21146-inc.rs
 ui/parser/issues/auxiliary/issue-89971-outer-attr-following-inner-attr-ice.rs
 ui/parser/issues/auxiliary/issue-94340-inc.rs
@@ -3613,6 +3608,8 @@
 ui/reachable/issue-11225-2.rs
 ui/reachable/issue-11225-3.rs
 ui/reachable/issue-948.rs
+ui/recursion/issue-23122-1.rs
+ui/recursion/issue-23122-2.rs
 ui/recursion/issue-23302-1.rs
 ui/recursion/issue-23302-2.rs
 ui/recursion/issue-23302-3.rs
@@ -3692,6 +3689,7 @@
 ui/resolve/issue-3099-a.rs
 ui/resolve/issue-3099-b.rs
 ui/resolve/issue-31845.rs
+ui/resolve/issue-3214.rs
 ui/resolve/issue-33876.rs
 ui/resolve/issue-35675.rs
 ui/resolve/issue-3907-2.rs
@@ -4203,6 +4201,8 @@
 ui/type/issue-103271.rs
 ui/type/issue-58355.rs
 ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs
+ui/type/issue-7607-1.rs
+ui/type/issue-7607-2.rs
 ui/type/issue-91268.rs
 ui/type/issue-94187-verbose-type-name.rs
 ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 28d70b4..a1445ce 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -19,8 +19,12 @@
 
 use crate::walk::{filter_dirs, walk};
 use regex::{Regex, RegexSet};
+use rustc_hash::FxHashMap;
 use std::{ffi::OsStr, path::Path};
 
+#[cfg(test)]
+mod tests;
+
 /// Error code markdown is restricted to 80 columns because they can be
 /// displayed on the console with --example.
 const ERROR_CODE_COLS: usize = 80;
@@ -65,12 +69,56 @@
     "//@ normalize-stderr-test",
 ];
 
+fn generate_problems<'a>(
+    consts: &'a [u32],
+    letter_digit: &'a FxHashMap<char, char>,
+) -> impl Iterator<Item = u32> + 'a {
+    consts.iter().flat_map(move |const_value| {
+        let problem =
+            letter_digit.iter().fold(format!("{:X}", const_value), |acc, (key, value)| {
+                acc.replace(&value.to_string(), &key.to_string())
+            });
+        let indexes: Vec<usize> = problem
+            .chars()
+            .enumerate()
+            .filter_map(|(index, c)| if letter_digit.contains_key(&c) { Some(index) } else { None })
+            .collect();
+        (0..1 << indexes.len()).map(move |i| {
+            u32::from_str_radix(
+                &problem
+                    .chars()
+                    .enumerate()
+                    .map(|(index, c)| {
+                        if let Some(pos) = indexes.iter().position(|&x| x == index) {
+                            if (i >> pos) & 1 == 1 { letter_digit[&c] } else { c }
+                        } else {
+                            c
+                        }
+                    })
+                    .collect::<String>(),
+                0x10,
+            )
+            .unwrap()
+        })
+    })
+}
+
 // Intentionally written in decimal rather than hex
-const PROBLEMATIC_CONSTS: &[u32] = &[
+const ROOT_PROBLEMATIC_CONSTS: &[u32] = &[
     184594741, 2880289470, 2881141438, 2965027518, 2976579765, 3203381950, 3405691582, 3405697037,
-    3735927486, 3735932941, 4027431614, 4276992702,
+    3735927486, 3735932941, 4027431614, 4276992702, 195934910, 252707358, 762133, 179681982,
+    173390526, 721077,
 ];
 
+fn generate_problematic_strings(
+    consts: &[u32],
+    letter_digit: &FxHashMap<char, char>,
+) -> Vec<String> {
+    generate_problems(consts, letter_digit)
+        .flat_map(|v| vec![v.to_string(), format!("{:x}", v), format!("{:X}", v)])
+        .collect()
+}
+
 const INTERNAL_COMPILER_DOCS_LINE: &str = "#### This error code is internal to the compiler and will not be emitted with normal Rust code.";
 
 /// Parser states for `line_is_url`.
@@ -267,11 +315,10 @@
         // We only check CSS files in rustdoc.
         path.extension().map_or(false, |e| e == "css") && !is_in(path, "src", "librustdoc")
     }
-
-    let problematic_consts_strings: Vec<String> = (PROBLEMATIC_CONSTS.iter().map(u32::to_string))
-        .chain(PROBLEMATIC_CONSTS.iter().map(|v| format!("{:x}", v)))
-        .chain(PROBLEMATIC_CONSTS.iter().map(|v| format!("{:X}", v)))
-        .collect();
+    let problematic_consts_strings = generate_problematic_strings(
+        ROOT_PROBLEMATIC_CONSTS,
+        &[('A', '4'), ('B', '8'), ('E', '3')].iter().cloned().collect(),
+    );
     let problematic_regex = RegexSet::new(problematic_consts_strings.as_slice()).unwrap();
 
     walk(path, skip, &mut |entry, contents| {
diff --git a/src/tools/tidy/src/style/tests.rs b/src/tools/tidy/src/style/tests.rs
new file mode 100644
index 0000000..292e239
--- /dev/null
+++ b/src/tools/tidy/src/style/tests.rs
@@ -0,0 +1,17 @@
+use super::*;
+
+#[test]
+fn test_generate_problematic_strings() {
+    let problematic_regex = RegexSet::new(
+        generate_problematic_strings(
+            ROOT_PROBLEMATIC_CONSTS,
+            &[('A', '4'), ('B', '8'), ('E', '3'), ('0', 'F')].iter().cloned().collect(), // use "futile" F intentionally
+        )
+        .as_slice(),
+    )
+    .unwrap();
+    assert!(problematic_regex.is_match("786357")); // check with no "decimal" hex digits - converted to integer
+    assert!(problematic_regex.is_match("589701")); // check with "decimal" replacements - converted to integer
+    assert!(problematic_regex.is_match("8FF85")); // check for hex display
+    assert!(!problematic_regex.is_match("1193046")); // check for non-matching value
+}
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 7136bc4..f9985a7 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -17,7 +17,7 @@
 const ENTRY_LIMIT: usize = 900;
 // FIXME: The following limits should be reduced eventually.
 
-const ISSUES_ENTRY_LIMIT: usize = 1720;
+const ISSUES_ENTRY_LIMIT: usize = 1676;
 const ROOT_ENTRY_LIMIT: usize = 859;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs
index 7547b49..ef5cea1 100644
--- a/src/tools/unicode-table-generator/src/raw_emitter.rs
+++ b/src/tools/unicode-table-generator/src/raw_emitter.rs
@@ -23,6 +23,7 @@
     }
 
     fn emit_bitset(&mut self, ranges: &[Range<u32>]) -> Result<(), String> {
+        let first_code_point = ranges.first().unwrap().start;
         let last_code_point = ranges.last().unwrap().end;
         // bitset for every bit in the codepoint range
         //
@@ -101,7 +102,10 @@
         )
         .unwrap();
         writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap();
-        writeln!(&mut self.file, "    super::bitset_search(",).unwrap();
+        if first_code_point > 0x7f {
+            writeln!(&mut self.file, "    (c as u32) >= {first_code_point:#04x} &&").unwrap();
+        }
+        writeln!(&mut self.file, "    super::bitset_search(").unwrap();
         writeln!(&mut self.file, "        c as u32,").unwrap();
         writeln!(&mut self.file, "        &BITSET_CHUNKS_MAP,").unwrap();
         writeln!(&mut self.file, "        &BITSET_INDEX_CHUNKS,").unwrap();
diff --git a/src/tools/unicode-table-generator/src/skiplist.rs b/src/tools/unicode-table-generator/src/skiplist.rs
index 9b613a9..8fae828 100644
--- a/src/tools/unicode-table-generator/src/skiplist.rs
+++ b/src/tools/unicode-table-generator/src/skiplist.rs
@@ -25,8 +25,9 @@
 
 impl RawEmitter {
     pub fn emit_skiplist(&mut self, ranges: &[Range<u32>]) {
+        let first_code_point = ranges.first().unwrap().start;
         let mut offsets = Vec::<u32>::new();
-        let points = ranges.iter().flat_map(|r| vec![r.start, r.end]).collect::<Vec<u32>>();
+        let points = ranges.iter().flat_map(|r| [r.start, r.end]).collect::<Vec<u32>>();
         let mut offset = 0;
         for pt in points {
             let delta = pt - offset;
@@ -86,7 +87,26 @@
         .unwrap();
         self.bytes_used += coded_offsets.len();
 
-        writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap();
+        // The inlining in this code works like the following:
+        //
+        // The `skip_search` function is always inlined into the parent `lookup` fn,
+        // thus the compiler can generate optimal code based on the referenced `static`s.
+        //
+        // In the case of ASCII optimization, the lower-bounds check is inlined into
+        // the caller, and slower-path `skip_search` is outlined into a separate `lookup_slow` fn.
+        //
+        // Thus, in both cases, the `skip_search` function is specialized for the `static`s,
+        // and outlined into the prebuilt `std`.
+        if first_code_point > 0x7f {
+            writeln!(&mut self.file, "#[inline]").unwrap();
+            writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap();
+            writeln!(&mut self.file, "    (c as u32) >= {first_code_point:#04x} && lookup_slow(c)")
+                .unwrap();
+            writeln!(&mut self.file, "}}").unwrap();
+            writeln!(&mut self.file, "fn lookup_slow(c: char) -> bool {{").unwrap();
+        } else {
+            writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap();
+        }
         writeln!(&mut self.file, "    super::skip_search(",).unwrap();
         writeln!(&mut self.file, "        c as u32,").unwrap();
         writeln!(&mut self.file, "        &SHORT_OFFSET_RUNS,").unwrap();
diff --git a/tests/codegen/array-equality.rs b/tests/codegen/array-equality.rs
index 5b85da1..bc5425c 100644
--- a/tests/codegen/array-equality.rs
+++ b/tests/codegen/array-equality.rs
@@ -1,7 +1,6 @@
 //@ compile-flags: -O -Z merge-functions=disabled
 //@ only-x86_64
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 
 // CHECK-LABEL: @array_eq_value
 #[no_mangle]
diff --git a/tests/codegen/checked_math.rs b/tests/codegen/checked_math.rs
new file mode 100644
index 0000000..41016e3
--- /dev/null
+++ b/tests/codegen/checked_math.rs
@@ -0,0 +1,86 @@
+//@ compile-flags: -O -Z merge-functions=disabled
+
+#![crate_type = "lib"]
+#![feature(unchecked_shifts)]
+
+// Because the result of something like `u32::checked_sub` can only be used if it
+// didn't overflow, make sure that LLVM actually knows that in optimized builds.
+// Thanks to poison semantics, this doesn't even need branches.
+
+// CHECK-LABEL: @checked_sub_unsigned
+// CHECK-SAME: (i16 noundef %a, i16 noundef %b)
+#[no_mangle]
+pub fn checked_sub_unsigned(a: u16, b: u16) -> Option<u16> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp uge i16 %a, %b
+    // CHECK-DAG: %[[DIFF_P:.+]] = sub nuw i16 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i16
+    // CHECK-DAG: %[[DIFF_U:.+]] = select i1 %[[IS_SOME]], i16 %[[DIFF_P]], i16 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i16, i16 } poison, i16 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i16, i16 } %[[R0]], i16 %[[DIFF_U]], 1
+    // CHECK: ret { i16, i16 } %[[R1]]
+    a.checked_sub(b)
+}
+
+// Note that `shl` and `shr` in LLVM are already unchecked. So rather than
+// looking for no-wrap flags, we just need there to not be any masking.
+
+// CHECK-LABEL: @checked_shl_unsigned
+// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+#[no_mangle]
+pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
+    // CHECK-DAG: %[[SHIFTED_P:.+]] = shl i32 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32
+    // CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1
+    // CHECK: ret { i32, i32 } %[[R1]]
+    a.checked_shl(b)
+}
+
+// CHECK-LABEL: @checked_shr_unsigned
+// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+#[no_mangle]
+pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
+    // CHECK-DAG: %[[SHIFTED_P:.+]] = lshr i32 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32
+    // CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1
+    // CHECK: ret { i32, i32 } %[[R1]]
+    a.checked_shr(b)
+}
+
+// CHECK-LABEL: @checked_shl_signed
+// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+#[no_mangle]
+pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
+    // CHECK-DAG: %[[SHIFTED_P:.+]] = shl i32 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32
+    // CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1
+    // CHECK: ret { i32, i32 } %[[R1]]
+    a.checked_shl(b)
+}
+
+// CHECK-LABEL: @checked_shr_signed
+// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+#[no_mangle]
+pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
+    // CHECK-DAG: %[[SHIFTED_P:.+]] = ashr i32 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32
+    // CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1
+    // CHECK: ret { i32, i32 } %[[R1]]
+    a.checked_shr(b)
+}
diff --git a/tests/codegen/enum/enum-debug-niche-2.rs b/tests/codegen/enum/enum-debug-niche-2.rs
index 2587188..58f43fe 100644
--- a/tests/codegen/enum/enum-debug-niche-2.rs
+++ b/tests/codegen/enum/enum-debug-niche-2.rs
@@ -7,7 +7,7 @@
 // CHECK: {{.*}}DICompositeType{{.*}}tag: DW_TAG_variant_part,{{.*}}size: 32,{{.*}}
 // CHECK: {{.*}}DIDerivedType{{.*}}tag: DW_TAG_member,{{.*}}name: "Placeholder",{{.*}}extraData: i128 4294967295{{[,)].*}}
 // CHECK: {{.*}}DIDerivedType{{.*}}tag: DW_TAG_member,{{.*}}name: "Error",{{.*}}extraData: i128 0{{[,)].*}}
-#![feature(generic_nonzero, never_type)]
+#![feature(never_type)]
 
 #[derive(Copy, Clone)]
 pub struct Entity {
diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs
index 468ec0a..2b27dab 100644
--- a/tests/codegen/function-arguments.rs
+++ b/tests/codegen/function-arguments.rs
@@ -1,7 +1,6 @@
 //@ compile-flags: -O -C no-prepopulate-passes
 #![crate_type = "lib"]
 #![feature(dyn_star)]
-#![feature(generic_nonzero)]
 #![feature(allocator_api)]
 
 use std::mem::MaybeUninit;
diff --git a/tests/codegen/intrinsics/transmute-niched.rs b/tests/codegen/intrinsics/transmute-niched.rs
index b5e0da1..f5b7bd2 100644
--- a/tests/codegen/intrinsics/transmute-niched.rs
+++ b/tests/codegen/intrinsics/transmute-niched.rs
@@ -3,7 +3,6 @@
 //@ [DBG] compile-flags: -C opt-level=0 -C no-prepopulate-passes
 //@ only-64bit (so I don't need to worry about usize)
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 
 use std::mem::transmute;
 use std::num::NonZero;
diff --git a/tests/codegen/issues/issue-119422.rs b/tests/codegen/issues/issue-119422.rs
index 19480b4..aa56bfe 100644
--- a/tests/codegen/issues/issue-119422.rs
+++ b/tests/codegen/issues/issue-119422.rs
@@ -4,7 +4,6 @@
 //@ compile-flags: -O --edition=2021 -Zmerge-functions=disabled
 //@ only-64bit (because the LLVM type of i64 for usize shows up)
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 
 use core::ptr::NonNull;
 use core::num::NonZero;
diff --git a/tests/codegen/loads.rs b/tests/codegen/loads.rs
index ba4de77..e3e2f75 100644
--- a/tests/codegen/loads.rs
+++ b/tests/codegen/loads.rs
@@ -1,7 +1,6 @@
 //@ compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0 -O
 
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 
 use std::mem::MaybeUninit;
 use std::num::NonZero;
diff --git a/tests/codegen/mem-replace-simple-type.rs b/tests/codegen/mem-replace-simple-type.rs
index 50b43f5..7209fa2 100644
--- a/tests/codegen/mem-replace-simple-type.rs
+++ b/tests/codegen/mem-replace-simple-type.rs
@@ -34,18 +34,20 @@
 
 #[no_mangle]
 // CHECK-LABEL: @replace_short_array_3(
+// CHECK-SAME: ptr{{.+}}sret{{.+}}%[[RET:.+]], ptr{{.+}}%r, ptr{{.+}}%v
 pub fn replace_short_array_3(r: &mut [u32; 3], v: [u32; 3]) -> [u32; 3] {
     // CHECK-NOT: alloca
-    // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %result, ptr align 4 %r, i64 12, i1 false)
+    // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[RET]], ptr align 4 %r, i64 12, i1 false)
     // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %r, ptr align 4 %v, i64 12, i1 false)
     std::mem::replace(r, v)
 }
 
 #[no_mangle]
 // CHECK-LABEL: @replace_short_array_4(
+// CHECK-SAME: ptr{{.+}}sret{{.+}}%[[RET:.+]], ptr{{.+}}%r, ptr{{.+}}%v
 pub fn replace_short_array_4(r: &mut [u32; 4], v: [u32; 4]) -> [u32; 4] {
     // CHECK-NOT: alloca
-    // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %result, ptr align 4 %r, i64 16, i1 false)
+    // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[RET]], ptr align 4 %r, i64 16, i1 false)
     // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %r, ptr align 4 %v, i64 16, i1 false)
     std::mem::replace(r, v)
 }
diff --git a/tests/codegen/option-as-slice.rs b/tests/codegen/option-as-slice.rs
index c5b1eaf..65637a2 100644
--- a/tests/codegen/option-as-slice.rs
+++ b/tests/codegen/option-as-slice.rs
@@ -1,7 +1,6 @@
 //@ compile-flags: -O -Z randomize-layout=no
 //@ only-x86_64
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 
 extern crate core;
 
diff --git a/tests/codegen/option-niche-eq.rs b/tests/codegen/option-niche-eq.rs
index 8b8044e..7b95533 100644
--- a/tests/codegen/option-niche-eq.rs
+++ b/tests/codegen/option-niche-eq.rs
@@ -1,7 +1,6 @@
 //@ compile-flags: -O -Zmerge-functions=disabled
 //@ min-llvm-version: 18
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 
 extern crate core;
 use core::cmp::Ordering;
diff --git a/tests/codegen/slice-ref-equality.rs b/tests/codegen/slice-ref-equality.rs
index 85d9c34..1153d78 100644
--- a/tests/codegen/slice-ref-equality.rs
+++ b/tests/codegen/slice-ref-equality.rs
@@ -1,6 +1,5 @@
 //@ compile-flags: -O -Zmerge-functions=disabled
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 
 use std::num::NonZero;
 
@@ -43,48 +42,48 @@
 // equality for non-byte types also just emit a `bcmp`, not a loop.
 
 // CHECK-LABEL: @eq_slice_of_nested_u8(
-// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
-// CHECK-SAME: [[USIZE]] noundef %3
+// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
+// CHECK-SAME: [[USIZE]] noundef %y.1
 #[no_mangle]
 fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool {
-    // CHECK: icmp eq [[USIZE]] %1, %3
-    // CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] %1, 3
+    // CHECK: icmp eq [[USIZE]] %x.1, %y.1
+    // CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] {{%x.1|%y.1}}, 3
     // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
     // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
     x == y
 }
 
 // CHECK-LABEL: @eq_slice_of_i32(
-// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
-// CHECK-SAME: [[USIZE]] noundef %3
+// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
+// CHECK-SAME: [[USIZE]] noundef %y.1
 #[no_mangle]
 fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool {
-    // CHECK: icmp eq [[USIZE]] %1, %3
-    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2
+    // CHECK: icmp eq [[USIZE]] %x.1, %y.1
+    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] {{%x.1|%y.1}}, 2
     // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
     // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
     x == y
 }
 
 // CHECK-LABEL: @eq_slice_of_nonzero(
-// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
-// CHECK-SAME: [[USIZE]] noundef %3
+// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
+// CHECK-SAME: [[USIZE]] noundef %y.1
 #[no_mangle]
 fn eq_slice_of_nonzero(x: &[NonZero<i32>], y: &[NonZero<i32>]) -> bool {
-    // CHECK: icmp eq [[USIZE]] %1, %3
-    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2
+    // CHECK: icmp eq [[USIZE]] %x.1, %y.1
+    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] {{%x.1|%y.1}}, 2
     // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
     // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
     x == y
 }
 
 // CHECK-LABEL: @eq_slice_of_option_of_nonzero(
-// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
-// CHECK-SAME: [[USIZE]] noundef %3
+// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
+// CHECK-SAME: [[USIZE]] noundef %y.1
 #[no_mangle]
 fn eq_slice_of_option_of_nonzero(x: &[Option<NonZero<i16>>], y: &[Option<NonZero<i16>>]) -> bool {
-    // CHECK: icmp eq [[USIZE]] %1, %3
-    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 1
+    // CHECK: icmp eq [[USIZE]] %x.1, %y.1
+    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] {{%x.1|%y.1}}, 1
     // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
     // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
     x == y
diff --git a/tests/codegen/transmute-optimized.rs b/tests/codegen/transmute-optimized.rs
index 1a5f53e..8e5bcb2 100644
--- a/tests/codegen/transmute-optimized.rs
+++ b/tests/codegen/transmute-optimized.rs
@@ -1,6 +1,5 @@
 //@ compile-flags: -O -Z merge-functions=disabled
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 
 // This tests that LLVM can optimize based on the niches in the source or
 // destination types for transmutes.
diff --git a/tests/codegen/vec-len-invariant.rs b/tests/codegen/vec-len-invariant.rs
new file mode 100644
index 0000000..780c86b
--- /dev/null
+++ b/tests/codegen/vec-len-invariant.rs
@@ -0,0 +1,16 @@
+//@ compile-flags: -O
+//@ only-64bit
+//
+// This test confirms that we do not reload the length of a Vec after growing it in push.
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @should_load_once
+#[no_mangle]
+pub fn should_load_once(v: &mut Vec<u8>) {
+    // CHECK: load i64
+    // CHECK: call {{.*}}grow_one
+    // CHECK-NOT: load i64
+    // CHECK: add {{.*}}, 1
+    v.push(1);
+}
diff --git a/tests/coverage/branch_generics.cov-map b/tests/coverage/branch/generics.cov-map
similarity index 93%
rename from tests/coverage/branch_generics.cov-map
rename to tests/coverage/branch/generics.cov-map
index 719e97e..d729b0c 100644
--- a/tests/coverage/branch_generics.cov-map
+++ b/tests/coverage/branch/generics.cov-map
@@ -1,4 +1,4 @@
-Function name: branch_generics::print_size::<()>
+Function name: generics::print_size::<()>
 Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
@@ -16,7 +16,7 @@
 - Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2)
     = (c1 + (c0 - c1))
 
-Function name: branch_generics::print_size::<u32>
+Function name: generics::print_size::<u32>
 Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
@@ -34,7 +34,7 @@
 - Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2)
     = (c1 + (c0 - c1))
 
-Function name: branch_generics::print_size::<u64>
+Function name: generics::print_size::<u64>
 Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
diff --git a/tests/coverage/branch_generics.coverage b/tests/coverage/branch/generics.coverage
similarity index 93%
rename from tests/coverage/branch_generics.coverage
rename to tests/coverage/branch/generics.coverage
index e7cec15..85f73d4 100644
--- a/tests/coverage/branch_generics.coverage
+++ b/tests/coverage/branch/generics.coverage
@@ -16,7 +16,7 @@
    LL|      2|    }
    LL|      3|}
   ------------------
-  | branch_generics::print_size::<()>:
+  | generics::print_size::<()>:
   |   LL|      1|fn print_size<T>() {
   |   LL|      1|    if std::mem::size_of::<T>() > 4 {
   |  ------------------
@@ -28,7 +28,7 @@
   |   LL|      1|    }
   |   LL|      1|}
   ------------------
-  | branch_generics::print_size::<u32>:
+  | generics::print_size::<u32>:
   |   LL|      1|fn print_size<T>() {
   |   LL|      1|    if std::mem::size_of::<T>() > 4 {
   |  ------------------
@@ -40,7 +40,7 @@
   |   LL|      1|    }
   |   LL|      1|}
   ------------------
-  | branch_generics::print_size::<u64>:
+  | generics::print_size::<u64>:
   |   LL|      1|fn print_size<T>() {
   |   LL|      1|    if std::mem::size_of::<T>() > 4 {
   |  ------------------
diff --git a/tests/coverage/branch_generics.rs b/tests/coverage/branch/generics.rs
similarity index 100%
rename from tests/coverage/branch_generics.rs
rename to tests/coverage/branch/generics.rs
diff --git a/tests/coverage/branch_guard.cov-map b/tests/coverage/branch/guard.cov-map
similarity index 96%
rename from tests/coverage/branch_guard.cov-map
rename to tests/coverage/branch/guard.cov-map
index 0b3622f..d67c3d3 100644
--- a/tests/coverage/branch_guard.cov-map
+++ b/tests/coverage/branch/guard.cov-map
@@ -1,4 +1,4 @@
-Function name: branch_guard::branch_match_guard
+Function name: guard::branch_match_guard
 Raw bytes (85): 0x[01, 01, 06, 19, 0d, 05, 09, 0f, 15, 13, 11, 17, 0d, 05, 09, 0d, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 15, 01, 14, 02, 0a, 0d, 03, 0e, 00, 0f, 19, 00, 14, 00, 19, 20, 0d, 02, 00, 14, 00, 1e, 0d, 00, 1d, 02, 0a, 11, 03, 0e, 00, 0f, 1d, 00, 14, 00, 19, 20, 11, 09, 00, 14, 00, 1e, 11, 00, 1d, 02, 0a, 17, 03, 0e, 02, 0a, 0b, 04, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
diff --git a/tests/coverage/branch_guard.coverage b/tests/coverage/branch/guard.coverage
similarity index 100%
rename from tests/coverage/branch_guard.coverage
rename to tests/coverage/branch/guard.coverage
diff --git a/tests/coverage/branch_guard.rs b/tests/coverage/branch/guard.rs
similarity index 100%
rename from tests/coverage/branch_guard.rs
rename to tests/coverage/branch/guard.rs
diff --git a/tests/coverage/branch/if-let.cov-map b/tests/coverage/branch/if-let.cov-map
new file mode 100644
index 0000000..c12df8d
--- /dev/null
+++ b/tests/coverage/branch/if-let.cov-map
@@ -0,0 +1,41 @@
+Function name: if_let::if_let
+Raw bytes (38): 0x[01, 01, 02, 05, 09, 09, 02, 06, 01, 0c, 01, 01, 10, 02, 03, 11, 00, 12, 05, 00, 16, 00, 1b, 02, 00, 1c, 02, 06, 09, 02, 0c, 02, 06, 07, 03, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 2
+- expression 0 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+Number of file 0 mappings: 6
+- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
+- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 18)
+    = (c1 - c2)
+- Code(Counter(1)) at (prev + 0, 22) to (start + 0, 27)
+- Code(Expression(0, Sub)) at (prev + 0, 28) to (start + 2, 6)
+    = (c1 - c2)
+- Code(Counter(2)) at (prev + 2, 12) to (start + 2, 6)
+- Code(Expression(1, Add)) at (prev + 3, 5) to (start + 1, 2)
+    = (c2 + (c1 - c2))
+
+Function name: if_let::if_let_chain
+Raw bytes (52): 0x[01, 01, 04, 01, 05, 05, 09, 0f, 0d, 05, 09, 08, 01, 17, 01, 00, 33, 02, 01, 11, 00, 12, 01, 00, 16, 00, 17, 0d, 01, 15, 00, 16, 02, 00, 1a, 00, 1b, 0d, 01, 05, 03, 06, 0f, 03, 0c, 02, 06, 0b, 03, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 4
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3)
+- expression 3 operands: lhs = Counter(1), rhs = Counter(2)
+Number of file 0 mappings: 8
+- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 51)
+- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 18)
+    = (c0 - c1)
+- Code(Counter(0)) at (prev + 0, 22) to (start + 0, 23)
+- Code(Counter(3)) at (prev + 1, 21) to (start + 0, 22)
+- Code(Expression(0, Sub)) at (prev + 0, 26) to (start + 0, 27)
+    = (c0 - c1)
+- Code(Counter(3)) at (prev + 1, 5) to (start + 3, 6)
+- Code(Expression(3, Add)) at (prev + 3, 12) to (start + 2, 6)
+    = (c1 + c2)
+- Code(Expression(2, Add)) at (prev + 3, 5) to (start + 1, 2)
+    = ((c1 + c2) + c3)
+
diff --git a/tests/coverage/branch/if-let.coverage b/tests/coverage/branch/if-let.coverage
new file mode 100644
index 0000000..f30c5d3
--- /dev/null
+++ b/tests/coverage/branch/if-let.coverage
@@ -0,0 +1,62 @@
+   LL|       |#![feature(coverage_attribute, let_chains)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ compile-flags: -Zcoverage-options=branch
+   LL|       |//@ llvm-cov-flags: --show-branches=count
+   LL|       |
+   LL|       |macro_rules! no_merge {
+   LL|       |    () => {
+   LL|       |        for _ in 0..1 {}
+   LL|       |    };
+   LL|       |}
+   LL|       |
+   LL|      3|fn if_let(input: Option<&str>) {
+   LL|      3|    no_merge!();
+   LL|       |
+   LL|      3|    if let Some(x) = input {
+                              ^2
+   LL|      2|        say(x);
+   LL|      2|    } else {
+   LL|      1|        say("none");
+   LL|      1|    }
+   LL|      3|    say("done");
+   LL|      3|}
+   LL|       |
+   LL|     15|fn if_let_chain(a: Option<&str>, b: Option<&str>) {
+   LL|     15|    if let Some(x) = a
+                              ^12
+   LL|     12|        && let Some(y) = b
+                                  ^8
+   LL|      8|    {
+   LL|      8|        say(x);
+   LL|      8|        say(y);
+   LL|      8|    } else {
+   LL|      7|        say("not both");
+   LL|      7|    }
+   LL|     15|    say("done");
+   LL|     15|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn say(message: &str) {
+   LL|       |    core::hint::black_box(message);
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    if_let(Some("x"));
+   LL|       |    if_let(Some("x"));
+   LL|       |    if_let(None);
+   LL|       |
+   LL|       |    for _ in 0..8 {
+   LL|       |        if_let_chain(Some("a"), Some("b"));
+   LL|       |    }
+   LL|       |    for _ in 0..4 {
+   LL|       |        if_let_chain(Some("a"), None);
+   LL|       |    }
+   LL|       |    for _ in 0..2 {
+   LL|       |        if_let_chain(None, Some("b"));
+   LL|       |    }
+   LL|       |    if_let_chain(None, None);
+   LL|       |}
+   LL|       |
+   LL|       |// FIXME(#124118) Actually instrument if-let and let-chains for branch coverage.
+
diff --git a/tests/coverage/branch/if-let.rs b/tests/coverage/branch/if-let.rs
new file mode 100644
index 0000000..13db00a
--- /dev/null
+++ b/tests/coverage/branch/if-let.rs
@@ -0,0 +1,58 @@
+#![feature(coverage_attribute, let_chains)]
+//@ edition: 2021
+//@ compile-flags: -Zcoverage-options=branch
+//@ llvm-cov-flags: --show-branches=count
+
+macro_rules! no_merge {
+    () => {
+        for _ in 0..1 {}
+    };
+}
+
+fn if_let(input: Option<&str>) {
+    no_merge!();
+
+    if let Some(x) = input {
+        say(x);
+    } else {
+        say("none");
+    }
+    say("done");
+}
+
+fn if_let_chain(a: Option<&str>, b: Option<&str>) {
+    if let Some(x) = a
+        && let Some(y) = b
+    {
+        say(x);
+        say(y);
+    } else {
+        say("not both");
+    }
+    say("done");
+}
+
+#[coverage(off)]
+fn say(message: &str) {
+    core::hint::black_box(message);
+}
+
+#[coverage(off)]
+fn main() {
+    if_let(Some("x"));
+    if_let(Some("x"));
+    if_let(None);
+
+    for _ in 0..8 {
+        if_let_chain(Some("a"), Some("b"));
+    }
+    for _ in 0..4 {
+        if_let_chain(Some("a"), None);
+    }
+    for _ in 0..2 {
+        if_let_chain(None, Some("b"));
+    }
+    if_let_chain(None, None);
+}
+
+// FIXME(#124118) Actually instrument if-let and let-chains for branch coverage.
diff --git a/tests/coverage/branch_if.cov-map b/tests/coverage/branch/if.cov-map
similarity index 98%
rename from tests/coverage/branch_if.cov-map
rename to tests/coverage/branch/if.cov-map
index 0dbfd92..50f6216 100644
--- a/tests/coverage/branch_if.cov-map
+++ b/tests/coverage/branch/if.cov-map
@@ -1,4 +1,4 @@
-Function name: branch_if::branch_and
+Function name: if::branch_and
 Raw bytes (56): 0x[01, 01, 04, 05, 09, 0d, 02, 11, 0f, 0d, 02, 08, 01, 2b, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 00, 0d, 00, 0e, 20, 11, 0d, 00, 0d, 00, 0e, 11, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
@@ -23,7 +23,7 @@
 - Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2)
     = (c4 + (c3 + (c1 - c2)))
 
-Function name: branch_if::branch_not
+Function name: if::branch_not
 Raw bytes (224): 0x[01, 01, 29, 05, 09, 09, 02, a3, 01, 0d, 09, 02, a3, 01, 0d, 09, 02, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 15, 8e, 01, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, a3, 01, 01, 08, 00, 0a, 20, 9e, 01, 0d, 00, 08, 00, 0a, 9e, 01, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 9b, 01, 01, 08, 00, 0b, 20, 11, 96, 01, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 96, 01, 02, 06, 00, 07, 93, 01, 01, 08, 00, 0c, 20, 8e, 01, 15, 00, 08, 00, 0c, 8e, 01, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 8b, 01, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
@@ -105,7 +105,7 @@
 - Code(Expression(34, Add)) at (prev + 1, 1) to (start + 0, 2)
     = (c5 + ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5))
 
-Function name: branch_if::branch_not_as
+Function name: if::branch_not_as
 Raw bytes (124): 0x[01, 01, 16, 05, 09, 09, 02, 57, 0d, 09, 02, 57, 0d, 09, 02, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 11, 4a, 4f, 11, 0d, 52, 57, 0d, 09, 02, 0e, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 20, 02, 09, 00, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 57, 01, 08, 00, 15, 20, 0d, 52, 00, 08, 00, 15, 0d, 00, 16, 02, 06, 52, 02, 06, 00, 07, 4f, 01, 08, 00, 16, 20, 4a, 11, 00, 08, 00, 16, 4a, 00, 17, 02, 06, 11, 02, 06, 00, 07, 47, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
@@ -160,7 +160,7 @@
 - Code(Expression(17, Add)) at (prev + 1, 1) to (start + 0, 2)
     = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4))
 
-Function name: branch_if::branch_or
+Function name: if::branch_or
 Raw bytes (56): 0x[01, 01, 04, 05, 09, 09, 0d, 0f, 11, 09, 0d, 08, 01, 35, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 20, 0d, 11, 00, 0d, 00, 0e, 0f, 00, 0f, 02, 06, 11, 02, 0c, 02, 06, 0b, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
diff --git a/tests/coverage/branch_if.coverage b/tests/coverage/branch/if.coverage
similarity index 100%
rename from tests/coverage/branch_if.coverage
rename to tests/coverage/branch/if.coverage
diff --git a/tests/coverage/branch_if.rs b/tests/coverage/branch/if.rs
similarity index 100%
rename from tests/coverage/branch_if.rs
rename to tests/coverage/branch/if.rs
diff --git a/tests/coverage/branch/lazy-boolean.cov-map b/tests/coverage/branch/lazy-boolean.cov-map
new file mode 100644
index 0000000..e2d7310
--- /dev/null
+++ b/tests/coverage/branch/lazy-boolean.cov-map
@@ -0,0 +1,177 @@
+Function name: lazy_boolean::branch_and
+Raw bytes (42): 0x[01, 01, 03, 09, 0a, 05, 09, 05, 09, 06, 01, 13, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 0e, 20, 09, 0a, 00, 0d, 00, 0e, 09, 00, 12, 00, 13, 03, 01, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 3
+- expression 0 operands: lhs = Counter(2), rhs = Expression(2, Sub)
+- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 2 operands: lhs = Counter(1), rhs = Counter(2)
+Number of file 0 mappings: 6
+- Code(Counter(0)) at (prev + 19, 1) to (start + 1, 16)
+- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10)
+    = (c2 + (c1 - c2))
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
+- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 13) to (start + 0, 14)
+    true  = c2
+    false = (c1 - c2)
+- Code(Counter(2)) at (prev + 0, 18) to (start + 0, 19)
+- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2)
+    = (c2 + (c1 - c2))
+
+Function name: lazy_boolean::branch_or
+Raw bytes (44): 0x[01, 01, 04, 09, 0e, 05, 09, 05, 09, 05, 09, 06, 01, 1b, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 0e, 20, 09, 0e, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 03, 01, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 4
+- expression 0 operands: lhs = Counter(2), rhs = Expression(3, Sub)
+- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 2 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 3 operands: lhs = Counter(1), rhs = Counter(2)
+Number of file 0 mappings: 6
+- Code(Counter(0)) at (prev + 27, 1) to (start + 1, 16)
+- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10)
+    = (c2 + (c1 - c2))
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
+- Branch { true: Counter(2), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14)
+    true  = c2
+    false = (c1 - c2)
+- Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19)
+    = (c1 - c2)
+- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2)
+    = (c2 + (c1 - c2))
+
+Function name: lazy_boolean::chain
+Raw bytes (149): 0x[01, 01, 13, 11, 07, 0b, 16, 15, 1a, 09, 0d, 05, 09, 05, 09, 09, 0d, 47, 25, 4b, 21, 19, 1d, 03, 19, 03, 19, 3e, 1d, 03, 19, 3e, 1d, 03, 19, 47, 25, 4b, 21, 19, 1d, 13, 01, 24, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 12, 20, 09, 16, 00, 0d, 00, 12, 09, 00, 16, 00, 1b, 20, 0d, 1a, 00, 16, 00, 1b, 0d, 00, 1f, 00, 24, 20, 11, 15, 00, 1f, 00, 24, 11, 00, 28, 00, 2d, 03, 01, 05, 00, 11, 43, 03, 09, 00, 0a, 03, 00, 0d, 00, 12, 20, 19, 3e, 00, 0d, 00, 12, 3e, 00, 16, 00, 1b, 20, 1d, 3a, 00, 16, 00, 1b, 3a, 00, 1f, 00, 24, 20, 21, 25, 00, 1f, 00, 24, 25, 00, 28, 00, 2d, 43, 01, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 19
+- expression 0 operands: lhs = Counter(4), rhs = Expression(1, Add)
+- expression 1 operands: lhs = Expression(2, Add), rhs = Expression(5, Sub)
+- expression 2 operands: lhs = Counter(5), rhs = Expression(6, Sub)
+- expression 3 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 4 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 5 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 6 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 7 operands: lhs = Expression(17, Add), rhs = Counter(9)
+- expression 8 operands: lhs = Expression(18, Add), rhs = Counter(8)
+- expression 9 operands: lhs = Counter(6), rhs = Counter(7)
+- expression 10 operands: lhs = Expression(0, Add), rhs = Counter(6)
+- expression 11 operands: lhs = Expression(0, Add), rhs = Counter(6)
+- expression 12 operands: lhs = Expression(15, Sub), rhs = Counter(7)
+- expression 13 operands: lhs = Expression(0, Add), rhs = Counter(6)
+- expression 14 operands: lhs = Expression(15, Sub), rhs = Counter(7)
+- expression 15 operands: lhs = Expression(0, Add), rhs = Counter(6)
+- expression 16 operands: lhs = Expression(17, Add), rhs = Counter(9)
+- expression 17 operands: lhs = Expression(18, Add), rhs = Counter(8)
+- expression 18 operands: lhs = Counter(6), rhs = Counter(7)
+Number of file 0 mappings: 19
+- Code(Counter(0)) at (prev + 36, 1) to (start + 1, 16)
+- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10)
+    = (c4 + ((c5 + (c2 - c3)) + (c1 - c2)))
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 18)
+- Branch { true: Counter(2), false: Expression(5, Sub) } at (prev + 0, 13) to (start + 0, 18)
+    true  = c2
+    false = (c1 - c2)
+- Code(Counter(2)) at (prev + 0, 22) to (start + 0, 27)
+- Branch { true: Counter(3), false: Expression(6, Sub) } at (prev + 0, 22) to (start + 0, 27)
+    true  = c3
+    false = (c2 - c3)
+- Code(Counter(3)) at (prev + 0, 31) to (start + 0, 36)
+- Branch { true: Counter(4), false: Counter(5) } at (prev + 0, 31) to (start + 0, 36)
+    true  = c4
+    false = c5
+- Code(Counter(4)) at (prev + 0, 40) to (start + 0, 45)
+- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 0, 17)
+    = (c4 + ((c5 + (c2 - c3)) + (c1 - c2)))
+- Code(Expression(16, Add)) at (prev + 3, 9) to (start + 0, 10)
+    = (((c6 + c7) + c8) + c9)
+- Code(Expression(0, Add)) at (prev + 0, 13) to (start + 0, 18)
+    = (c4 + ((c5 + (c2 - c3)) + (c1 - c2)))
+- Branch { true: Counter(6), false: Expression(15, Sub) } at (prev + 0, 13) to (start + 0, 18)
+    true  = c6
+    false = ((c4 + ((c5 + (c2 - c3)) + (c1 - c2))) - c6)
+- Code(Expression(15, Sub)) at (prev + 0, 22) to (start + 0, 27)
+    = ((c4 + ((c5 + (c2 - c3)) + (c1 - c2))) - c6)
+- Branch { true: Counter(7), false: Expression(14, Sub) } at (prev + 0, 22) to (start + 0, 27)
+    true  = c7
+    false = (((c4 + ((c5 + (c2 - c3)) + (c1 - c2))) - c6) - c7)
+- Code(Expression(14, Sub)) at (prev + 0, 31) to (start + 0, 36)
+    = (((c4 + ((c5 + (c2 - c3)) + (c1 - c2))) - c6) - c7)
+- Branch { true: Counter(8), false: Counter(9) } at (prev + 0, 31) to (start + 0, 36)
+    true  = c8
+    false = c9
+- Code(Counter(9)) at (prev + 0, 40) to (start + 0, 45)
+- Code(Expression(16, Add)) at (prev + 1, 5) to (start + 1, 2)
+    = (((c6 + c7) + c8) + c9)
+
+Function name: lazy_boolean::nested_mixed
+Raw bytes (159): 0x[01, 01, 18, 07, 22, 11, 36, 3b, 11, 09, 0d, 26, 0d, 05, 09, 05, 09, 05, 09, 26, 0d, 05, 09, 09, 0d, 3b, 11, 09, 0d, 3b, 11, 09, 0d, 19, 5f, 1d, 21, 03, 15, 15, 19, 52, 56, 15, 19, 03, 15, 19, 5f, 1d, 21, 13, 01, 31, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0e, 00, 13, 20, 09, 26, 00, 0e, 00, 13, 26, 00, 17, 00, 1d, 20, 0d, 22, 00, 17, 00, 1d, 3b, 00, 23, 00, 28, 20, 11, 36, 00, 23, 00, 28, 36, 00, 2c, 00, 33, 03, 01, 05, 00, 11, 5b, 03, 09, 00, 0a, 03, 00, 0e, 00, 13, 20, 15, 56, 00, 0e, 00, 13, 15, 00, 17, 00, 1c, 20, 19, 52, 00, 17, 00, 1c, 4f, 00, 22, 00, 28, 20, 1d, 21, 00, 22, 00, 28, 1d, 00, 2c, 00, 33, 5b, 01, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 24
+- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(8, Sub)
+- expression 1 operands: lhs = Counter(4), rhs = Expression(13, Sub)
+- expression 2 operands: lhs = Expression(14, Add), rhs = Counter(4)
+- expression 3 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 4 operands: lhs = Expression(9, Sub), rhs = Counter(3)
+- expression 5 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 6 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 7 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 8 operands: lhs = Expression(9, Sub), rhs = Counter(3)
+- expression 9 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 10 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 11 operands: lhs = Expression(14, Add), rhs = Counter(4)
+- expression 12 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 13 operands: lhs = Expression(14, Add), rhs = Counter(4)
+- expression 14 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 15 operands: lhs = Counter(6), rhs = Expression(23, Add)
+- expression 16 operands: lhs = Counter(7), rhs = Counter(8)
+- expression 17 operands: lhs = Expression(0, Add), rhs = Counter(5)
+- expression 18 operands: lhs = Counter(5), rhs = Counter(6)
+- expression 19 operands: lhs = Expression(20, Sub), rhs = Expression(21, Sub)
+- expression 20 operands: lhs = Counter(5), rhs = Counter(6)
+- expression 21 operands: lhs = Expression(0, Add), rhs = Counter(5)
+- expression 22 operands: lhs = Counter(6), rhs = Expression(23, Add)
+- expression 23 operands: lhs = Counter(7), rhs = Counter(8)
+Number of file 0 mappings: 19
+- Code(Counter(0)) at (prev + 49, 1) to (start + 1, 16)
+- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10)
+    = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3))
+- Code(Counter(1)) at (prev + 0, 14) to (start + 0, 19)
+- Branch { true: Counter(2), false: Expression(9, Sub) } at (prev + 0, 14) to (start + 0, 19)
+    true  = c2
+    false = (c1 - c2)
+- Code(Expression(9, Sub)) at (prev + 0, 23) to (start + 0, 29)
+    = (c1 - c2)
+- Branch { true: Counter(3), false: Expression(8, Sub) } at (prev + 0, 23) to (start + 0, 29)
+    true  = c3
+    false = ((c1 - c2) - c3)
+- Code(Expression(14, Add)) at (prev + 0, 35) to (start + 0, 40)
+    = (c2 + c3)
+- Branch { true: Counter(4), false: Expression(13, Sub) } at (prev + 0, 35) to (start + 0, 40)
+    true  = c4
+    false = ((c2 + c3) - c4)
+- Code(Expression(13, Sub)) at (prev + 0, 44) to (start + 0, 51)
+    = ((c2 + c3) - c4)
+- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 0, 17)
+    = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3))
+- Code(Expression(22, Add)) at (prev + 3, 9) to (start + 0, 10)
+    = (c6 + (c7 + c8))
+- Code(Expression(0, Add)) at (prev + 0, 14) to (start + 0, 19)
+    = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3))
+- Branch { true: Counter(5), false: Expression(21, Sub) } at (prev + 0, 14) to (start + 0, 19)
+    true  = c5
+    false = (((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) - c5)
+- Code(Counter(5)) at (prev + 0, 23) to (start + 0, 28)
+- Branch { true: Counter(6), false: Expression(20, Sub) } at (prev + 0, 23) to (start + 0, 28)
+    true  = c6
+    false = (c5 - c6)
+- Code(Expression(19, Add)) at (prev + 0, 34) to (start + 0, 40)
+    = ((c5 - c6) + (((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) - c5))
+- Branch { true: Counter(7), false: Counter(8) } at (prev + 0, 34) to (start + 0, 40)
+    true  = c7
+    false = c8
+- Code(Counter(7)) at (prev + 0, 44) to (start + 0, 51)
+- Code(Expression(22, Add)) at (prev + 1, 5) to (start + 1, 2)
+    = (c6 + (c7 + c8))
+
diff --git a/tests/coverage/branch/lazy-boolean.coverage b/tests/coverage/branch/lazy-boolean.coverage
new file mode 100644
index 0000000..f6aba1d
--- /dev/null
+++ b/tests/coverage/branch/lazy-boolean.coverage
@@ -0,0 +1,113 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ compile-flags: -Zcoverage-options=branch
+   LL|       |//@ llvm-cov-flags: --show-branches=count
+   LL|       |
+   LL|       |// Tests for branch coverage of the lazy boolean operators `&&` and `||`,
+   LL|       |// as ordinary expressions that aren't part of an `if` condition or similar.
+   LL|       |
+   LL|       |use core::hint::black_box;
+   LL|       |
+   LL|       |// Helper macro to prevent start-of-function spans from being merged into
+   LL|       |// spans on the lines we care about.
+   LL|       |macro_rules! no_merge {
+   LL|       |    () => {
+   LL|       |        for _ in 0..1 {}
+   LL|       |    };
+   LL|       |}
+   LL|       |
+   LL|     15|fn branch_and(a: bool, b: bool) {
+   LL|     15|    no_merge!();
+   LL|       |
+   LL|       |    //      |13  |18 (no branch)
+   LL|     15|    let c = a && b;
+                               ^12
+  ------------------
+  |  Branch (LL:13): [True: 12, False: 3]
+  ------------------
+   LL|     15|    black_box(c);
+   LL|     15|}
+   LL|       |
+   LL|     15|fn branch_or(a: bool, b: bool) {
+   LL|     15|    no_merge!();
+   LL|       |
+   LL|       |    //      |13  |18 (no branch)
+   LL|     15|    let c = a || b;
+                               ^3
+  ------------------
+  |  Branch (LL:13): [True: 12, False: 3]
+  ------------------
+   LL|     15|    black_box(c);
+   LL|     15|}
+   LL|       |
+   LL|       |// Test for chaining one operator several times.
+   LL|     16|fn chain(x: u32) {
+   LL|     16|    no_merge!();
+   LL|       |
+   LL|       |    //      |13      |22      |31      |40 (no branch)
+   LL|     16|    let c = x > 1 && x > 2 && x > 4 && x > 8;
+                                   ^14      ^13      ^11
+  ------------------
+  |  Branch (LL:13): [True: 14, False: 2]
+  |  Branch (LL:22): [True: 13, False: 1]
+  |  Branch (LL:31): [True: 11, False: 2]
+  ------------------
+   LL|     16|    black_box(c);
+   LL|       |
+   LL|       |    //      |13      |22      |31      |40 (no branch)
+   LL|     16|    let d = x < 1 || x < 2 || x < 4 || x < 8;
+                                   ^15      ^14      ^12
+  ------------------
+  |  Branch (LL:13): [True: 1, False: 15]
+  |  Branch (LL:22): [True: 1, False: 14]
+  |  Branch (LL:31): [True: 2, False: 12]
+  ------------------
+   LL|     16|    black_box(d);
+   LL|     16|}
+   LL|       |
+   LL|       |// Test for nested combinations of different operators.
+   LL|     16|fn nested_mixed(x: u32) {
+   LL|     16|    no_merge!();
+   LL|       |
+   LL|       |    //       |14      |23         |35      |44 (no branch)
+   LL|     16|    let c = (x < 4 || x >= 9) && (x < 2 || x >= 10);
+                                    ^12         ^11      ^9
+  ------------------
+  |  Branch (LL:14): [True: 4, False: 12]
+  |  Branch (LL:23): [True: 7, False: 5]
+  |  Branch (LL:35): [True: 2, False: 9]
+  ------------------
+   LL|     16|    black_box(c);
+   LL|       |
+   LL|       |    //       |14      |23        |34       |44 (no branch)
+   LL|     16|    let d = (x < 4 && x < 1) || (x >= 8 && x >= 10);
+                                    ^4         ^15       ^8
+  ------------------
+  |  Branch (LL:14): [True: 4, False: 12]
+  |  Branch (LL:23): [True: 1, False: 3]
+  |  Branch (LL:34): [True: 8, False: 7]
+  ------------------
+   LL|     16|    black_box(d);
+   LL|     16|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    // Use each set of arguments (2^n) times, so that each combination has a
+   LL|       |    // unique sum, and we can use those sums to verify expected control flow.
+   LL|       |    // 1x (false, false)
+   LL|       |    // 2x (false, true)
+   LL|       |    // 4x (true, false)
+   LL|       |    // 8x (true, true)
+   LL|       |    for a in [false, true, true, true, true] {
+   LL|       |        for b in [false, true, true] {
+   LL|       |            branch_and(a, b);
+   LL|       |            branch_or(a, b);
+   LL|       |        }
+   LL|       |    }
+   LL|       |
+   LL|       |    for x in 0..16 {
+   LL|       |        chain(x);
+   LL|       |        nested_mixed(x);
+   LL|       |    }
+   LL|       |}
+
diff --git a/tests/coverage/branch/lazy-boolean.rs b/tests/coverage/branch/lazy-boolean.rs
new file mode 100644
index 0000000..3c73fc1
--- /dev/null
+++ b/tests/coverage/branch/lazy-boolean.rs
@@ -0,0 +1,80 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+//@ compile-flags: -Zcoverage-options=branch
+//@ llvm-cov-flags: --show-branches=count
+
+// Tests for branch coverage of the lazy boolean operators `&&` and `||`,
+// as ordinary expressions that aren't part of an `if` condition or similar.
+
+use core::hint::black_box;
+
+// Helper macro to prevent start-of-function spans from being merged into
+// spans on the lines we care about.
+macro_rules! no_merge {
+    () => {
+        for _ in 0..1 {}
+    };
+}
+
+fn branch_and(a: bool, b: bool) {
+    no_merge!();
+
+    //      |13  |18 (no branch)
+    let c = a && b;
+    black_box(c);
+}
+
+fn branch_or(a: bool, b: bool) {
+    no_merge!();
+
+    //      |13  |18 (no branch)
+    let c = a || b;
+    black_box(c);
+}
+
+// Test for chaining one operator several times.
+fn chain(x: u32) {
+    no_merge!();
+
+    //      |13      |22      |31      |40 (no branch)
+    let c = x > 1 && x > 2 && x > 4 && x > 8;
+    black_box(c);
+
+    //      |13      |22      |31      |40 (no branch)
+    let d = x < 1 || x < 2 || x < 4 || x < 8;
+    black_box(d);
+}
+
+// Test for nested combinations of different operators.
+fn nested_mixed(x: u32) {
+    no_merge!();
+
+    //       |14      |23         |35      |44 (no branch)
+    let c = (x < 4 || x >= 9) && (x < 2 || x >= 10);
+    black_box(c);
+
+    //       |14      |23        |34       |44 (no branch)
+    let d = (x < 4 && x < 1) || (x >= 8 && x >= 10);
+    black_box(d);
+}
+
+#[coverage(off)]
+fn main() {
+    // Use each set of arguments (2^n) times, so that each combination has a
+    // unique sum, and we can use those sums to verify expected control flow.
+    // 1x (false, false)
+    // 2x (false, true)
+    // 4x (true, false)
+    // 8x (true, true)
+    for a in [false, true, true, true, true] {
+        for b in [false, true, true] {
+            branch_and(a, b);
+            branch_or(a, b);
+        }
+    }
+
+    for x in 0..16 {
+        chain(x);
+        nested_mixed(x);
+    }
+}
diff --git a/tests/coverage/branch/let-else.cov-map b/tests/coverage/branch/let-else.cov-map
new file mode 100644
index 0000000..ad987bd
--- /dev/null
+++ b/tests/coverage/branch/let-else.cov-map
@@ -0,0 +1,18 @@
+Function name: let_else::let_else
+Raw bytes (38): 0x[01, 01, 02, 05, 09, 09, 02, 06, 01, 0c, 01, 01, 10, 02, 03, 0e, 00, 0f, 05, 00, 13, 00, 18, 09, 01, 09, 01, 0f, 02, 04, 05, 00, 0b, 07, 01, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 2
+- expression 0 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+Number of file 0 mappings: 6
+- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
+- Code(Expression(0, Sub)) at (prev + 3, 14) to (start + 0, 15)
+    = (c1 - c2)
+- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24)
+- Code(Counter(2)) at (prev + 1, 9) to (start + 1, 15)
+- Code(Expression(0, Sub)) at (prev + 4, 5) to (start + 0, 11)
+    = (c1 - c2)
+- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2)
+    = (c2 + (c1 - c2))
+
diff --git a/tests/coverage/branch/let-else.coverage b/tests/coverage/branch/let-else.coverage
new file mode 100644
index 0000000..83730e1
--- /dev/null
+++ b/tests/coverage/branch/let-else.coverage
@@ -0,0 +1,37 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ compile-flags: -Zcoverage-options=branch
+   LL|       |//@ llvm-cov-flags: --show-branches=count
+   LL|       |
+   LL|       |macro_rules! no_merge {
+   LL|       |    () => {
+   LL|       |        for _ in 0..1 {}
+   LL|       |    };
+   LL|       |}
+   LL|       |
+   LL|      3|fn let_else(value: Option<&str>) {
+   LL|      3|    no_merge!();
+   LL|       |
+   LL|      3|    let Some(x) = value else {
+                           ^2
+   LL|      1|        say("none");
+   LL|      1|        return;
+   LL|       |    };
+   LL|       |
+   LL|      2|    say(x);
+   LL|      3|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn say(message: &str) {
+   LL|       |    core::hint::black_box(message);
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    let_else(Some("x"));
+   LL|       |    let_else(Some("x"));
+   LL|       |    let_else(None);
+   LL|       |}
+   LL|       |
+   LL|       |// FIXME(#124118) Actually instrument let-else for branch coverage.
+
diff --git a/tests/coverage/branch/let-else.rs b/tests/coverage/branch/let-else.rs
new file mode 100644
index 0000000..af0665d
--- /dev/null
+++ b/tests/coverage/branch/let-else.rs
@@ -0,0 +1,35 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+//@ compile-flags: -Zcoverage-options=branch
+//@ llvm-cov-flags: --show-branches=count
+
+macro_rules! no_merge {
+    () => {
+        for _ in 0..1 {}
+    };
+}
+
+fn let_else(value: Option<&str>) {
+    no_merge!();
+
+    let Some(x) = value else {
+        say("none");
+        return;
+    };
+
+    say(x);
+}
+
+#[coverage(off)]
+fn say(message: &str) {
+    core::hint::black_box(message);
+}
+
+#[coverage(off)]
+fn main() {
+    let_else(Some("x"));
+    let_else(Some("x"));
+    let_else(None);
+}
+
+// FIXME(#124118) Actually instrument let-else for branch coverage.
diff --git a/tests/coverage/branch/match-arms.cov-map b/tests/coverage/branch/match-arms.cov-map
new file mode 100644
index 0000000..1f17f11
--- /dev/null
+++ b/tests/coverage/branch/match-arms.cov-map
@@ -0,0 +1,92 @@
+Function name: match_arms::guards
+Raw bytes (88): 0x[01, 01, 08, 07, 15, 0b, 11, 0f, 0d, 00, 09, 17, 25, 1b, 21, 1f, 1d, 03, 19, 0c, 01, 30, 01, 01, 10, 29, 03, 0b, 00, 10, 19, 01, 11, 00, 29, 20, 19, 09, 00, 17, 00, 1b, 1d, 01, 11, 00, 29, 20, 1d, 0d, 00, 17, 00, 1b, 21, 01, 11, 00, 29, 20, 21, 11, 00, 17, 00, 1b, 25, 01, 11, 00, 29, 20, 25, 15, 00, 17, 00, 1b, 03, 01, 0e, 00, 18, 13, 03, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 8
+- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(5)
+- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4)
+- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3)
+- expression 3 operands: lhs = Zero, rhs = Counter(2)
+- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(9)
+- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(8)
+- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(7)
+- expression 7 operands: lhs = Expression(0, Add), rhs = Counter(6)
+Number of file 0 mappings: 12
+- Code(Counter(0)) at (prev + 48, 1) to (start + 1, 16)
+- Code(Counter(10)) at (prev + 3, 11) to (start + 0, 16)
+- Code(Counter(6)) at (prev + 1, 17) to (start + 0, 41)
+- Branch { true: Counter(6), false: Counter(2) } at (prev + 0, 23) to (start + 0, 27)
+    true  = c6
+    false = c2
+- Code(Counter(7)) at (prev + 1, 17) to (start + 0, 41)
+- Branch { true: Counter(7), false: Counter(3) } at (prev + 0, 23) to (start + 0, 27)
+    true  = c7
+    false = c3
+- Code(Counter(8)) at (prev + 1, 17) to (start + 0, 41)
+- Branch { true: Counter(8), false: Counter(4) } at (prev + 0, 23) to (start + 0, 27)
+    true  = c8
+    false = c4
+- Code(Counter(9)) at (prev + 1, 17) to (start + 0, 41)
+- Branch { true: Counter(9), false: Counter(5) } at (prev + 0, 23) to (start + 0, 27)
+    true  = c9
+    false = c5
+- Code(Expression(0, Add)) at (prev + 1, 14) to (start + 0, 24)
+    = ((((Zero + c2) + c3) + c4) + c5)
+- Code(Expression(4, Add)) at (prev + 3, 5) to (start + 1, 2)
+    = ((((((((Zero + c2) + c3) + c4) + c5) + c6) + c7) + c8) + c9)
+
+Function name: match_arms::match_arms
+Raw bytes (51): 0x[01, 01, 06, 05, 07, 0b, 11, 09, 0d, 13, 02, 17, 09, 11, 0d, 07, 01, 18, 01, 01, 10, 05, 03, 0b, 00, 10, 11, 01, 11, 00, 21, 0d, 01, 11, 00, 21, 09, 01, 11, 00, 21, 02, 01, 11, 00, 21, 0f, 03, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 6
+- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Add)
+- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4)
+- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 3 operands: lhs = Expression(4, Add), rhs = Expression(0, Sub)
+- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(2)
+- expression 5 operands: lhs = Counter(4), rhs = Counter(3)
+Number of file 0 mappings: 7
+- Code(Counter(0)) at (prev + 24, 1) to (start + 1, 16)
+- Code(Counter(1)) at (prev + 3, 11) to (start + 0, 16)
+- Code(Counter(4)) at (prev + 1, 17) to (start + 0, 33)
+- Code(Counter(3)) at (prev + 1, 17) to (start + 0, 33)
+- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 33)
+- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 33)
+    = (c1 - ((c2 + c3) + c4))
+- Code(Expression(3, Add)) at (prev + 3, 5) to (start + 1, 2)
+    = (((c4 + c3) + c2) + (c1 - ((c2 + c3) + c4)))
+
+Function name: match_arms::or_patterns
+Raw bytes (75): 0x[01, 01, 0d, 11, 0d, 05, 2f, 33, 11, 09, 0d, 09, 2a, 05, 2f, 33, 11, 09, 0d, 03, 27, 09, 2a, 05, 2f, 33, 11, 09, 0d, 09, 01, 25, 01, 01, 10, 05, 03, 0b, 00, 10, 11, 01, 11, 00, 12, 0d, 00, 1e, 00, 1f, 03, 00, 24, 00, 2e, 09, 01, 11, 00, 12, 2a, 00, 1e, 00, 1f, 27, 00, 24, 00, 2e, 23, 03, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 13
+- expression 0 operands: lhs = Counter(4), rhs = Counter(3)
+- expression 1 operands: lhs = Counter(1), rhs = Expression(11, Add)
+- expression 2 operands: lhs = Expression(12, Add), rhs = Counter(4)
+- expression 3 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 4 operands: lhs = Counter(2), rhs = Expression(10, Sub)
+- expression 5 operands: lhs = Counter(1), rhs = Expression(11, Add)
+- expression 6 operands: lhs = Expression(12, Add), rhs = Counter(4)
+- expression 7 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 8 operands: lhs = Expression(0, Add), rhs = Expression(9, Add)
+- expression 9 operands: lhs = Counter(2), rhs = Expression(10, Sub)
+- expression 10 operands: lhs = Counter(1), rhs = Expression(11, Add)
+- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(4)
+- expression 12 operands: lhs = Counter(2), rhs = Counter(3)
+Number of file 0 mappings: 9
+- Code(Counter(0)) at (prev + 37, 1) to (start + 1, 16)
+- Code(Counter(1)) at (prev + 3, 11) to (start + 0, 16)
+- Code(Counter(4)) at (prev + 1, 17) to (start + 0, 18)
+- Code(Counter(3)) at (prev + 0, 30) to (start + 0, 31)
+- Code(Expression(0, Add)) at (prev + 0, 36) to (start + 0, 46)
+    = (c4 + c3)
+- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 18)
+- Code(Expression(10, Sub)) at (prev + 0, 30) to (start + 0, 31)
+    = (c1 - ((c2 + c3) + c4))
+- Code(Expression(9, Add)) at (prev + 0, 36) to (start + 0, 46)
+    = (c2 + (c1 - ((c2 + c3) + c4)))
+- Code(Expression(8, Add)) at (prev + 3, 5) to (start + 1, 2)
+    = ((c4 + c3) + (c2 + (c1 - ((c2 + c3) + c4))))
+
diff --git a/tests/coverage/branch/match-arms.coverage b/tests/coverage/branch/match-arms.coverage
new file mode 100644
index 0000000..ea8a6f9
--- /dev/null
+++ b/tests/coverage/branch/match-arms.coverage
@@ -0,0 +1,105 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ compile-flags: -Zcoverage-options=branch
+   LL|       |//@ llvm-cov-flags: --show-branches=count
+   LL|       |
+   LL|       |// Tests for branch coverage of various kinds of match arms.
+   LL|       |
+   LL|       |// Helper macro to prevent start-of-function spans from being merged into
+   LL|       |// spans on the lines we care about.
+   LL|       |macro_rules! no_merge {
+   LL|       |    () => {
+   LL|       |        for _ in 0..1 {}
+   LL|       |    };
+   LL|       |}
+   LL|       |
+   LL|       |#[derive(Clone, Copy, Debug)]
+   LL|       |enum Enum {
+   LL|       |    A(u32),
+   LL|       |    B(u32),
+   LL|       |    C(u32),
+   LL|       |    D(u32),
+   LL|       |}
+   LL|       |
+   LL|     15|fn match_arms(value: Enum) {
+   LL|     15|    no_merge!();
+   LL|       |
+   LL|     15|    match value {
+   LL|      8|        Enum::D(d) => consume(d),
+   LL|      4|        Enum::C(c) => consume(c),
+   LL|      2|        Enum::B(b) => consume(b),
+   LL|      1|        Enum::A(a) => consume(a),
+   LL|       |    }
+   LL|       |
+   LL|     15|    consume(0);
+   LL|     15|}
+   LL|       |
+   LL|     15|fn or_patterns(value: Enum) {
+   LL|     15|    no_merge!();
+   LL|       |
+   LL|     15|    match value {
+   LL|     12|        Enum::D(x) | Enum::C(x) => consume(x),
+                              ^8           ^4
+   LL|      3|        Enum::B(y) | Enum::A(y) => consume(y),
+                              ^2           ^1
+   LL|       |    }
+   LL|       |
+   LL|     15|    consume(0);
+   LL|     15|}
+   LL|       |
+   LL|     45|fn guards(value: Enum, cond: bool) {
+   LL|     45|    no_merge!();
+   LL|       |
+   LL|      3|    match value {
+   LL|      8|        Enum::D(d) if cond => consume(d),
+  ------------------
+  |  Branch (LL:23): [True: 8, False: 16]
+  ------------------
+   LL|      4|        Enum::C(c) if cond => consume(c),
+  ------------------
+  |  Branch (LL:23): [True: 4, False: 8]
+  ------------------
+   LL|      2|        Enum::B(b) if cond => consume(b),
+  ------------------
+  |  Branch (LL:23): [True: 2, False: 4]
+  ------------------
+   LL|      1|        Enum::A(a) if cond => consume(a),
+  ------------------
+  |  Branch (LL:23): [True: 1, False: 2]
+  ------------------
+   LL|     30|        _ => consume(0),
+   LL|       |    }
+   LL|       |
+   LL|     45|    consume(0);
+   LL|     45|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn consume<T>(x: T) {
+   LL|       |    core::hint::black_box(x);
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    #[coverage(off)]
+   LL|       |    fn call_everything(e: Enum) {
+   LL|       |        match_arms(e);
+   LL|       |        or_patterns(e);
+   LL|       |        for cond in [false, false, true] {
+   LL|       |            guards(e, cond);
+   LL|       |        }
+   LL|       |    }
+   LL|       |
+   LL|       |    call_everything(Enum::A(0));
+   LL|       |    for b in 0..2 {
+   LL|       |        call_everything(Enum::B(b));
+   LL|       |    }
+   LL|       |    for c in 0..4 {
+   LL|       |        call_everything(Enum::C(c));
+   LL|       |    }
+   LL|       |    for d in 0..8 {
+   LL|       |        call_everything(Enum::D(d));
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|       |// FIXME(#124118) Actually instrument match arms for branch coverage.
+
diff --git a/tests/coverage/branch/match-arms.rs b/tests/coverage/branch/match-arms.rs
new file mode 100644
index 0000000..63151f5
--- /dev/null
+++ b/tests/coverage/branch/match-arms.rs
@@ -0,0 +1,90 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+//@ compile-flags: -Zcoverage-options=branch
+//@ llvm-cov-flags: --show-branches=count
+
+// Tests for branch coverage of various kinds of match arms.
+
+// Helper macro to prevent start-of-function spans from being merged into
+// spans on the lines we care about.
+macro_rules! no_merge {
+    () => {
+        for _ in 0..1 {}
+    };
+}
+
+#[derive(Clone, Copy, Debug)]
+enum Enum {
+    A(u32),
+    B(u32),
+    C(u32),
+    D(u32),
+}
+
+fn match_arms(value: Enum) {
+    no_merge!();
+
+    match value {
+        Enum::D(d) => consume(d),
+        Enum::C(c) => consume(c),
+        Enum::B(b) => consume(b),
+        Enum::A(a) => consume(a),
+    }
+
+    consume(0);
+}
+
+fn or_patterns(value: Enum) {
+    no_merge!();
+
+    match value {
+        Enum::D(x) | Enum::C(x) => consume(x),
+        Enum::B(y) | Enum::A(y) => consume(y),
+    }
+
+    consume(0);
+}
+
+fn guards(value: Enum, cond: bool) {
+    no_merge!();
+
+    match value {
+        Enum::D(d) if cond => consume(d),
+        Enum::C(c) if cond => consume(c),
+        Enum::B(b) if cond => consume(b),
+        Enum::A(a) if cond => consume(a),
+        _ => consume(0),
+    }
+
+    consume(0);
+}
+
+#[coverage(off)]
+fn consume<T>(x: T) {
+    core::hint::black_box(x);
+}
+
+#[coverage(off)]
+fn main() {
+    #[coverage(off)]
+    fn call_everything(e: Enum) {
+        match_arms(e);
+        or_patterns(e);
+        for cond in [false, false, true] {
+            guards(e, cond);
+        }
+    }
+
+    call_everything(Enum::A(0));
+    for b in 0..2 {
+        call_everything(Enum::B(b));
+    }
+    for c in 0..4 {
+        call_everything(Enum::C(c));
+    }
+    for d in 0..8 {
+        call_everything(Enum::D(d));
+    }
+}
+
+// FIXME(#124118) Actually instrument match arms for branch coverage.
diff --git a/tests/coverage/branch/match-trivial.cov-map b/tests/coverage/branch/match-trivial.cov-map
new file mode 100644
index 0000000..1136a52
--- /dev/null
+++ b/tests/coverage/branch/match-trivial.cov-map
@@ -0,0 +1,17 @@
+Function name: match_trivial::_uninhabited (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 16, 01, 01, 10]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 22, 1) to (start + 1, 16)
+
+Function name: match_trivial::trivial
+Raw bytes (14): 0x[01, 01, 00, 02, 01, 1e, 01, 01, 10, 05, 03, 0b, 05, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 30, 1) to (start + 1, 16)
+- Code(Counter(1)) at (prev + 3, 11) to (start + 5, 2)
+
diff --git a/tests/coverage/branch/match-trivial.coverage b/tests/coverage/branch/match-trivial.coverage
new file mode 100644
index 0000000..4ffb172
--- /dev/null
+++ b/tests/coverage/branch/match-trivial.coverage
@@ -0,0 +1,49 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ compile-flags: -Zcoverage-options=branch
+   LL|       |//@ llvm-cov-flags: --show-branches=count
+   LL|       |
+   LL|       |// When instrumenting match expressions for branch coverage, make sure we don't
+   LL|       |// cause an ICE or produce weird coverage output for matches with <2 arms.
+   LL|       |
+   LL|       |// Helper macro to prevent start-of-function spans from being merged into
+   LL|       |// spans on the lines we care about.
+   LL|       |macro_rules! no_merge {
+   LL|       |    () => {
+   LL|       |        for _ in 0..1 {}
+   LL|       |    };
+   LL|       |}
+   LL|       |
+   LL|       |enum Uninhabited {}
+   LL|       |enum Trivial {
+   LL|       |    Value,
+   LL|       |}
+   LL|       |
+   LL|      0|fn _uninhabited(x: Uninhabited) {
+   LL|      0|    no_merge!();
+   LL|       |
+   LL|       |    match x {}
+   LL|       |
+   LL|       |    consume("done");
+   LL|       |}
+   LL|       |
+   LL|      1|fn trivial(x: Trivial) {
+   LL|      1|    no_merge!();
+   LL|       |
+   LL|      1|    match x {
+   LL|      1|        Trivial::Value => consume("trivial"),
+   LL|      1|    }
+   LL|      1|
+   LL|      1|    consume("done");
+   LL|      1|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn consume<T>(x: T) {
+   LL|       |    core::hint::black_box(x);
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    trivial(Trivial::Value);
+   LL|       |}
+
diff --git a/tests/coverage/branch/match-trivial.rs b/tests/coverage/branch/match-trivial.rs
new file mode 100644
index 0000000..db8887a
--- /dev/null
+++ b/tests/coverage/branch/match-trivial.rs
@@ -0,0 +1,48 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+//@ compile-flags: -Zcoverage-options=branch
+//@ llvm-cov-flags: --show-branches=count
+
+// When instrumenting match expressions for branch coverage, make sure we don't
+// cause an ICE or produce weird coverage output for matches with <2 arms.
+
+// Helper macro to prevent start-of-function spans from being merged into
+// spans on the lines we care about.
+macro_rules! no_merge {
+    () => {
+        for _ in 0..1 {}
+    };
+}
+
+enum Uninhabited {}
+enum Trivial {
+    Value,
+}
+
+fn _uninhabited(x: Uninhabited) {
+    no_merge!();
+
+    match x {}
+
+    consume("done");
+}
+
+fn trivial(x: Trivial) {
+    no_merge!();
+
+    match x {
+        Trivial::Value => consume("trivial"),
+    }
+
+    consume("done");
+}
+
+#[coverage(off)]
+fn consume<T>(x: T) {
+    core::hint::black_box(x);
+}
+
+#[coverage(off)]
+fn main() {
+    trivial(Trivial::Value);
+}
diff --git a/tests/coverage/branch_while.cov-map b/tests/coverage/branch/while.cov-map
similarity index 96%
rename from tests/coverage/branch_while.cov-map
rename to tests/coverage/branch/while.cov-map
index d5f54f1..5a3ef09 100644
--- a/tests/coverage/branch_while.cov-map
+++ b/tests/coverage/branch/while.cov-map
@@ -1,4 +1,4 @@
-Function name: branch_while::while_cond
+Function name: while::while_cond
 Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 0c, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 10, 20, 09, 0a, 00, 0b, 00, 10, 09, 00, 11, 02, 06, 0a, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
@@ -18,7 +18,7 @@
 - Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2)
     = ((c1 + c2) - c2)
 
-Function name: branch_while::while_cond_not
+Function name: while::while_cond_not
 Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 15, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 14, 20, 09, 0a, 00, 0b, 00, 14, 09, 00, 15, 02, 06, 0a, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
@@ -38,7 +38,7 @@
 - Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2)
     = ((c1 + c2) - c2)
 
-Function name: branch_while::while_op_and
+Function name: while::while_op_and
 Raw bytes (56): 0x[01, 01, 04, 05, 09, 03, 0d, 03, 0d, 11, 0d, 08, 01, 1e, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 0a, 0d, 00, 0b, 00, 10, 0a, 00, 14, 00, 19, 20, 09, 11, 00, 14, 00, 19, 09, 00, 1a, 03, 06, 0f, 04, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
@@ -64,7 +64,7 @@
 - Code(Expression(3, Add)) at (prev + 4, 1) to (start + 0, 2)
     = (c4 + c3)
 
-Function name: branch_while::while_op_or
+Function name: while::while_op_or
 Raw bytes (66): 0x[01, 01, 09, 05, 1b, 09, 0d, 03, 09, 03, 09, 22, 0d, 03, 09, 09, 0d, 22, 0d, 03, 09, 08, 01, 29, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 09, 22, 00, 0b, 00, 10, 22, 00, 14, 00, 19, 20, 0d, 1e, 00, 14, 00, 19, 1b, 00, 1a, 03, 06, 1e, 04, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
diff --git a/tests/coverage/branch_while.coverage b/tests/coverage/branch/while.coverage
similarity index 100%
rename from tests/coverage/branch_while.coverage
rename to tests/coverage/branch/while.coverage
diff --git a/tests/coverage/branch_while.rs b/tests/coverage/branch/while.rs
similarity index 100%
rename from tests/coverage/branch_while.rs
rename to tests/coverage/branch/while.rs
diff --git a/tests/coverage/mcdc_if.cov-map b/tests/coverage/mcdc_if.cov-map
new file mode 100644
index 0000000..35a2656
--- /dev/null
+++ b/tests/coverage/mcdc_if.cov-map
@@ -0,0 +1,218 @@
+Function name: mcdc_if::mcdc_check_a
+Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 0f, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 4
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+- expression 2 operands: lhs = Counter(3), rhs = Expression(3, Add)
+- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+Number of file 0 mappings: 8
+- Code(Counter(0)) at (prev + 15, 1) to (start + 1, 9)
+- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14)
+- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
+    true  = c1
+    false = (c0 - c1)
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
+- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14)
+    true  = c3
+    false = c2
+- Code(Counter(3)) at (prev + 0, 15) to (start + 2, 6)
+- Code(Expression(3, Add)) at (prev + 2, 12) to (start + 2, 6)
+    = (c2 + (c0 - c1))
+- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2)
+    = (c3 + (c2 + (c0 - c1)))
+
+Function name: mcdc_if::mcdc_check_b
+Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 17, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 4
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+- expression 2 operands: lhs = Counter(3), rhs = Expression(3, Add)
+- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+Number of file 0 mappings: 8
+- Code(Counter(0)) at (prev + 23, 1) to (start + 1, 9)
+- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14)
+- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
+    true  = c1
+    false = (c0 - c1)
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
+- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14)
+    true  = c3
+    false = c2
+- Code(Counter(3)) at (prev + 0, 15) to (start + 2, 6)
+- Code(Expression(3, Add)) at (prev + 2, 12) to (start + 2, 6)
+    = (c2 + (c0 - c1))
+- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2)
+    = (c3 + (c2 + (c0 - c1)))
+
+Function name: mcdc_if::mcdc_check_both
+Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 1f, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 4
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+- expression 2 operands: lhs = Counter(3), rhs = Expression(3, Add)
+- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+Number of file 0 mappings: 8
+- Code(Counter(0)) at (prev + 31, 1) to (start + 1, 9)
+- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14)
+- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
+    true  = c1
+    false = (c0 - c1)
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
+- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14)
+    true  = c3
+    false = c2
+- Code(Counter(3)) at (prev + 0, 15) to (start + 2, 6)
+- Code(Expression(3, Add)) at (prev + 2, 12) to (start + 2, 6)
+    = (c2 + (c0 - c1))
+- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2)
+    = (c3 + (c2 + (c0 - c1)))
+
+Function name: mcdc_if::mcdc_check_neither
+Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 07, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 4
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+- expression 2 operands: lhs = Counter(3), rhs = Expression(3, Add)
+- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+Number of file 0 mappings: 8
+- Code(Counter(0)) at (prev + 7, 1) to (start + 1, 9)
+- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14)
+- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
+    true  = c1
+    false = (c0 - c1)
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
+- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14)
+    true  = c3
+    false = c2
+- Code(Counter(3)) at (prev + 0, 15) to (start + 2, 6)
+- Code(Expression(3, Add)) at (prev + 2, 12) to (start + 2, 6)
+    = (c2 + (c0 - c1))
+- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2)
+    = (c3 + (c2 + (c0 - c1)))
+
+Function name: mcdc_if::mcdc_check_not_tree_decision
+Raw bytes (87): 0x[01, 01, 08, 01, 05, 02, 09, 05, 09, 0d, 1e, 02, 09, 11, 1b, 0d, 1e, 02, 09, 0a, 01, 31, 01, 03, 0a, 28, 00, 03, 03, 08, 00, 15, 30, 05, 02, 01, 02, 03, 00, 09, 00, 0a, 02, 00, 0e, 00, 0f, 30, 09, 1e, 03, 02, 00, 00, 0e, 00, 0f, 0b, 00, 14, 00, 15, 30, 11, 0d, 02, 00, 00, 00, 14, 00, 15, 11, 00, 16, 02, 06, 1b, 02, 0c, 02, 06, 17, 03, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 8
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Expression(0, Sub), rhs = Counter(2)
+- expression 2 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 3 operands: lhs = Counter(3), rhs = Expression(7, Sub)
+- expression 4 operands: lhs = Expression(0, Sub), rhs = Counter(2)
+- expression 5 operands: lhs = Counter(4), rhs = Expression(6, Add)
+- expression 6 operands: lhs = Counter(3), rhs = Expression(7, Sub)
+- expression 7 operands: lhs = Expression(0, Sub), rhs = Counter(2)
+Number of file 0 mappings: 10
+- Code(Counter(0)) at (prev + 49, 1) to (start + 3, 10)
+- MCDCDecision { bitmap_idx: 0, conditions_num: 3 } at (prev + 3, 8) to (start + 0, 21)
+- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 3 } at (prev + 0, 9) to (start + 0, 10)
+    true  = c1
+    false = (c0 - c1)
+- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 15)
+    = (c0 - c1)
+- MCDCBranch { true: Counter(2), false: Expression(7, Sub), condition_id: 3, true_next_id: 2, false_next_id: 0 } at (prev + 0, 14) to (start + 0, 15)
+    true  = c2
+    false = ((c0 - c1) - c2)
+- Code(Expression(2, Add)) at (prev + 0, 20) to (start + 0, 21)
+    = (c1 + c2)
+- MCDCBranch { true: Counter(4), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 20) to (start + 0, 21)
+    true  = c4
+    false = c3
+- Code(Counter(4)) at (prev + 0, 22) to (start + 2, 6)
+- Code(Expression(6, Add)) at (prev + 2, 12) to (start + 2, 6)
+    = (c3 + ((c0 - c1) - c2))
+- Code(Expression(5, Add)) at (prev + 3, 1) to (start + 0, 2)
+    = (c4 + (c3 + ((c0 - c1) - c2)))
+
+Function name: mcdc_if::mcdc_check_tree_decision
+Raw bytes (87): 0x[01, 01, 08, 01, 05, 05, 0d, 05, 0d, 0d, 11, 09, 02, 1b, 1f, 0d, 11, 09, 02, 0a, 01, 27, 01, 03, 09, 28, 00, 03, 03, 08, 00, 15, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0e, 00, 0f, 30, 0d, 0a, 02, 00, 03, 00, 0e, 00, 0f, 0a, 00, 13, 00, 14, 30, 11, 09, 03, 00, 00, 00, 13, 00, 14, 1b, 00, 16, 02, 06, 1f, 02, 0c, 02, 06, 17, 03, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 8
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(1), rhs = Counter(3)
+- expression 2 operands: lhs = Counter(1), rhs = Counter(3)
+- expression 3 operands: lhs = Counter(3), rhs = Counter(4)
+- expression 4 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+- expression 5 operands: lhs = Expression(6, Add), rhs = Expression(7, Add)
+- expression 6 operands: lhs = Counter(3), rhs = Counter(4)
+- expression 7 operands: lhs = Counter(2), rhs = Expression(0, Sub)
+Number of file 0 mappings: 10
+- Code(Counter(0)) at (prev + 39, 1) to (start + 3, 9)
+- MCDCDecision { bitmap_idx: 0, conditions_num: 3 } at (prev + 3, 8) to (start + 0, 21)
+- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
+    true  = c1
+    false = (c0 - c1)
+- Code(Counter(1)) at (prev + 0, 14) to (start + 0, 15)
+- MCDCBranch { true: Counter(3), false: Expression(2, Sub), condition_id: 2, true_next_id: 0, false_next_id: 3 } at (prev + 0, 14) to (start + 0, 15)
+    true  = c3
+    false = (c1 - c3)
+- Code(Expression(2, Sub)) at (prev + 0, 19) to (start + 0, 20)
+    = (c1 - c3)
+- MCDCBranch { true: Counter(4), false: Counter(2), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 19) to (start + 0, 20)
+    true  = c4
+    false = c2
+- Code(Expression(6, Add)) at (prev + 0, 22) to (start + 2, 6)
+    = (c3 + c4)
+- Code(Expression(7, Add)) at (prev + 2, 12) to (start + 2, 6)
+    = (c2 + (c0 - c1))
+- Code(Expression(5, Add)) at (prev + 3, 1) to (start + 0, 2)
+    = ((c3 + c4) + (c2 + (c0 - c1)))
+
+Function name: mcdc_if::mcdc_nested_if
+Raw bytes (124): 0x[01, 01, 0d, 01, 05, 02, 09, 05, 09, 1b, 15, 05, 09, 1b, 15, 05, 09, 11, 15, 02, 09, 2b, 32, 0d, 2f, 11, 15, 02, 09, 0e, 01, 3b, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 00, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 30, 09, 32, 02, 00, 00, 00, 0d, 00, 0e, 1b, 01, 09, 01, 0d, 28, 01, 02, 01, 0c, 00, 12, 30, 16, 15, 01, 02, 00, 00, 0c, 00, 0d, 16, 00, 11, 00, 12, 30, 0d, 11, 02, 00, 00, 00, 11, 00, 12, 0d, 00, 13, 02, 0a, 2f, 02, 0a, 00, 0b, 32, 01, 0c, 02, 06, 27, 03, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 13
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Expression(0, Sub), rhs = Counter(2)
+- expression 2 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 3 operands: lhs = Expression(6, Add), rhs = Counter(5)
+- expression 4 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(5)
+- expression 6 operands: lhs = Counter(1), rhs = Counter(2)
+- expression 7 operands: lhs = Counter(4), rhs = Counter(5)
+- expression 8 operands: lhs = Expression(0, Sub), rhs = Counter(2)
+- expression 9 operands: lhs = Expression(10, Add), rhs = Expression(12, Sub)
+- expression 10 operands: lhs = Counter(3), rhs = Expression(11, Add)
+- expression 11 operands: lhs = Counter(4), rhs = Counter(5)
+- expression 12 operands: lhs = Expression(0, Sub), rhs = Counter(2)
+Number of file 0 mappings: 14
+- Code(Counter(0)) at (prev + 59, 1) to (start + 1, 9)
+- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14)
+- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 8) to (start + 0, 9)
+    true  = c1
+    false = (c0 - c1)
+- Code(Expression(0, Sub)) at (prev + 0, 13) to (start + 0, 14)
+    = (c0 - c1)
+- MCDCBranch { true: Counter(2), false: Expression(12, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14)
+    true  = c2
+    false = ((c0 - c1) - c2)
+- Code(Expression(6, Add)) at (prev + 1, 9) to (start + 1, 13)
+    = (c1 + c2)
+- MCDCDecision { bitmap_idx: 1, conditions_num: 2 } at (prev + 1, 12) to (start + 0, 18)
+- MCDCBranch { true: Expression(5, Sub), false: Counter(5), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 12) to (start + 0, 13)
+    true  = ((c1 + c2) - c5)
+    false = c5
+- Code(Expression(5, Sub)) at (prev + 0, 17) to (start + 0, 18)
+    = ((c1 + c2) - c5)
+- MCDCBranch { true: Counter(3), false: Counter(4), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 17) to (start + 0, 18)
+    true  = c3
+    false = c4
+- Code(Counter(3)) at (prev + 0, 19) to (start + 2, 10)
+- Code(Expression(11, Add)) at (prev + 2, 10) to (start + 0, 11)
+    = (c4 + c5)
+- Code(Expression(12, Sub)) at (prev + 1, 12) to (start + 2, 6)
+    = ((c0 - c1) - c2)
+- Code(Expression(9, Add)) at (prev + 3, 1) to (start + 0, 2)
+    = ((c3 + (c4 + c5)) + ((c0 - c1) - c2))
+
diff --git a/tests/coverage/mcdc_if.coverage b/tests/coverage/mcdc_if.coverage
new file mode 100644
index 0000000..c2ed311
--- /dev/null
+++ b/tests/coverage/mcdc_if.coverage
@@ -0,0 +1,262 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ min-llvm-version: 18
+   LL|       |//@ compile-flags: -Zcoverage-options=mcdc
+   LL|       |//@ llvm-cov-flags: --show-mcdc
+   LL|       |
+   LL|      2|fn mcdc_check_neither(a: bool, b: bool) {
+   LL|      2|    if a && b {
+                          ^0
+  ------------------
+  |---> MC/DC Decision Region (LL:8) to (LL:14)
+  |
+  |  Number of Conditions: 2
+  |     Condition C1 --> (LL:8)
+  |     Condition C2 --> (LL:13)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2    Result
+  |  1 { F,  -  = F      }
+  |
+  |  C1-Pair: not covered
+  |  C2-Pair: not covered
+  |  MC/DC Coverage for Decision: 0.00%
+  |
+  ------------------
+   LL|      0|        say("a and b");
+   LL|      2|    } else {
+   LL|      2|        say("not both");
+   LL|      2|    }
+   LL|      2|}
+   LL|       |
+   LL|      2|fn mcdc_check_a(a: bool, b: bool) {
+   LL|      2|    if a && b {
+                          ^1
+  ------------------
+  |---> MC/DC Decision Region (LL:8) to (LL:14)
+  |
+  |  Number of Conditions: 2
+  |     Condition C1 --> (LL:8)
+  |     Condition C2 --> (LL:13)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2    Result
+  |  1 { F,  -  = F      }
+  |  2 { T,  T  = T      }
+  |
+  |  C1-Pair: covered: (1,2)
+  |  C2-Pair: not covered
+  |  MC/DC Coverage for Decision: 50.00%
+  |
+  ------------------
+   LL|      1|        say("a and b");
+   LL|      1|    } else {
+   LL|      1|        say("not both");
+   LL|      1|    }
+   LL|      2|}
+   LL|       |
+   LL|      2|fn mcdc_check_b(a: bool, b: bool) {
+   LL|      2|    if a && b {
+  ------------------
+  |---> MC/DC Decision Region (LL:8) to (LL:14)
+  |
+  |  Number of Conditions: 2
+  |     Condition C1 --> (LL:8)
+  |     Condition C2 --> (LL:13)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2    Result
+  |  1 { T,  F  = F      }
+  |  2 { T,  T  = T      }
+  |
+  |  C1-Pair: not covered
+  |  C2-Pair: covered: (1,2)
+  |  MC/DC Coverage for Decision: 50.00%
+  |
+  ------------------
+   LL|      1|        say("a and b");
+   LL|      1|    } else {
+   LL|      1|        say("not both");
+   LL|      1|    }
+   LL|      2|}
+   LL|       |
+   LL|      3|fn mcdc_check_both(a: bool, b: bool) {
+   LL|      3|    if a && b {
+                          ^2
+  ------------------
+  |---> MC/DC Decision Region (LL:8) to (LL:14)
+  |
+  |  Number of Conditions: 2
+  |     Condition C1 --> (LL:8)
+  |     Condition C2 --> (LL:13)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2    Result
+  |  1 { F,  -  = F      }
+  |  2 { T,  F  = F      }
+  |  3 { T,  T  = T      }
+  |
+  |  C1-Pair: covered: (1,3)
+  |  C2-Pair: covered: (2,3)
+  |  MC/DC Coverage for Decision: 100.00%
+  |
+  ------------------
+   LL|      1|        say("a and b");
+   LL|      2|    } else {
+   LL|      2|        say("not both");
+   LL|      2|    }
+   LL|      3|}
+   LL|       |
+   LL|      4|fn mcdc_check_tree_decision(a: bool, b: bool, c: bool) {
+   LL|      4|    // This expression is intentionally written in a way
+   LL|      4|    // where 100% branch coverage indicates 100% mcdc coverage.
+   LL|      4|    if a && (b || c) {
+                           ^3   ^2
+  ------------------
+  |---> MC/DC Decision Region (LL:8) to (LL:21)
+  |
+  |  Number of Conditions: 3
+  |     Condition C1 --> (LL:8)
+  |     Condition C2 --> (LL:14)
+  |     Condition C3 --> (LL:19)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2, C3    Result
+  |  1 { F,  -,  -  = F      }
+  |  2 { T,  F,  F  = F      }
+  |  3 { T,  T,  -  = T      }
+  |  4 { T,  F,  T  = T      }
+  |
+  |  C1-Pair: covered: (1,3)
+  |  C2-Pair: covered: (2,3)
+  |  C3-Pair: covered: (2,4)
+  |  MC/DC Coverage for Decision: 100.00%
+  |
+  ------------------
+   LL|      2|        say("pass");
+   LL|      2|    } else {
+   LL|      2|        say("reject");
+   LL|      2|    }
+   LL|      4|}
+   LL|       |
+   LL|      4|fn mcdc_check_not_tree_decision(a: bool, b: bool, c: bool) {
+   LL|      4|    // Contradict to `mcdc_check_tree_decision`,
+   LL|      4|    // 100% branch coverage of this expression does not mean indicates 100% mcdc coverage.
+   LL|      4|    if (a || b) && c {
+                           ^1
+  ------------------
+  |---> MC/DC Decision Region (LL:8) to (LL:21)
+  |
+  |  Number of Conditions: 3
+  |     Condition C1 --> (LL:9)
+  |     Condition C2 --> (LL:14)
+  |     Condition C3 --> (LL:20)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2, C3    Result
+  |  1 { T,  -,  F  = F      }
+  |  2 { T,  -,  T  = T      }
+  |  3 { F,  T,  T  = T      }
+  |
+  |  C1-Pair: not covered
+  |  C2-Pair: not covered
+  |  C3-Pair: covered: (1,2)
+  |  MC/DC Coverage for Decision: 33.33%
+  |
+  ------------------
+   LL|      2|        say("pass");
+   LL|      2|    } else {
+   LL|      2|        say("reject");
+   LL|      2|    }
+   LL|      4|}
+   LL|       |
+   LL|      3|fn mcdc_nested_if(a: bool, b: bool, c: bool) {
+   LL|      3|    if a || b {
+                          ^0
+  ------------------
+  |---> MC/DC Decision Region (LL:8) to (LL:14)
+  |
+  |  Number of Conditions: 2
+  |     Condition C1 --> (LL:8)
+  |     Condition C2 --> (LL:13)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2    Result
+  |  1 { T,  -  = T      }
+  |
+  |  C1-Pair: not covered
+  |  C2-Pair: not covered
+  |  MC/DC Coverage for Decision: 0.00%
+  |
+  ------------------
+   LL|      3|        say("a or b");
+   LL|      3|        if b && c {
+                              ^2
+  ------------------
+  |---> MC/DC Decision Region (LL:12) to (LL:18)
+  |
+  |  Number of Conditions: 2
+  |     Condition C1 --> (LL:12)
+  |     Condition C2 --> (LL:17)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2    Result
+  |  1 { F,  -  = F      }
+  |  2 { T,  F  = F      }
+  |  3 { T,  T  = T      }
+  |
+  |  C1-Pair: covered: (1,3)
+  |  C2-Pair: covered: (2,3)
+  |  MC/DC Coverage for Decision: 100.00%
+  |
+  ------------------
+   LL|      1|            say("b and c");
+   LL|      2|        }
+   LL|      0|    } else {
+   LL|      0|        say("neither a nor b");
+   LL|      0|    }
+   LL|      3|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    mcdc_check_neither(false, false);
+   LL|       |    mcdc_check_neither(false, true);
+   LL|       |
+   LL|       |    mcdc_check_a(true, true);
+   LL|       |    mcdc_check_a(false, true);
+   LL|       |
+   LL|       |    mcdc_check_b(true, true);
+   LL|       |    mcdc_check_b(true, false);
+   LL|       |
+   LL|       |    mcdc_check_both(false, true);
+   LL|       |    mcdc_check_both(true, true);
+   LL|       |    mcdc_check_both(true, false);
+   LL|       |
+   LL|       |    mcdc_check_tree_decision(false, true, true);
+   LL|       |    mcdc_check_tree_decision(true, true, false);
+   LL|       |    mcdc_check_tree_decision(true, false, false);
+   LL|       |    mcdc_check_tree_decision(true, false, true);
+   LL|       |
+   LL|       |    mcdc_check_not_tree_decision(false, true, true);
+   LL|       |    mcdc_check_not_tree_decision(true, true, false);
+   LL|       |    mcdc_check_not_tree_decision(true, false, false);
+   LL|       |    mcdc_check_not_tree_decision(true, false, true);
+   LL|       |
+   LL|       |    mcdc_nested_if(true, false, true);
+   LL|       |    mcdc_nested_if(true, true, true);
+   LL|       |    mcdc_nested_if(true, true, false);
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn say(message: &str) {
+   LL|       |    core::hint::black_box(message);
+   LL|       |}
+
diff --git a/tests/coverage/mcdc_if.rs b/tests/coverage/mcdc_if.rs
new file mode 100644
index 0000000..a858437
--- /dev/null
+++ b/tests/coverage/mcdc_if.rs
@@ -0,0 +1,103 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+//@ min-llvm-version: 18
+//@ compile-flags: -Zcoverage-options=mcdc
+//@ llvm-cov-flags: --show-mcdc
+
+fn mcdc_check_neither(a: bool, b: bool) {
+    if a && b {
+        say("a and b");
+    } else {
+        say("not both");
+    }
+}
+
+fn mcdc_check_a(a: bool, b: bool) {
+    if a && b {
+        say("a and b");
+    } else {
+        say("not both");
+    }
+}
+
+fn mcdc_check_b(a: bool, b: bool) {
+    if a && b {
+        say("a and b");
+    } else {
+        say("not both");
+    }
+}
+
+fn mcdc_check_both(a: bool, b: bool) {
+    if a && b {
+        say("a and b");
+    } else {
+        say("not both");
+    }
+}
+
+fn mcdc_check_tree_decision(a: bool, b: bool, c: bool) {
+    // This expression is intentionally written in a way
+    // where 100% branch coverage indicates 100% mcdc coverage.
+    if a && (b || c) {
+        say("pass");
+    } else {
+        say("reject");
+    }
+}
+
+fn mcdc_check_not_tree_decision(a: bool, b: bool, c: bool) {
+    // Contradict to `mcdc_check_tree_decision`,
+    // 100% branch coverage of this expression does not mean indicates 100% mcdc coverage.
+    if (a || b) && c {
+        say("pass");
+    } else {
+        say("reject");
+    }
+}
+
+fn mcdc_nested_if(a: bool, b: bool, c: bool) {
+    if a || b {
+        say("a or b");
+        if b && c {
+            say("b and c");
+        }
+    } else {
+        say("neither a nor b");
+    }
+}
+
+#[coverage(off)]
+fn main() {
+    mcdc_check_neither(false, false);
+    mcdc_check_neither(false, true);
+
+    mcdc_check_a(true, true);
+    mcdc_check_a(false, true);
+
+    mcdc_check_b(true, true);
+    mcdc_check_b(true, false);
+
+    mcdc_check_both(false, true);
+    mcdc_check_both(true, true);
+    mcdc_check_both(true, false);
+
+    mcdc_check_tree_decision(false, true, true);
+    mcdc_check_tree_decision(true, true, false);
+    mcdc_check_tree_decision(true, false, false);
+    mcdc_check_tree_decision(true, false, true);
+
+    mcdc_check_not_tree_decision(false, true, true);
+    mcdc_check_not_tree_decision(true, true, false);
+    mcdc_check_not_tree_decision(true, false, false);
+    mcdc_check_not_tree_decision(true, false, true);
+
+    mcdc_nested_if(true, false, true);
+    mcdc_nested_if(true, true, true);
+    mcdc_nested_if(true, true, false);
+}
+
+#[coverage(off)]
+fn say(message: &str) {
+    core::hint::black_box(message);
+}
diff --git a/tests/crashes/100618.rs b/tests/crashes/100618.rs
new file mode 100644
index 0000000..911c409
--- /dev/null
+++ b/tests/crashes/100618.rs
@@ -0,0 +1,12 @@
+//@ known-bug: #100618
+//@ compile-flags: -Cdebuginfo=2
+
+//@ only-x86_64
+enum Foo<T: 'static> {
+    Value(T),
+    Recursive(&'static Foo<Option<T>>),
+}
+
+fn main() {
+    let _x = Foo::Value(());
+}
diff --git a/tests/crashes/103708.rs b/tests/crashes/103708.rs
new file mode 100644
index 0000000..f315427
--- /dev/null
+++ b/tests/crashes/103708.rs
@@ -0,0 +1,16 @@
+//@ known-bug: #103708
+#![feature(min_specialization)]
+
+trait MySpecTrait {
+    fn f();
+}
+
+impl<'a, T: ?Sized> MySpecTrait for T {
+    default fn f() {}
+}
+
+impl<'a, T: ?Sized> MySpecTrait for &'a T {
+    fn f() {}
+}
+
+fn main() {}
diff --git a/tests/crashes/104685.rs b/tests/crashes/104685.rs
new file mode 100644
index 0000000..25ddbb2
--- /dev/null
+++ b/tests/crashes/104685.rs
@@ -0,0 +1,15 @@
+//@ known-bug: #104685
+//@ compile-flags: -Zextra-const-ub-checks
+#![feature(extern_types)]
+
+extern {
+    pub type ExternType;
+}
+
+extern "C" {
+    pub static EXTERN: ExternType;
+}
+
+pub static EMPTY: () = unsafe { &EXTERN; };
+
+fn main() {}
diff --git a/tests/crashes/105249.rs b/tests/crashes/105249.rs
new file mode 100644
index 0000000..592ed5b
--- /dev/null
+++ b/tests/crashes/105249.rs
@@ -0,0 +1,16 @@
+//@ known-bug: #105249
+//@ compile-flags: -Zpolymorphize=on
+
+trait Foo<T> {
+    fn print<'a>(&'a self) where T: 'a { println!("foo"); }
+}
+
+impl<'a> Foo<&'a ()> for () { }
+
+trait Bar: for<'a> Foo<&'a ()> { }
+
+impl Bar for () {}
+
+fn main() {
+    (&() as &dyn Bar).print(); // Segfault
+}
diff --git a/tests/crashes/105299.rs b/tests/crashes/105299.rs
new file mode 100644
index 0000000..8e3aafa4
--- /dev/null
+++ b/tests/crashes/105299.rs
@@ -0,0 +1,19 @@
+//@ known-bug: #105299
+
+pub trait Foo: Clone {}
+
+pub struct Bar<'a, T: Clone> {
+    pub cow: std::borrow::Cow<'a, [T]>,
+
+    pub THIS_CAUSES_ICE: (), // #1
+}
+
+impl<T> Bar<'_, T>
+where
+    T: Clone,
+    [T]: Foo,
+{
+    pub fn MOVES_SELF(self) {} // #2
+}
+
+pub fn main() {}
diff --git a/tests/crashes/107362.rs b/tests/crashes/107362.rs
new file mode 100644
index 0000000..8d55d61
--- /dev/null
+++ b/tests/crashes/107362.rs
@@ -0,0 +1,43 @@
+//@ known-bug: #107362
+//@ compile-flags: -Cdebuginfo=2
+
+pub trait Functor
+{
+    type With<T>: Functor;
+}
+
+pub struct IdFunctor<T>(T);
+impl<T> Functor for IdFunctor<T> {
+    type With<T2> = IdFunctor<T2>;
+}
+
+impl<T> Functor for Vec<T> {
+    type With<T2> = Vec<T2> ;
+}
+
+
+pub struct Compose<F1, F2, T>(F1::With<F2::With<T>>)
+where
+    F1: Functor + ?Sized,
+    F2: Functor + ?Sized;
+
+impl<F1, F2, T> Functor for Compose<F1, F2, T>
+where
+    F1: Functor + ?Sized,
+    F2: Functor + ?Sized
+{
+    type With<T2> = F1::With<F2::With<T2>> ;
+}
+
+pub enum Value<F>
+where
+    F: Functor + ?Sized,
+{
+    SignedInt(*mut F::With<i64>),
+    Array(*mut Value<Compose<F, Vec<()>, ()>>),
+
+}
+
+fn main() {
+    let x: Value<IdFunctor<()>> = Value::SignedInt(&mut IdFunctor(1));
+}
diff --git a/tests/crashes/108499.rs b/tests/crashes/108499.rs
new file mode 100644
index 0000000..4a0638c
--- /dev/null
+++ b/tests/crashes/108499.rs
@@ -0,0 +1,44 @@
+//@ known-bug: #108499
+
+// at lower recursion limits the recursion limit is reached before the bug happens
+#![recursion_limit = "2000"]
+
+// this will try to calculate 3↑↑3=3^(3^3)
+type Test = <() as Op<((), ()), [[[(); 0]; 0]; 0], [[[(); 0]; 0]; 0],
+    [[[[(); 0]; 0]; 0]; 0]>>::Result;
+
+use std::default::Default;
+
+fn main() {
+    // force the compiler to actually evaluate `Test`
+    println!("{}", Test::default());
+}
+
+trait Op<X, A, B, C> {
+    type Result;
+}
+
+// this recursive function defines the hyperoperation sequence,
+// a canonical example of the type of recursion which produces the issue
+// the problem seems to be caused by having two recursive calls, the second
+// of which depending on the first
+impl<
+    X: Op<(X, Y), A, [B; 0], [C; 0]>,
+    Y: Op<(X, Y), A, X::Result, C>,
+    A, B, C,
+> Op<(X, Y), A, [[B; 0]; 0], [C; 0]> for () {
+    type Result = Y::Result;
+}
+
+// base cases
+impl<X, A, B> Op<X, A, B, ()> for () {
+    type Result = [B; 0];
+}
+
+impl<X, A> Op<X, A, [(); 0], [(); 0]> for () {
+    type Result = [A; 0];
+}
+
+impl<X, A, C> Op<X, A, [(); 0], [[C; 0]; 0]> for () {
+    type Result = A;
+}
diff --git a/tests/crashes/112623.rs b/tests/crashes/112623.rs
new file mode 100644
index 0000000..fc7361b
--- /dev/null
+++ b/tests/crashes/112623.rs
@@ -0,0 +1,26 @@
+//@ known-bug: #112623
+
+#![feature(const_trait_impl, effects)]
+
+#[const_trait]
+trait Value {
+    fn value() -> u32;
+}
+
+const fn get_value<T: ~const Value>() -> u32 {
+    T::value()
+}
+
+struct FortyTwo;
+
+impl const Value for FortyTwo {
+    fn value() -> i64 {
+        42
+    }
+}
+
+const FORTY_TWO: u32 = get_value::<FortyTwo>();
+
+fn main() {
+    assert_eq!(FORTY_TWO, 42);
+}
diff --git a/tests/crashes/114198-2.rs b/tests/crashes/114198-2.rs
new file mode 100644
index 0000000..de9d61a
--- /dev/null
+++ b/tests/crashes/114198-2.rs
@@ -0,0 +1,15 @@
+//@ known-bug: #114198
+//@ compile-flags: -Zprint-mono-items=eager
+
+impl Trait for <Ty as Owner>::Struct {}
+trait Trait {
+    fn test(&self) {}
+}
+
+enum Ty {}
+trait Owner { type Struct: ?Sized; }
+impl Owner for Ty {
+    type Struct = dyn Trait + Send;
+}
+
+fn main() {}
diff --git a/tests/crashes/114198.rs b/tests/crashes/114198.rs
new file mode 100644
index 0000000..1ec8cdd
--- /dev/null
+++ b/tests/crashes/114198.rs
@@ -0,0 +1,13 @@
+//@ known-bug: #114198
+//@ compile-flags: -Zprint-mono-items=eager
+
+#![feature(lazy_type_alias)]
+
+impl Trait for Struct {}
+trait Trait {
+    fn test(&self) {}
+}
+
+type Struct = dyn Trait + Send;
+
+fn main() {}
diff --git a/tests/crashes/114920.rs b/tests/crashes/114920.rs
new file mode 100644
index 0000000..9aa7598
--- /dev/null
+++ b/tests/crashes/114920.rs
@@ -0,0 +1,2 @@
+//@ known-bug: #114920
+#![core::prelude::v1::test]
diff --git a/tests/crashes/115994.rs b/tests/crashes/115994.rs
new file mode 100644
index 0000000..23d1507
--- /dev/null
+++ b/tests/crashes/115994.rs
@@ -0,0 +1,17 @@
+//@ known-bug: #115994
+//@ compile-flags: -Cdebuginfo=2 --crate-type lib
+
+// To prevent "overflow while adding drop-check rules".
+use std::mem::ManuallyDrop;
+
+pub enum Foo<U> {
+    Leaf(U),
+
+    Branch(BoxedFoo<BoxedFoo<U>>),
+}
+
+pub type BoxedFoo<U> = ManuallyDrop<Box<Foo<U>>>;
+
+pub fn test() -> Foo<usize> {
+    todo!()
+}
diff --git a/tests/crashes/116721.rs b/tests/crashes/116721.rs
new file mode 100644
index 0000000..fc1a653
--- /dev/null
+++ b/tests/crashes/116721.rs
@@ -0,0 +1,9 @@
+//@ known-bug: #116721
+//@ compile-flags: -Zmir-opt-level=3 --emit=mir
+fn hey<T>(it: &[T])
+where
+    [T]: Clone,
+{
+}
+
+fn main() {}
diff --git a/tests/crashes/118185-2.rs b/tests/crashes/118185-2.rs
new file mode 100644
index 0000000..c3a29c3
--- /dev/null
+++ b/tests/crashes/118185-2.rs
@@ -0,0 +1,26 @@
+//@ known-bug: #118185
+
+fn main() {
+    let target: Target = create_target();
+    target.get(0); // correct arguments work
+    target.get(10.0); // CRASH HERE
+}
+
+// must be generic
+fn create_target<T>() -> T {
+    unimplemented!()
+}
+
+// unimplemented trait, but contains function with the same name
+pub trait RandomTrait {
+    fn get(&mut self); // but less arguments
+}
+
+struct Target;
+
+impl Target {
+    // correct function with arguments
+    pub fn get(&self, data: i32) {
+        unimplemented!()
+    }
+}
diff --git a/tests/crashes/118185.rs b/tests/crashes/118185.rs
new file mode 100644
index 0000000..c3a29c3
--- /dev/null
+++ b/tests/crashes/118185.rs
@@ -0,0 +1,26 @@
+//@ known-bug: #118185
+
+fn main() {
+    let target: Target = create_target();
+    target.get(0); // correct arguments work
+    target.get(10.0); // CRASH HERE
+}
+
+// must be generic
+fn create_target<T>() -> T {
+    unimplemented!()
+}
+
+// unimplemented trait, but contains function with the same name
+pub trait RandomTrait {
+    fn get(&mut self); // but less arguments
+}
+
+struct Target;
+
+impl Target {
+    // correct function with arguments
+    pub fn get(&self, data: i32) {
+        unimplemented!()
+    }
+}
diff --git a/tests/crashes/118244.rs b/tests/crashes/118244.rs
new file mode 100644
index 0000000..bfa1f5b
--- /dev/null
+++ b/tests/crashes/118244.rs
@@ -0,0 +1,22 @@
+//@ known-bug: #118244
+//@ compile-flags: -Cdebuginfo=2
+
+#![allow(incomplete_features)]
+#![feature(generic_const_exprs)]
+struct Inner<const N: usize, const M: usize>;
+impl<const N: usize, const M: usize> Inner<N, M> where [(); N + M]: {
+    fn i() -> Self {
+        Self
+    }
+}
+
+struct Outer<const A: usize, const B: usize>(Inner<A, { B * 2 }>) where [(); A + (B * 2)]:;
+impl<const A: usize, const B: usize> Outer<A, B> where [(); A + (B * 2)]: {
+    fn o() -> Self {
+        Self(Inner::i())
+    }
+}
+
+fn main() {
+    Outer::<1, 1>::o();
+}
diff --git a/tests/crashes/118545.rs b/tests/crashes/118545.rs
new file mode 100644
index 0000000..1d4bb84
--- /dev/null
+++ b/tests/crashes/118545.rs
@@ -0,0 +1,8 @@
+//@ known-bug: #118545
+#![feature(generic_const_exprs)]
+
+struct Checked<const F: fn()>;
+
+fn foo() {}
+const _: Checked<foo> = Checked::<foo>;
+pub fn main() {}
diff --git a/tests/crashes/118590.rs b/tests/crashes/118590.rs
new file mode 100644
index 0000000..829c165
--- /dev/null
+++ b/tests/crashes/118590.rs
@@ -0,0 +1,11 @@
+//@ known-bug: #118590
+
+fn main() {
+    recurse(std::iter::empty::<()>())
+}
+
+fn recurse(nums: impl Iterator) {
+    if true { return }
+
+    recurse(nums.skip(42).peekable())
+}
diff --git a/tests/crashes/119381.rs b/tests/crashes/119381.rs
new file mode 100644
index 0000000..51d1d08
--- /dev/null
+++ b/tests/crashes/119381.rs
@@ -0,0 +1,6 @@
+//@ known-bug: #119381
+
+#![feature(with_negative_coherence)]
+trait Trait {}
+impl<const N: u8> Trait for [(); N] {}
+impl<const N: i8> Trait for [(); N] {}
diff --git a/tests/crashes/120033.rs b/tests/crashes/120033.rs
new file mode 100644
index 0000000..f150297
--- /dev/null
+++ b/tests/crashes/120033.rs
@@ -0,0 +1,14 @@
+//@ known-bug: #120033
+#![feature(non_lifetime_binders)]
+
+pub trait Foo<T: ?Sized> {
+    type Bar<K: ?Sized>;
+}
+
+pub struct Bar<T: ?AutoTrait> {}
+
+pub fn f<T1, T2>()
+where
+    T1: for<T> Foo<usize, Bar = Bar<T>>,
+    T2: for<L, T> Foo<usize, Bar<T> = T1::Bar<T>>,
+{}
diff --git a/tests/crashes/120254.rs b/tests/crashes/120254.rs
new file mode 100644
index 0000000..ea68523
--- /dev/null
+++ b/tests/crashes/120254.rs
@@ -0,0 +1,24 @@
+//@ known-bug: #120254
+
+trait Dbg {}
+
+struct Foo<I, E> {
+    input: I,
+    errors: E,
+}
+
+trait Bar: Offset<<Self as Bar>::Checkpoint> {
+    type Checkpoint;
+}
+
+impl<I: Bar, E: Dbg> Bar for Foo<I, E> {
+    type Checkpoint = I::Checkpoint;
+}
+
+trait Offset<Start = Self> {}
+
+impl<I: Bar, E: Dbg> Offset<<Foo<I, E> as Bar>::Checkpoint> for Foo<I, E> {}
+
+impl<I: Bar, E> Foo<I, E> {
+    fn record_err(self, _: <Self as Bar>::Checkpoint) -> () {}
+}
diff --git a/tests/crashes/120421.rs b/tests/crashes/120421.rs
new file mode 100644
index 0000000..b6059f3
--- /dev/null
+++ b/tests/crashes/120421.rs
@@ -0,0 +1,12 @@
+//@ known-bug: #120421
+//@ compile-flags: -Zlint-mir
+
+#![feature(never_patterns)]
+
+enum Void {}
+
+fn main() {
+    let res_void: Result<bool, Void> = Ok(true);
+
+    for (Ok(mut _x) | Err(!)) in [res_void] {}
+}
diff --git a/tests/crashes/120600-2.rs b/tests/crashes/120600-2.rs
deleted file mode 100644
index aa1785e..0000000
--- a/tests/crashes/120600-2.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//@ known-bug: #120600
-#![feature(never_type, never_type_fallback)]
-
-enum E { Bar(!) }
-
-fn f(a: &E, b: &E) {
-    match (a, b) {
-        (E::Bar(a), E::Bar(b)) => { *a == *b; }
-        _ => {}
-    }
-}
-
-pub fn main() {}
diff --git a/tests/crashes/120600.rs b/tests/crashes/120600.rs
deleted file mode 100644
index 1be51a8..0000000
--- a/tests/crashes/120600.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@ known-bug: #120600
-#![feature(never_type)]
-#![feature(never_type_fallback)]
-
-#[derive(Ord, Eq, PartialOrd, PartialEq)]
-enum E {
-    Foo,
-    Bar(!, i32, i32),
-}
-
-fn main() {}
diff --git a/tests/crashes/120792.rs b/tests/crashes/120792.rs
new file mode 100644
index 0000000..5188925
--- /dev/null
+++ b/tests/crashes/120792.rs
@@ -0,0 +1,25 @@
+//@ known-bug: #120792
+//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes
+
+impl Trait<()> for () {
+    fn foo<'a, K>(self, _: (), _: K) {
+        todo!();
+    }
+}
+
+trait Foo<T> {}
+
+impl<F, T> Foo<T> for F {
+    fn main() {
+        ().foo((), ());
+    }
+}
+
+trait Trait<T> {
+    fn foo<'a, K>(self, _: T, _: K)
+    where
+        T: 'a,
+        K: 'a;
+}
+
+pub fn main() {}
diff --git a/tests/crashes/120811.rs b/tests/crashes/120811.rs
new file mode 100644
index 0000000..6b368fe
--- /dev/null
+++ b/tests/crashes/120811.rs
@@ -0,0 +1,27 @@
+//@ known-bug: #120811
+
+trait Container {
+    type Item<'a>;
+}
+impl Container for () {
+    type Item<'a> = ();
+}
+struct Exchange<C, F> {
+    _marker: std::marker::PhantomData<(C, F)>,
+}
+fn exchange<C, F>(_: F) -> Exchange<C, F>
+where
+    C: Container,
+    for<'a> F: FnMut(&C::Item<'a>),
+{
+    unimplemented!()
+}
+trait Parallelization<C> {}
+impl<C, F> Parallelization<C> for Exchange<C, F> {}
+fn unary_frontier<P: Parallelization<()>>(_: P) {}
+fn main() {
+    let exchange = exchange(|_| ());
+    let _ = || {
+        unary_frontier(exchange);
+    };
+}
diff --git a/tests/crashes/121063.rs b/tests/crashes/121063.rs
new file mode 100644
index 0000000..cb9db28
--- /dev/null
+++ b/tests/crashes/121063.rs
@@ -0,0 +1,20 @@
+//@ known-bug: #121063
+//@ compile-flags: -Zpolymorphize=on --edition=2021 -Zinline-mir=yes
+
+use std::{
+    fmt, ops,
+    path::{Component, Path, PathBuf},
+};
+
+pub struct AbsPathBuf(PathBuf);
+
+impl TryFrom<PathBuf> for AbsPathBuf {
+    type Error = PathBuf;
+    fn try_from(path: impl AsRef<Path>) -> Result<AbsPathBuf, PathBuf> {}
+}
+
+impl TryFrom<&str> for AbsPathBuf {
+    fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> {
+        AbsPathBuf::try_from(PathBuf::from(path))
+    }
+}
diff --git a/tests/crashes/121127.rs b/tests/crashes/121127.rs
new file mode 100644
index 0000000..2e64bf6
--- /dev/null
+++ b/tests/crashes/121127.rs
@@ -0,0 +1,23 @@
+//@ known-bug: #121127
+//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes -C debuginfo=2
+// Note that as of PR#123949 this only crashes with debuginfo enabled
+
+#![feature(specialization)]
+
+pub trait Foo {
+    fn abc() -> u32;
+}
+
+pub trait Marker {}
+
+impl<T> Foo for T {
+    default fn abc(f: fn(&T), t: &T) -> u32 {
+        16
+    }
+}
+
+impl<T: Marker> Foo for T {
+    fn def() -> u32 {
+        Self::abc()
+    }
+}
diff --git a/tests/crashes/121363.rs b/tests/crashes/121363.rs
new file mode 100644
index 0000000..2a5b627
--- /dev/null
+++ b/tests/crashes/121363.rs
@@ -0,0 +1,9 @@
+//@ known-bug: #121363
+//@ compile-flags: -Zmir-opt-level=5 --crate-type lib
+
+#![feature(trivial_bounds)]
+
+#[derive(Debug)]
+struct TwoStrs(str, str)
+where
+    str: Sized;
diff --git a/tests/crashes/121538.rs b/tests/crashes/121538.rs
new file mode 100644
index 0000000..f18bad8
--- /dev/null
+++ b/tests/crashes/121538.rs
@@ -0,0 +1,30 @@
+//@ known-bug: #121538
+//@ compile-flags: -Cdebuginfo=2
+
+use std::marker::PhantomData;
+
+struct Digit<T> {
+    elem: T
+}
+
+struct Node<T:'static> { m: PhantomData<&'static T> }
+
+enum FingerTree<T:'static> {
+    Single(T),
+
+    Deep(
+        Digit<T>,
+        Node<FingerTree<Node<T>>>,
+        )
+}
+
+enum Wrapper<T:'static> {
+    Simple,
+    Other(FingerTree<T>),
+}
+
+fn main() {
+    let w =
+        Some(Wrapper::Simple::<u32>);
+
+}
diff --git a/tests/crashes/122259.rs b/tests/crashes/122259.rs
new file mode 100644
index 0000000..5ac8063
--- /dev/null
+++ b/tests/crashes/122259.rs
@@ -0,0 +1,12 @@
+//@ known-bug: #122259
+
+#![feature(unsized_fn_params)]
+
+#[derive(Copy, Clone)]
+struct Target(str);
+
+fn w(t: &Target) {
+    x(*t);
+}
+
+fn x(t: Target) {}
diff --git a/tests/crashes/122548.rs b/tests/crashes/122548.rs
deleted file mode 100644
index 232ce5d..0000000
--- a/tests/crashes/122548.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-//@ known-bug: #122548
-#![feature(const_mut_refs)]
-#![feature(const_refs_to_static)]
-
-use std::cell::UnsafeCell;
-
-struct Meh {
-    x: &'static UnsafeCell<i32>,
-}
-
-const MUH: Meh = Meh {
-    x: &mut *(&READONLY as *const _ as *mut _),
-};
-
-static READONLY: i32 = 0;
-
-pub fn main() {}
diff --git a/tests/crashes/122630.rs b/tests/crashes/122630.rs
new file mode 100644
index 0000000..e666244
--- /dev/null
+++ b/tests/crashes/122630.rs
@@ -0,0 +1,22 @@
+//@ known-bug: #122630
+//@ compile-flags: -Zvalidate-mir
+
+use std::ops::Coroutine;
+
+const FOO_SIZE: usize = 1024;
+struct Foo([u8; FOO_SIZE]);
+
+impl Drop for Foo {
+    fn move_before_yield_with_noop() -> impl Coroutine<Yield = ()> {}
+}
+
+fn overlap_move_points() -> impl Coroutine<Yield = ()> {
+    static || {
+        let first = Foo([0; FOO_SIZE]);
+        yield;
+        let second = first;
+        yield;
+        let second = first;
+        yield;
+    }
+}
diff --git a/tests/crashes/123456.rs b/tests/crashes/123456.rs
new file mode 100644
index 0000000..ed7cbad
--- /dev/null
+++ b/tests/crashes/123456.rs
@@ -0,0 +1,16 @@
+//@ known-bug: #123456
+
+trait Project {
+    const SELF: Self;
+}
+
+fn take1(
+    _: Project<
+        SELF = {
+                   j2.join().unwrap();
+               },
+    >,
+) {
+}
+
+pub fn main() {}
diff --git a/tests/crashes/123461.rs b/tests/crashes/123461.rs
new file mode 100644
index 0000000..6dbfb5c
--- /dev/null
+++ b/tests/crashes/123461.rs
@@ -0,0 +1,5 @@
+//@ known-bug: #123461
+
+fn main() {
+    let _: [_; unsafe { std::mem::transmute(|o_b: Option<_>| {}) }];
+}
diff --git a/tests/crashes/123664.rs b/tests/crashes/123664.rs
new file mode 100644
index 0000000..80c415f
--- /dev/null
+++ b/tests/crashes/123664.rs
@@ -0,0 +1,4 @@
+//@ known-bug: #123664
+#![feature(generic_const_exprs, effects)]
+const fn with_positive<F: ~const Fn()>() {}
+pub fn main() {}
diff --git a/tests/crashes/123690.rs b/tests/crashes/123690.rs
new file mode 100644
index 0000000..3af70e2
--- /dev/null
+++ b/tests/crashes/123690.rs
@@ -0,0 +1,278 @@
+//@ known-bug: #123690
+
+fn more_discriminant_overflow() {
+    pub enum Infallible {}
+
+    pub enum E1 {
+        V2 {},
+        V3,
+        V4,
+    }
+
+    #[repr(u8)]
+
+    pub enum E2<X> {
+        V1 { f: bool },
+
+        /*_00*/ _01(X),
+        _02(X),
+        _03(X),
+        _04(X),
+        _05(X),
+        _06(X),
+        _07(X),
+        _08(X),
+        _09(X),
+        _0A(X),
+        _0B(X),
+        _0C(X),
+        _0D(X),
+        _0E(X),
+        _0F(X),
+        _10(X),
+        _11(X),
+        _12(X),
+        _13(X),
+        _14(X),
+        _15(X),
+        _16(X),
+        _17(X),
+        _18(X),
+        _19(X),
+        _1A(X),
+        _1B(X),
+        _1C(X),
+        _1D(X),
+        _1E(X),
+        _1F(X),
+        _20(X),
+        _21(X),
+        _22(X),
+        _23(X),
+        _24(X),
+        _25(X),
+        _26(X),
+        _27(X),
+        _28(X),
+        _29(X),
+        _2A(X),
+        _2B(X),
+        _2C(X),
+        _2D(X),
+        _2E(X),
+        _2F(X),
+        _30(X),
+        _31(X),
+        _32(X),
+        _33(X),
+        _34(X),
+        _35(X),
+        _36(X),
+        _37(X),
+        _38(X),
+        _39(X),
+        _3A(X),
+        _3B(X),
+        _3C(X),
+        _3D(X),
+        _3E(X),
+        _3F(X),
+        _40(X),
+        _41(X),
+        _42(X),
+        _43(X),
+        _44(X),
+        _45(X),
+        _46(X),
+        _47(X),
+        _48(X),
+        _49(X),
+        _4A(X),
+        _4B(X),
+        _4C(X),
+        _4D(X),
+        _4E(X),
+        _4F(X),
+        _50(X),
+        _51(X),
+        _52(X),
+        _53(X),
+        _54(X),
+        _55(X),
+        _56(X),
+        _57(X),
+        _58(X),
+        _59(X),
+        _5A(X),
+        _5B(X),
+        _5C(X),
+        _5D(X),
+        _5E(X),
+        _5F(X),
+        _60(X),
+        _61(X),
+        _62(X),
+        _63(X),
+        _64(X),
+        _65(X),
+        _66(X),
+        _67(X),
+        _68(X),
+        _69(X),
+        _6A(X),
+        _6B(X),
+        _6C(X),
+        _6D(X),
+        _6E(X),
+        _6F(X),
+        _70(X),
+        _71(X),
+        _72(X),
+        _73(X),
+        _74(E1),
+        _75(X),
+        _76(X),
+        _77(X),
+        _78(X),
+        _79(X),
+        _7A(X),
+        _7B(X),
+        _7C(X),
+        _7D(X),
+        _7E(X),
+        _7F(X),
+        _80(X),
+        _81(X),
+        _82(X),
+        _83(X),
+        _84(X),
+        _85(X),
+        _86(X),
+        _87(X),
+        _88(X),
+        _89(X),
+        _8A(X),
+        _8B(X),
+        _8C(X),
+        _8D(X),
+        _8E(X),
+        _8F(X),
+        _90(X),
+        _91(X),
+        _92(X),
+        _93(X),
+        _94(X),
+        _95(X),
+        _96(X),
+        _97(X),
+        _98(X),
+        _99(X),
+        _9A(X),
+        _9B(X),
+        _9C(X),
+        _9D(X),
+        _9E(X),
+        _9F(X),
+        _A0(X),
+        _A1(X),
+        _A2(X),
+        _A3(X),
+        _A4(X),
+        _A5(X),
+        _A6(X),
+        _A7(X),
+        _A8(X),
+        _A9(X),
+        _AA(X),
+        _AB(X),
+        _AC(X),
+        _AD(X),
+        _AE(X),
+        _AF(X),
+        _B0(X),
+        _B1(X),
+        _B2(X),
+        _B3(X),
+        _B4(X),
+        _B5(X),
+        _B6(X),
+        _B7(X),
+        _B8(X),
+        _B9(X),
+        _BA(X),
+        _BB(X),
+        _BC(X),
+        _BD(X),
+        _BE(X),
+        _BF(X),
+        _C0(X),
+        _C1(X),
+        _C2(X),
+        _C3(X),
+        _C4(X),
+        _C5(X),
+        _C6(X),
+        _D8(X),
+        _C8(X),
+        _C9(X),
+        _CA(X),
+        _CB(X),
+        _CC(X),
+        _CD(X),
+        _CE(X),
+        _CF(X),
+        _D0(X),
+        _D1(X),
+        _D2(X),
+        _D3(X),
+        _D4(X),
+        _D5(X),
+        _D6(X),
+        _D7(X),
+        _D8(X),
+        _D9(X),
+        _DA(X),
+        _DB(X),
+        _DC(X),
+        _DD(X),
+        _DE(X),
+        _DF(X),
+        _E0(X),
+        _E1(X),
+        _E2(X),
+        _E3(X),
+        _E4(X),
+        _E5(X),
+        _E6(X),
+        _E7(X),
+        _E8(X),
+        _E9(X),
+        _EA(X),
+        _EB(X),
+        _EC(X),
+        _ED(X),
+        _EE(X),
+        _EF(i32, i32),
+        _F0(X),
+        _F1(X),
+        _F2(X),
+        _F3(X),
+        _F4(X),
+        _F5(X),
+        _F6(X),
+        _F7(X),
+        _F8(X),
+        _F9(X),
+        _FA(X),
+        _FB(X),
+        _FC(X),
+        _FD(X),
+        _FE(X),
+        _FF(X),
+
+        V3,
+        V4,
+    }
+
+    if let E2::V1 { .. } = E2::V3::<Infallible> {}
+}
diff --git a/tests/crashes/123693.rs b/tests/crashes/123693.rs
new file mode 100644
index 0000000..c2e1920
--- /dev/null
+++ b/tests/crashes/123693.rs
@@ -0,0 +1,22 @@
+//@ known-bug: #123693
+
+#![feature(transmutability)]
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, { Assume::NOTHING }>,
+    {
+    }
+}
+
+enum Lopsided {
+    Smol(()),
+    Lorg(bool),
+}
+
+fn should_pad_variants() {
+    assert::is_transmutable::<Lopsided, ()>();
+}
diff --git a/tests/crashes/123710.rs b/tests/crashes/123710.rs
new file mode 100644
index 0000000..f171fa7c
--- /dev/null
+++ b/tests/crashes/123710.rs
@@ -0,0 +1,17 @@
+//@ known-bug: #123710
+
+#[repr(packed)]
+#[repr(u32)]
+enum E {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    union InvalidTag {
+        int: u32,
+        e: E,
+    }
+    let _invalid_tag = InvalidTag { int: 4 };
+}
diff --git a/tests/crashes/123809.rs b/tests/crashes/123809.rs
new file mode 100644
index 0000000..c7a633a
--- /dev/null
+++ b/tests/crashes/123809.rs
@@ -0,0 +1,4 @@
+//@ known-bug: #123809
+type Positive = std::pat::pattern_type!(std::pat:: is 0..);
+
+pub fn main() {}
diff --git a/tests/crashes/123810.rs b/tests/crashes/123810.rs
new file mode 100644
index 0000000..57de3bc
--- /dev/null
+++ b/tests/crashes/123810.rs
@@ -0,0 +1,10 @@
+//@ known-bug: #123810
+//@ compile-flags: -Zlint-mir
+
+fn temp() -> (String, i32) {
+    (String::from("Hello"), 1)
+}
+
+fn main() {
+    let f = if true { &temp() } else { &temp() };
+}
diff --git a/tests/crashes/123863.rs b/tests/crashes/123863.rs
new file mode 100644
index 0000000..e0f3ac9
--- /dev/null
+++ b/tests/crashes/123863.rs
@@ -0,0 +1,6 @@
+//@ known-bug: #123863
+const fn concat_strs<const A: &'static str>() -> &'static str {
+    struct Inner<const A: &'static str>;
+    Inner::concat_strs::<"a">::A
+}
+pub fn main() {}
diff --git a/tests/crashes/123893.rs b/tests/crashes/123893.rs
new file mode 100644
index 0000000..137ae78
--- /dev/null
+++ b/tests/crashes/123893.rs
@@ -0,0 +1,20 @@
+//@ known-bug: #123893
+//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes -Zinline-mir-threshold=20
+pub fn main() {
+    generic_impl::<bool>();
+}
+
+fn generic_impl<T>() {
+    trait MagicTrait {
+        const IS_BIG: bool;
+    }
+    impl<T> MagicTrait for T {
+        const IS_BIG: bool = true;
+    }
+    if T::IS_BIG {
+        big_impl::<i32>();
+    }
+}
+
+#[inline(never)]
+fn big_impl<T>() {}
diff --git a/tests/crashes/123901.rs b/tests/crashes/123901.rs
new file mode 100644
index 0000000..06722f0
--- /dev/null
+++ b/tests/crashes/123901.rs
@@ -0,0 +1,8 @@
+//@ known-bug: #123901
+//@ edition:2021
+
+pub fn test(test: &u64, temp: &u64) {
+    async |check, a, b| {
+        temp.abs_diff(12);
+    };
+}
diff --git a/tests/crashes/123911.rs b/tests/crashes/123911.rs
new file mode 100644
index 0000000..1a4d6a7
--- /dev/null
+++ b/tests/crashes/123911.rs
@@ -0,0 +1,16 @@
+//@ known-bug: #123911
+
+macro_rules! m {
+    ($attr_path: path) => {
+        #[$attr_path]
+        fn f() {}
+    }
+}
+
+m!(inline<{
+    let a = CharCharFloat { a: 1 };
+    let b = rustrt::rust_dbg_abi_4(a);
+    println!("a: {}", b.a);
+}>);
+
+fn main() {}
diff --git a/tests/crashes/123912.rs b/tests/crashes/123912.rs
new file mode 100644
index 0000000..35216ca
--- /dev/null
+++ b/tests/crashes/123912.rs
@@ -0,0 +1,15 @@
+//@ known-bug: #123912
+
+macro_rules! m {
+    ($attr_path: path) => {
+        #[$attr_path]
+        fn f() {}
+    }
+}
+
+m!(inline<{
+    let a = CharCharFloat { a: 1 };
+    println!("a: {}", a);
+}>);
+
+fn main() {}
diff --git a/tests/crashes/123917.rs b/tests/crashes/123917.rs
new file mode 100644
index 0000000..66e7546
--- /dev/null
+++ b/tests/crashes/123917.rs
@@ -0,0 +1,41 @@
+//@ known-bug: #123917
+//@ compile-flags: -Zmir-opt-level=5 -Zpolymorphize=on
+
+use std::marker::PhantomData;
+
+pub struct Id<'id>();
+
+pub struct Item<'life, T> {
+    data: T,
+}
+
+pub struct Token<'life, 'borrow, 'compact, 'reborrow, T>
+where
+    'life: 'reborrow,
+    T: Tokenize,
+{
+    ptr: *mut <T as Tokenize>::Tokenized,
+    ptr: core::ptr::NonNull<T::Tokenized>,
+    _phantom: PhantomData<Id<'life>>,
+}
+
+impl<'life> Arena<'life> {
+    pub fn tokenize<'before, 'compact, 'borrow, 'reborrow, T, U>(
+        item: Item<'life, &'before mut T>,
+    ) -> Token<'life, 'borrow, 'compact, 'reborrow, U>
+    where
+        T: Tokenize<'life, 'borrow, 'compact, 'reborrow, Untokenized = U>,
+        T::Untokenized: Tokenize<'life, 'borrow, 'compact, 'reborrow>,
+    {
+        let dst = item.data as *mut T as *mut T::Tokenized;
+        Token {
+            ptr: core::ptr::NonNull::new(dst as *mut _).unwrap(),
+            _phantom: PhantomData,
+        }
+    }
+}
+
+pub trait Tokenize {
+    type Tokenized;
+    type Untokenized;
+}
diff --git a/tests/crashes/123955.rs b/tests/crashes/123955.rs
new file mode 100644
index 0000000..fdd58c8
--- /dev/null
+++ b/tests/crashes/123955.rs
@@ -0,0 +1,6 @@
+//@ known-bug: #123955
+//@ compile-flags: -Clto -Zvirtual-function-elimination
+//@ only-x86_64
+pub fn main() {
+    _ = Box::new(()) as Box<dyn Send>;
+}
diff --git a/tests/crashes/123959.rs b/tests/crashes/123959.rs
new file mode 100644
index 0000000..fe22c02
--- /dev/null
+++ b/tests/crashes/123959.rs
@@ -0,0 +1,5 @@
+//@ known-bug: #123959
+#![feature(generic_const_exprs)]
+fn foo<T>(_: [(); std::mem::offset_of!((T,), 0)]) {}
+
+pub fn main() {}
diff --git a/tests/crashes/124004.rs b/tests/crashes/124004.rs
new file mode 100644
index 0000000..1fcf078
--- /dev/null
+++ b/tests/crashes/124004.rs
@@ -0,0 +1,18 @@
+//@ known-bug: #124004
+
+#![feature(box_patterns)]
+
+use std::ops::{ Deref };
+
+struct X(dyn Iterator<Item = &'a ()>);
+
+impl Deref for X {
+    type Target = isize;
+
+    fn deref(&self) -> &isize {
+        let &X(box ref x) = self;
+        x
+    }
+}
+
+fn main() {}
diff --git a/tests/crashes/124020.rs b/tests/crashes/124020.rs
new file mode 100644
index 0000000..f461f32
--- /dev/null
+++ b/tests/crashes/124020.rs
@@ -0,0 +1,33 @@
+//@ known-bug: #124020
+//@ compile-flags: -Zpolymorphize=on --edition=2018 --crate-type=lib
+
+#![feature(async_closure, noop_waker, async_fn_traits)]
+
+use std::future::Future;
+use std::pin::pin;
+use std::task::*;
+
+pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
+    let mut fut = pin!(fut);
+    let ctx = &mut Context::from_waker(Waker::noop());
+
+    loop {
+        match fut.as_mut().poll(ctx) {
+            Poll::Pending => {}
+            Poll::Ready(t) => break t,
+        }
+    }
+}
+
+async fn call_once(f: impl async FnOnce(DropMe)) {
+    f(DropMe("world")).await;
+}
+
+struct DropMe(&'static str);
+
+pub fn future() {
+    block_on(async {
+        let async_closure = async move |a: DropMe| {};
+        call_once(async_closure).await;
+    });
+}
diff --git a/tests/crashes/124021.rs b/tests/crashes/124021.rs
new file mode 100644
index 0000000..a2b6b7f
--- /dev/null
+++ b/tests/crashes/124021.rs
@@ -0,0 +1,6 @@
+//@ known-bug: #124021
+type Opaque2<'a> = impl Sized + 'a;
+
+fn test2() -> impl for<'a, 'b> Fn((&'a str, &'b str)) -> (Opaque2<'a>, Opaque2<'a>) {
+    |x| x
+}
diff --git a/tests/crashes/124031.rs b/tests/crashes/124031.rs
new file mode 100644
index 0000000..bdc66fb
--- /dev/null
+++ b/tests/crashes/124031.rs
@@ -0,0 +1,17 @@
+//@ known-bug: #124031
+
+trait Trait {
+    type RefTarget;
+}
+
+impl Trait for () {}
+
+struct Other {
+    data: <() as Trait>::RefTarget,
+}
+
+fn main() {
+    unsafe {
+        std::mem::transmute::<Option<()>, Option<&Other>>(None);
+    }
+}
diff --git a/tests/crashes/124083.rs b/tests/crashes/124083.rs
new file mode 100644
index 0000000..e9cbf3f
--- /dev/null
+++ b/tests/crashes/124083.rs
@@ -0,0 +1,9 @@
+//@ known-bug: #124083
+
+struct Outest(&'a ());
+
+fn make() -> Outest {}
+
+fn main() {
+    if let Outest("foo") = make() {}
+}
diff --git a/tests/crashes/124092.rs b/tests/crashes/124092.rs
new file mode 100644
index 0000000..c03db38
--- /dev/null
+++ b/tests/crashes/124092.rs
@@ -0,0 +1,7 @@
+//@ known-bug: #124092
+//@ compile-flags: -Zvirtual-function-elimination=true -Clto=true
+//@ only-x86_64
+const X: for<'b> fn(&'b ()) = |&()| ();
+fn main() {
+    let dyn_debug = Box::new(X) as Box<fn(&'static ())> as Box<dyn Send>;
+}
diff --git a/tests/crashes/124151.rs b/tests/crashes/124151.rs
new file mode 100644
index 0000000..5e55ac2
--- /dev/null
+++ b/tests/crashes/124151.rs
@@ -0,0 +1,14 @@
+//@ known-bug: #124151
+#![feature(generic_const_exprs)]
+
+use std::ops::Add;
+
+pub struct Dimension;
+
+pub struct Quantity<S, const D: Dimension>(S);
+
+impl<const D: Dimension, LHS, RHS> Add<LHS, D> for Quantity<LHS, { Dimension }> {}
+
+pub fn add<const U: Dimension>(x: Quantity<f32, U>) -> Quantity<f32, U> {
+    x + y
+}
diff --git a/tests/crashes/124164.rs b/tests/crashes/124164.rs
new file mode 100644
index 0000000..8c9b4bd
--- /dev/null
+++ b/tests/crashes/124164.rs
@@ -0,0 +1,4 @@
+//@ known-bug: #124164
+static S_COUNT: = std::sync::atomic::AtomicUsize::new(0);
+
+fn main() {}
diff --git a/tests/crashes/124182.rs b/tests/crashes/124182.rs
new file mode 100644
index 0000000..4694820
--- /dev/null
+++ b/tests/crashes/124182.rs
@@ -0,0 +1,22 @@
+//@ known-bug: #124182
+struct LazyLock<T> {
+    data: (Copy, fn() -> T),
+}
+
+impl<T> LazyLock<T> {
+    pub const fn new(f: fn() -> T) -> LazyLock<T> {
+        LazyLock { data: (None, f) }
+    }
+}
+
+struct A<T = i32>(Option<T>);
+
+impl<T> Default for A<T> {
+    fn default() -> Self {
+        A(None)
+    }
+}
+
+static EMPTY_SET: LazyLock<A<i32>> = LazyLock::new(A::default);
+
+fn main() {}
diff --git a/tests/crashes/124189.rs b/tests/crashes/124189.rs
new file mode 100644
index 0000000..7c193ec
--- /dev/null
+++ b/tests/crashes/124189.rs
@@ -0,0 +1,14 @@
+//@ known-bug: #124189
+trait Trait {
+    type Type;
+}
+
+impl<T> Trait for T {
+    type Type = ();
+}
+
+fn f(_: <&Copy as Trait>::Type) {}
+
+fn main() {
+    f(());
+}
diff --git a/tests/crashes/124207.rs b/tests/crashes/124207.rs
new file mode 100644
index 0000000..a4e1c55
--- /dev/null
+++ b/tests/crashes/124207.rs
@@ -0,0 +1,9 @@
+//@ known-bug: #124207
+#![feature(transmutability)]
+#![feature(type_alias_impl_trait)]
+trait OpaqueTrait {}
+type OpaqueType = impl OpaqueTrait;
+trait AnotherTrait {}
+impl<T: std::mem::BikeshedIntrinsicFrom<(), ()>> AnotherTrait for T {}
+impl AnotherTrait for OpaqueType {}
+pub fn main() {}
diff --git a/tests/crashes/97006.rs b/tests/crashes/97006.rs
new file mode 100644
index 0000000..c8dfa52
--- /dev/null
+++ b/tests/crashes/97006.rs
@@ -0,0 +1,15 @@
+//@ known-bug: #97006
+//@ compile-flags: -Zunpretty=hir
+
+#![allow(unused)]
+
+macro_rules! m {
+    ($attr_path: path) => {
+        #[$attr_path]
+        fn f() {}
+    }
+}
+
+m!(inline<u8>); //~ ERROR: unexpected generic arguments in path
+
+fn main() {}
diff --git a/tests/debuginfo/msvc-pretty-enums.rs b/tests/debuginfo/msvc-pretty-enums.rs
index cfac14a..0293ec0 100644
--- a/tests/debuginfo/msvc-pretty-enums.rs
+++ b/tests/debuginfo/msvc-pretty-enums.rs
@@ -132,7 +132,6 @@
 // cdb-command: dx -r2 arbitrary_discr2,d
 // cdb-check: arbitrary_discr2,d : Def [Type: enum2$<msvc_pretty_enums::ArbitraryDiscr>]
 // cdb-check:     [+0x[...]] __0              : 5678 [Type: unsigned int]
-#![feature(generic_nonzero)]
 #![feature(rustc_attrs)]
 #![feature(repr128)]
 #![feature(arbitrary_enum_discriminant)]
diff --git a/tests/debuginfo/numeric-types.rs b/tests/debuginfo/numeric-types.rs
index 1ff72d3..98bc31e 100644
--- a/tests/debuginfo/numeric-types.rs
+++ b/tests/debuginfo/numeric-types.rs
@@ -237,7 +237,6 @@
 
 // lldb-command:v nz_usize
 // lldb-check:[...] 122 { __0 = { 0 = 122 } }
-#![feature(generic_nonzero)]
 
 use std::num::*;
 use std::sync::atomic::*;
diff --git a/tests/mir-opt/README.md b/tests/mir-opt/README.md
index 39a7b5a..e4b252f 100644
--- a/tests/mir-opt/README.md
+++ b/tests/mir-opt/README.md
@@ -14,17 +14,18 @@
 
 to your test, causing separate files to be generated for 32bit and 64bit systems.
 
-## Unit testing
+## Testing a particular MIR pass
 
 If you are only testing the behavior of a particular mir-opt pass on some specific input (as is
 usually the case), you should add
 
 ```
-// unit-test: PassName
+//@ test-mir-pass: PassName
 ```
 
 to the top of the file. This makes sure that other passes don't run which means you'll get the input
-you expected and your test won't break when other code changes.
+you expected and your test won't break when other code changes. This also lets you test passes
+that are disabled by default.
 
 ## Emit a diff of the mir for a specific optimization
 
diff --git a/tests/mir-opt/array_index_is_temporary.rs b/tests/mir-opt/array_index_is_temporary.rs
index 500b8b7..cd44c31 100644
--- a/tests/mir-opt/array_index_is_temporary.rs
+++ b/tests/mir-opt/array_index_is_temporary.rs
@@ -1,4 +1,4 @@
-//@ unit-test: SimplifyCfg-pre-optimizations
+//@ test-mir-pass: SimplifyCfg-pre-optimizations
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // Retagging (from Stacked Borrows) relies on the array index being a fresh
 // temporary, so that side-effects cannot change it.
diff --git a/tests/mir-opt/basic_assignment.rs b/tests/mir-opt/basic_assignment.rs
index 0a23cdd..a9342c1 100644
--- a/tests/mir-opt/basic_assignment.rs
+++ b/tests/mir-opt/basic_assignment.rs
@@ -1,4 +1,4 @@
-//@ unit-test: ElaborateDrops
+//@ test-mir-pass: ElaborateDrops
 //@ needs-unwind
 // this tests move up progration, which is not yet implemented
 
diff --git a/tests/mir-opt/box_expr.rs b/tests/mir-opt/box_expr.rs
index 8c01662..a2d3ab9 100644
--- a/tests/mir-opt/box_expr.rs
+++ b/tests/mir-opt/box_expr.rs
@@ -1,4 +1,4 @@
-//@ unit-test: ElaborateDrops
+//@ test-mir-pass: ElaborateDrops
 //@ needs-unwind
 
 #![feature(rustc_attrs, stmt_expr_attributes)]
diff --git a/tests/mir-opt/building/eq_never_type._f.built.after.mir b/tests/mir-opt/building/eq_never_type._f.built.after.mir
new file mode 100644
index 0000000..3943825
--- /dev/null
+++ b/tests/mir-opt/building/eq_never_type._f.built.after.mir
@@ -0,0 +1,53 @@
+// MIR for `_f` after built
+
+fn _f(_1: !, _2: !) -> () {
+    debug a => _1;
+    debug b => _2;
+    let mut _0: ();
+    let mut _3: !;
+    let _4: bool;
+    let mut _5: &();
+    let mut _6: !;
+    let mut _7: &();
+    let _8: ();
+    let mut _9: !;
+
+    bb0: {
+        StorageLive(_4);
+        StorageLive(_5);
+        StorageLive(_6);
+        _6 = _1;
+        unreachable;
+    }
+
+    bb1: {
+        StorageDead(_6);
+        StorageLive(_7);
+        StorageLive(_8);
+        StorageLive(_9);
+        _9 = _2;
+        unreachable;
+    }
+
+    bb2: {
+        _7 = &_8;
+        StorageDead(_9);
+        _4 = <() as PartialEq>::eq(move _5, move _7) -> [return: bb3, unwind: bb5];
+    }
+
+    bb3: {
+        StorageDead(_7);
+        StorageDead(_5);
+        StorageDead(_8);
+        StorageDead(_4);
+        unreachable;
+    }
+
+    bb4: {
+        return;
+    }
+
+    bb5 (cleanup): {
+        resume;
+    }
+}
diff --git a/tests/mir-opt/building/eq_never_type.rs b/tests/mir-opt/building/eq_never_type.rs
new file mode 100644
index 0000000..90e2e69
--- /dev/null
+++ b/tests/mir-opt/building/eq_never_type.rs
@@ -0,0 +1,13 @@
+// skip-filecheck
+#![feature(never_type)]
+#![allow(unreachable_code)]
+
+// EMIT_MIR eq_never_type._f.built.after.mir
+fn _f(a: !, b: !) {
+    // Both arguments must be references (i.e. == should auto-borrow/coerce-to-ref both arguments)
+    // (this previously was buggy due to `NeverToAny` coercion incorrectly throwing out other
+    // coercions)
+    a == b;
+}
+
+fn main() {}
diff --git a/tests/mir-opt/const_allocation.rs b/tests/mir-opt/const_allocation.rs
index 038caaa..e65b2ed 100644
--- a/tests/mir-opt/const_allocation.rs
+++ b/tests/mir-opt/const_allocation.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 static FOO: &[(Option<i32>, &[&str])] =
diff --git a/tests/mir-opt/const_allocation2.rs b/tests/mir-opt/const_allocation2.rs
index ba987c1..0681d43 100644
--- a/tests/mir-opt/const_allocation2.rs
+++ b/tests/mir-opt/const_allocation2.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 // EMIT_MIR const_allocation2.main.GVN.after.mir
diff --git a/tests/mir-opt/const_allocation3.rs b/tests/mir-opt/const_allocation3.rs
index 86ffdef..46be3e1 100644
--- a/tests/mir-opt/const_allocation3.rs
+++ b/tests/mir-opt/const_allocation3.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 // EMIT_MIR const_allocation3.main.GVN.after.mir
diff --git a/tests/mir-opt/const_debuginfo.rs b/tests/mir-opt/const_debuginfo.rs
index 319bcf4..b880d7e 100644
--- a/tests/mir-opt/const_debuginfo.rs
+++ b/tests/mir-opt/const_debuginfo.rs
@@ -1,4 +1,4 @@
-//@ unit-test: ConstDebugInfo
+//@ test-mir-pass: ConstDebugInfo
 //@ compile-flags: -C overflow-checks=no -Zmir-enable-passes=+GVN
 
 struct Point {
diff --git a/tests/mir-opt/const_prop/address_of_pair.rs b/tests/mir-opt/const_prop/address_of_pair.rs
index c6bd576..6d0c0f8 100644
--- a/tests/mir-opt/const_prop/address_of_pair.rs
+++ b/tests/mir-opt/const_prop/address_of_pair.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR address_of_pair.fn0.GVN.diff
 pub fn fn0() -> bool {
diff --git a/tests/mir-opt/const_prop/aggregate.rs b/tests/mir-opt/const_prop/aggregate.rs
index 2db4770..8f8f92b 100644
--- a/tests/mir-opt/const_prop/aggregate.rs
+++ b/tests/mir-opt/const_prop/aggregate.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -O
 
 // EMIT_MIR aggregate.main.GVN.diff
diff --git a/tests/mir-opt/const_prop/array_index.rs b/tests/mir-opt/const_prop/array_index.rs
index 2a9ca5f..aff7226 100644
--- a/tests/mir-opt/const_prop/array_index.rs
+++ b/tests/mir-opt/const_prop/array_index.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
diff --git a/tests/mir-opt/const_prop/bad_op_div_by_zero.rs b/tests/mir-opt/const_prop/bad_op_div_by_zero.rs
index c411d3b..6c57671 100644
--- a/tests/mir-opt/const_prop/bad_op_div_by_zero.rs
+++ b/tests/mir-opt/const_prop/bad_op_div_by_zero.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 // EMIT_MIR bad_op_div_by_zero.main.GVN.diff
diff --git a/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs b/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs
index aa09c16..b7623dc 100644
--- a/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs
+++ b/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 // EMIT_MIR bad_op_mod_by_zero.main.GVN.diff
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
index 25d513e..0f8d278 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
diff --git a/tests/mir-opt/const_prop/boolean_identities.rs b/tests/mir-opt/const_prop/boolean_identities.rs
index cbc106a..f237493 100644
--- a/tests/mir-opt/const_prop/boolean_identities.rs
+++ b/tests/mir-opt/const_prop/boolean_identities.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR boolean_identities.test.GVN.diff
 pub fn test(x: bool, y: bool) -> bool {
diff --git a/tests/mir-opt/const_prop/boxes.rs b/tests/mir-opt/const_prop/boxes.rs
index 859491c..7813352 100644
--- a/tests/mir-opt/const_prop/boxes.rs
+++ b/tests/mir-opt/const_prop/boxes.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -O
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
diff --git a/tests/mir-opt/const_prop/cast.rs b/tests/mir-opt/const_prop/cast.rs
index ad95515..dce2e38 100644
--- a/tests/mir-opt/const_prop/cast.rs
+++ b/tests/mir-opt/const_prop/cast.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR cast.main.GVN.diff
 
 fn main() {
diff --git a/tests/mir-opt/const_prop/checked_add.rs b/tests/mir-opt/const_prop/checked_add.rs
index 6f43e6a..0560b04 100644
--- a/tests/mir-opt/const_prop/checked_add.rs
+++ b/tests/mir-opt/const_prop/checked_add.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -C overflow-checks=on
 
 // EMIT_MIR checked_add.main.GVN.diff
diff --git a/tests/mir-opt/const_prop/control_flow_simplification.rs b/tests/mir-opt/const_prop/control_flow_simplification.rs
index eb33682..39b5f28 100644
--- a/tests/mir-opt/const_prop/control_flow_simplification.rs
+++ b/tests/mir-opt/const_prop/control_flow_simplification.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -Zmir-opt-level=1
 
 trait NeedsDrop: Sized {
diff --git a/tests/mir-opt/const_prop/discriminant.rs b/tests/mir-opt/const_prop/discriminant.rs
index 51542af..aa169eb 100644
--- a/tests/mir-opt/const_prop/discriminant.rs
+++ b/tests/mir-opt/const_prop/discriminant.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // FIXME(wesleywiser): Ideally, we could const-prop away all of this and just be left with
 // `let x = 42` but that doesn't work because const-prop doesn't support `Operand::Indirect`
diff --git a/tests/mir-opt/const_prop/indirect.rs b/tests/mir-opt/const_prop/indirect.rs
index 5c469c5..ca53e2b 100644
--- a/tests/mir-opt/const_prop/indirect.rs
+++ b/tests/mir-opt/const_prop/indirect.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -C overflow-checks=on
 
 // EMIT_MIR indirect.main.GVN.diff
diff --git a/tests/mir-opt/const_prop/indirect_mutation.rs b/tests/mir-opt/const_prop/indirect_mutation.rs
index b2a9d5d..32ff8f1 100644
--- a/tests/mir-opt/const_prop/indirect_mutation.rs
+++ b/tests/mir-opt/const_prop/indirect_mutation.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // Check that we do not propagate past an indirect mutation.
 #![feature(raw_ref_op)]
 
diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-abort.diff b/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-abort.diff
index 4c2df22..8c5a0df 100644
--- a/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-abort.diff
+++ b/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-abort.diff
@@ -9,8 +9,6 @@
       scope 1 {
       }
       scope 2 (inlined #[track_caller] <u8 as Add>::add) {
-          debug self => _2;
-          debug other => _3;
           let mut _4: (u8, bool);
       }
   
diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-unwind.diff
index c4e666b..7887a8a 100644
--- a/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-unwind.diff
@@ -9,8 +9,6 @@
       scope 1 {
       }
       scope 2 (inlined #[track_caller] <u8 as Add>::add) {
-          debug self => _2;
-          debug other => _3;
           let mut _4: (u8, bool);
       }
   
diff --git a/tests/mir-opt/const_prop/inherit_overflow.rs b/tests/mir-opt/const_prop/inherit_overflow.rs
index e71a05c..c37f3fd 100644
--- a/tests/mir-opt/const_prop/inherit_overflow.rs
+++ b/tests/mir-opt/const_prop/inherit_overflow.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -Zmir-enable-passes=+Inline
 
 // After inlining, this will contain a `CheckedBinaryOp`.
diff --git a/tests/mir-opt/const_prop/invalid_constant.rs b/tests/mir-opt/const_prop/invalid_constant.rs
index 1df82f2..afd8746 100644
--- a/tests/mir-opt/const_prop/invalid_constant.rs
+++ b/tests/mir-opt/const_prop/invalid_constant.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -Zmir-enable-passes=+RemoveZsts
 // Verify that we can pretty print invalid constants.
 
diff --git a/tests/mir-opt/const_prop/issue_66971.rs b/tests/mir-opt/const_prop/issue_66971.rs
index 30a9d62..03f3496 100644
--- a/tests/mir-opt/const_prop/issue_66971.rs
+++ b/tests/mir-opt/const_prop/issue_66971.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // Due to a bug in propagating scalar pairs the assertion below used to fail. In the expected
 // outputs below, after GVN this is how _2 would look like with the bug:
diff --git a/tests/mir-opt/const_prop/issue_67019.rs b/tests/mir-opt/const_prop/issue_67019.rs
index bf788b9..e13c744 100644
--- a/tests/mir-opt/const_prop/issue_67019.rs
+++ b/tests/mir-opt/const_prop/issue_67019.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // This used to ICE in const-prop
 
diff --git a/tests/mir-opt/const_prop/large_array_index.rs b/tests/mir-opt/const_prop/large_array_index.rs
index 1cefc85..afba73f 100644
--- a/tests/mir-opt/const_prop/large_array_index.rs
+++ b/tests/mir-opt/const_prop/large_array_index.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
diff --git a/tests/mir-opt/const_prop/mult_by_zero.rs b/tests/mir-opt/const_prop/mult_by_zero.rs
index d79f3e8..7943c74 100644
--- a/tests/mir-opt/const_prop/mult_by_zero.rs
+++ b/tests/mir-opt/const_prop/mult_by_zero.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR mult_by_zero.test.GVN.diff
 fn test(x: i32) -> i32 {
diff --git a/tests/mir-opt/const_prop/mutable_variable.rs b/tests/mir-opt/const_prop/mutable_variable.rs
index 4445bd2..9698fba 100644
--- a/tests/mir-opt/const_prop/mutable_variable.rs
+++ b/tests/mir-opt/const_prop/mutable_variable.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR mutable_variable.main.GVN.diff
 fn main() {
diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate.rs
index c2b2731..7de647e 100644
--- a/tests/mir-opt/const_prop/mutable_variable_aggregate.rs
+++ b/tests/mir-opt/const_prop/mutable_variable_aggregate.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR mutable_variable_aggregate.main.GVN.diff
 fn main() {
diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs
index c9f09f8..5656c0e 100644
--- a/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs
+++ b/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR mutable_variable_aggregate_mut_ref.main.GVN.diff
 fn main() {
diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs
index 5b7804b..6f99e6b 100644
--- a/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs
+++ b/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR mutable_variable_aggregate_partial_read.main.GVN.diff
 fn main() {
diff --git a/tests/mir-opt/const_prop/mutable_variable_no_prop.rs b/tests/mir-opt/const_prop/mutable_variable_no_prop.rs
index 9ea2e78..8289832 100644
--- a/tests/mir-opt/const_prop/mutable_variable_no_prop.rs
+++ b/tests/mir-opt/const_prop/mutable_variable_no_prop.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // Verify that we do not propagate the contents of this mutable static.
 static mut STATIC: u32 = 0x42424242;
diff --git a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs
index 39ac1fa..cc92949 100644
--- a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs
+++ b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR mutable_variable_unprop_assign.main.GVN.diff
 fn main() {
diff --git a/tests/mir-opt/const_prop/offset_of.rs b/tests/mir-opt/const_prop/offset_of.rs
index 7d258f2..105cbfb 100644
--- a/tests/mir-opt/const_prop/offset_of.rs
+++ b/tests/mir-opt/const_prop/offset_of.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 #![feature(offset_of_enum, offset_of_nested)]
diff --git a/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs b/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs
index 535870f..a435582 100644
--- a/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs
+++ b/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -O
 
 // Regression test for https://github.com/rust-lang/rust/issues/118328
diff --git a/tests/mir-opt/const_prop/pointer_expose_provenance.rs b/tests/mir-opt/const_prop/pointer_expose_provenance.rs
index 840a4d6..f148a5b 100644
--- a/tests/mir-opt/const_prop/pointer_expose_provenance.rs
+++ b/tests/mir-opt/const_prop/pointer_expose_provenance.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 #[inline(never)]
 fn read(_: usize) { }
diff --git a/tests/mir-opt/const_prop/read_immutable_static.rs b/tests/mir-opt/const_prop/read_immutable_static.rs
index ec2dbf6..05fec2f 100644
--- a/tests/mir-opt/const_prop/read_immutable_static.rs
+++ b/tests/mir-opt/const_prop/read_immutable_static.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 static FOO: u8 = 2;
 
diff --git a/tests/mir-opt/const_prop/ref_deref.rs b/tests/mir-opt/const_prop/ref_deref.rs
index 20c1fba..aef3632 100644
--- a/tests/mir-opt/const_prop/ref_deref.rs
+++ b/tests/mir-opt/const_prop/ref_deref.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR ref_deref.main.GVN.diff
 fn main() {
diff --git a/tests/mir-opt/const_prop/ref_deref_project.rs b/tests/mir-opt/const_prop/ref_deref_project.rs
index 59e7f1a50..5a48a88 100644
--- a/tests/mir-opt/const_prop/ref_deref_project.rs
+++ b/tests/mir-opt/const_prop/ref_deref_project.rs
@@ -1,5 +1,5 @@
 // This does not currently propagate (#67862)
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 // EMIT_MIR ref_deref_project.main.GVN.diff
 fn main() {
diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.rs b/tests/mir-opt/const_prop/reify_fn_ptr.rs
index 55dca24..ffce4e9 100644
--- a/tests/mir-opt/const_prop/reify_fn_ptr.rs
+++ b/tests/mir-opt/const_prop/reify_fn_ptr.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR reify_fn_ptr.main.GVN.diff
 
 fn main() {
diff --git a/tests/mir-opt/const_prop/repeat.rs b/tests/mir-opt/const_prop/repeat.rs
index d881462..d778191 100644
--- a/tests/mir-opt/const_prop/repeat.rs
+++ b/tests/mir-opt/const_prop/repeat.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
diff --git a/tests/mir-opt/const_prop/return_place.rs b/tests/mir-opt/const_prop/return_place.rs
index fea28c9..e7eea11 100644
--- a/tests/mir-opt/const_prop/return_place.rs
+++ b/tests/mir-opt/const_prop/return_place.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //@ compile-flags: -C overflow-checks=on
 
diff --git a/tests/mir-opt/const_prop/scalar_literal_propagation.rs b/tests/mir-opt/const_prop/scalar_literal_propagation.rs
index e077746..9d02f24 100644
--- a/tests/mir-opt/const_prop/scalar_literal_propagation.rs
+++ b/tests/mir-opt/const_prop/scalar_literal_propagation.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 // EMIT_MIR scalar_literal_propagation.main.GVN.diff
diff --git a/tests/mir-opt/const_prop/slice_len.rs b/tests/mir-opt/const_prop/slice_len.rs
index 4a48f92..63cdbf0 100644
--- a/tests/mir-opt/const_prop/slice_len.rs
+++ b/tests/mir-opt/const_prop/slice_len.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -Zmir-enable-passes=+InstSimplify
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
diff --git a/tests/mir-opt/const_prop/switch_int.rs b/tests/mir-opt/const_prop/switch_int.rs
index a176bf1..114380e 100644
--- a/tests/mir-opt/const_prop/switch_int.rs
+++ b/tests/mir-opt/const_prop/switch_int.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -Zmir-enable-passes=+SimplifyConstCondition-after-const-prop
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs
index 5f2d767..9cbf892 100644
--- a/tests/mir-opt/const_prop/transmute.rs
+++ b/tests/mir-opt/const_prop/transmute.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -O --crate-type=lib
 //@ ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.rs b/tests/mir-opt/const_prop/tuple_literal_propagation.rs
index 5992bb1..582411c7b 100644
--- a/tests/mir-opt/const_prop/tuple_literal_propagation.rs
+++ b/tests/mir-opt/const_prop/tuple_literal_propagation.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // EMIT_MIR tuple_literal_propagation.main.GVN.diff
 
diff --git a/tests/mir-opt/const_prop/while_let_loops.rs b/tests/mir-opt/const_prop/while_let_loops.rs
index 6a421da..cb2c836 100644
--- a/tests/mir-opt/const_prop/while_let_loops.rs
+++ b/tests/mir-opt/const_prop/while_let_loops.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR while_let_loops.change_loop_body.GVN.diff
 
 pub fn change_loop_body() {
diff --git a/tests/mir-opt/copy-prop/borrowed_local.rs b/tests/mir-opt/copy-prop/borrowed_local.rs
index 24b8e45..74ac628 100644
--- a/tests/mir-opt/copy-prop/borrowed_local.rs
+++ b/tests/mir-opt/copy-prop/borrowed_local.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 
 #![feature(custom_mir, core_intrinsics)]
 #![allow(unused_assignments)]
diff --git a/tests/mir-opt/copy-prop/branch.rs b/tests/mir-opt/copy-prop/branch.rs
index 0944bb3..fc9b8dc 100644
--- a/tests/mir-opt/copy-prop/branch.rs
+++ b/tests/mir-opt/copy-prop/branch.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //! Tests that we bail out when there are multiple assignments to the same local.
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 fn val() -> i32 {
     1
 }
diff --git a/tests/mir-opt/copy-prop/calls.rs b/tests/mir-opt/copy-prop/calls.rs
index 7d123e6..8937c0d 100644
--- a/tests/mir-opt/copy-prop/calls.rs
+++ b/tests/mir-opt/copy-prop/calls.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // Check that CopyProp does propagate return values of call terminators.
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 //@ needs-unwind
 
 #![feature(custom_mir, core_intrinsics)]
diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.rs b/tests/mir-opt/copy-prop/copy_propagation_arg.rs
index 3516d8f..e062e1e 100644
--- a/tests/mir-opt/copy-prop/copy_propagation_arg.rs
+++ b/tests/mir-opt/copy-prop/copy_propagation_arg.rs
@@ -2,7 +2,7 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // Check that CopyProp does not propagate an assignment to a function argument
 // (doing so can break usages of the original argument value)
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 fn dummy(x: u8) -> u8 {
     x
 }
diff --git a/tests/mir-opt/copy-prop/custom_move_arg.rs b/tests/mir-opt/copy-prop/custom_move_arg.rs
index 3577ed2..a82d461 100644
--- a/tests/mir-opt/copy-prop/custom_move_arg.rs
+++ b/tests/mir-opt/copy-prop/custom_move_arg.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 
 #![feature(custom_mir, core_intrinsics)]
 #![allow(unused_assignments)]
diff --git a/tests/mir-opt/copy-prop/cycle.rs b/tests/mir-opt/copy-prop/cycle.rs
index ed97e86..1c0c9ea 100644
--- a/tests/mir-opt/copy-prop/cycle.rs
+++ b/tests/mir-opt/copy-prop/cycle.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code.
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 fn val() -> i32 {
     1
 }
diff --git a/tests/mir-opt/copy-prop/dead_stores_79191.rs b/tests/mir-opt/copy-prop/dead_stores_79191.rs
index f6e0eac..24420e1 100644
--- a/tests/mir-opt/copy-prop/dead_stores_79191.rs
+++ b/tests/mir-opt/copy-prop/dead_stores_79191.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 
 fn id<T>(x: T) -> T {
     x
diff --git a/tests/mir-opt/copy-prop/dead_stores_better.rs b/tests/mir-opt/copy-prop/dead_stores_better.rs
index fdf4287..4b18742 100644
--- a/tests/mir-opt/copy-prop/dead_stores_better.rs
+++ b/tests/mir-opt/copy-prop/dead_stores_better.rs
@@ -3,7 +3,7 @@
 // This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates
 // that that pass enables this one to do more optimizations.
 
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 //@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination
 
 fn id<T>(x: T) -> T {
diff --git a/tests/mir-opt/copy-prop/issue_107511.rs b/tests/mir-opt/copy-prop/issue_107511.rs
index d9bd08b..5e8fc8d 100644
--- a/tests/mir-opt/copy-prop/issue_107511.rs
+++ b/tests/mir-opt/copy-prop/issue_107511.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 
 // EMIT_MIR issue_107511.main.CopyProp.diff
 fn main() {
diff --git a/tests/mir-opt/copy-prop/move_arg.rs b/tests/mir-opt/copy-prop/move_arg.rs
index 85ced0f..4983405 100644
--- a/tests/mir-opt/copy-prop/move_arg.rs
+++ b/tests/mir-opt/copy-prop/move_arg.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // Test that we do not move multiple times from the same local.
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 
 // EMIT_MIR move_arg.f.CopyProp.diff
 pub fn f<T: Copy>(a: T) {
diff --git a/tests/mir-opt/copy-prop/move_projection.rs b/tests/mir-opt/copy-prop/move_projection.rs
index d68ffad..231e408 100644
--- a/tests/mir-opt/copy-prop/move_projection.rs
+++ b/tests/mir-opt/copy-prop/move_projection.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 
 #![feature(custom_mir, core_intrinsics)]
 #![allow(unused_assignments)]
diff --git a/tests/mir-opt/copy-prop/mutate_through_pointer.rs b/tests/mir-opt/copy-prop/mutate_through_pointer.rs
index 610f540..14ca513 100644
--- a/tests/mir-opt/copy-prop/mutate_through_pointer.rs
+++ b/tests/mir-opt/copy-prop/mutate_through_pointer.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 //
 // This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB
 // according to Miri. However, the decision to make this UB - and to allow
diff --git a/tests/mir-opt/copy-prop/non_dominate.rs b/tests/mir-opt/copy-prop/non_dominate.rs
index d8b42b7..34e7eab 100644
--- a/tests/mir-opt/copy-prop/non_dominate.rs
+++ b/tests/mir-opt/copy-prop/non_dominate.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 
 #![feature(custom_mir, core_intrinsics)]
 #![allow(unused_assignments)]
diff --git a/tests/mir-opt/copy-prop/partial_init.rs b/tests/mir-opt/copy-prop/partial_init.rs
index 4639055..88e9498 100644
--- a/tests/mir-opt/copy-prop/partial_init.rs
+++ b/tests/mir-opt/copy-prop/partial_init.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 // Verify that we do not ICE on partial initializations.
 
 #![feature(custom_mir, core_intrinsics)]
diff --git a/tests/mir-opt/copy-prop/reborrow.rs b/tests/mir-opt/copy-prop/reborrow.rs
index 7d02fb3..2f17205 100644
--- a/tests/mir-opt/copy-prop/reborrow.rs
+++ b/tests/mir-opt/copy-prop/reborrow.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // Check that CopyProp considers reborrows as not mutating the pointer.
-//@ unit-test: CopyProp
+//@ test-mir-pass: CopyProp
 
 #![feature(raw_ref_op)]
 
diff --git a/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff
new file mode 100644
index 0000000..e60f71f
--- /dev/null
+++ b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff
@@ -0,0 +1,138 @@
+- // MIR for `main` before InstrumentCoverage
++ // MIR for `main` after InstrumentCoverage
+  
+  fn main() -> () {
+      let mut _0: ();
+      let mut _1: Enum;
+      let mut _2: isize;
+      let _3: u32;
+      let mut _4: u32;
+      let _5: u32;
+      let mut _6: u32;
+      let _7: u32;
+      let mut _8: u32;
+      let _9: u32;
+      let mut _10: u32;
+      scope 1 {
+          debug d => _3;
+      }
+      scope 2 {
+          debug c => _5;
+      }
+      scope 3 {
+          debug b => _7;
+      }
+      scope 4 {
+          debug a => _9;
+      }
+  
++     coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Add, rhs: Counter(2) };
++     coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Add, rhs: Counter(3) };
++     coverage ExpressionId(2) => Expression { lhs: Counter(0), op: Subtract, rhs: Expression(1) };
++     coverage ExpressionId(3) => Expression { lhs: Counter(3), op: Add, rhs: Counter(2) };
++     coverage ExpressionId(4) => Expression { lhs: Expression(3), op: Add, rhs: Counter(1) };
++     coverage ExpressionId(5) => Expression { lhs: Expression(4), op: Add, rhs: Expression(2) };
++     coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:14:1 - 15:21;
++     coverage Code(Counter(3)) => $DIR/branch_match_arms.rs:16:17 - 16:33;
++     coverage Code(Counter(2)) => $DIR/branch_match_arms.rs:17:17 - 17:33;
++     coverage Code(Counter(1)) => $DIR/branch_match_arms.rs:18:17 - 18:33;
++     coverage Code(Expression(2)) => $DIR/branch_match_arms.rs:19:17 - 19:33;
++     coverage Code(Expression(5)) => $DIR/branch_match_arms.rs:21:1 - 21:2;
++ 
+      bb0: {
++         Coverage::CounterIncrement(0);
+          StorageLive(_1);
+          _1 = Enum::A(const 0_u32);
+          PlaceMention(_1);
+          _2 = discriminant(_1);
+          switchInt(move _2) -> [0: bb5, 1: bb4, 2: bb3, 3: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          FakeRead(ForMatchedPlace(None), _1);
+          unreachable;
+      }
+  
+      bb2: {
++         Coverage::CounterIncrement(3);
+          falseEdge -> [real: bb6, imaginary: bb3];
+      }
+  
+      bb3: {
++         Coverage::CounterIncrement(2);
+          falseEdge -> [real: bb8, imaginary: bb4];
+      }
+  
+      bb4: {
++         Coverage::CounterIncrement(1);
+          falseEdge -> [real: bb10, imaginary: bb5];
+      }
+  
+      bb5: {
++         Coverage::ExpressionUsed(2);
+          StorageLive(_9);
+          _9 = ((_1 as A).0: u32);
+          StorageLive(_10);
+          _10 = _9;
+          _0 = consume(move _10) -> [return: bb12, unwind: bb14];
+      }
+  
+      bb6: {
+          StorageLive(_3);
+          _3 = ((_1 as D).0: u32);
+          StorageLive(_4);
+          _4 = _3;
+          _0 = consume(move _4) -> [return: bb7, unwind: bb14];
+      }
+  
+      bb7: {
+          StorageDead(_4);
+          StorageDead(_3);
+          goto -> bb13;
+      }
+  
+      bb8: {
+          StorageLive(_5);
+          _5 = ((_1 as C).0: u32);
+          StorageLive(_6);
+          _6 = _5;
+          _0 = consume(move _6) -> [return: bb9, unwind: bb14];
+      }
+  
+      bb9: {
+          StorageDead(_6);
+          StorageDead(_5);
+          goto -> bb13;
+      }
+  
+      bb10: {
+          StorageLive(_7);
+          _7 = ((_1 as B).0: u32);
+          StorageLive(_8);
+          _8 = _7;
+          _0 = consume(move _8) -> [return: bb11, unwind: bb14];
+      }
+  
+      bb11: {
+          StorageDead(_8);
+          StorageDead(_7);
+          goto -> bb13;
+      }
+  
+      bb12: {
+          StorageDead(_10);
+          StorageDead(_9);
+          goto -> bb13;
+      }
+  
+      bb13: {
++         Coverage::ExpressionUsed(5);
+          StorageDead(_1);
+          return;
+      }
+  
+      bb14 (cleanup): {
+          resume;
+      }
+  }
+  
diff --git a/tests/mir-opt/coverage/branch_match_arms.rs b/tests/mir-opt/coverage/branch_match_arms.rs
new file mode 100644
index 0000000..18764b3
--- /dev/null
+++ b/tests/mir-opt/coverage/branch_match_arms.rs
@@ -0,0 +1,27 @@
+#![feature(coverage_attribute)]
+//@ test-mir-pass: InstrumentCoverage
+//@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime -Zcoverage-options=branch
+// skip-filecheck
+
+enum Enum {
+    A(u32),
+    B(u32),
+    C(u32),
+    D(u32),
+}
+
+// EMIT_MIR branch_match_arms.main.InstrumentCoverage.diff
+fn main() {
+    match Enum::A(0) {
+        Enum::D(d) => consume(d),
+        Enum::C(c) => consume(c),
+        Enum::B(b) => consume(b),
+        Enum::A(a) => consume(a),
+    }
+}
+
+#[inline(never)]
+#[coverage(off)]
+fn consume(x: u32) {
+    core::hint::black_box(x);
+}
diff --git a/tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff
similarity index 100%
rename from tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff
rename to tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff
diff --git a/tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff
similarity index 100%
rename from tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff
rename to tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff
diff --git a/tests/mir-opt/instrument_coverage.rs b/tests/mir-opt/coverage/instrument_coverage.rs
similarity index 95%
rename from tests/mir-opt/instrument_coverage.rs
rename to tests/mir-opt/coverage/instrument_coverage.rs
index ae63990..beb88b6 100644
--- a/tests/mir-opt/instrument_coverage.rs
+++ b/tests/mir-opt/coverage/instrument_coverage.rs
@@ -2,7 +2,7 @@
 // The Coverage::CounterIncrement statements are later converted into LLVM
 // instrprof.increment intrinsics, during codegen.
 
-//@ unit-test: InstrumentCoverage
+//@ test-mir-pass: InstrumentCoverage
 //@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime
 
 // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff
diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff
similarity index 100%
rename from tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff
rename to tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff
diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff
similarity index 100%
rename from tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff
rename to tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff
diff --git a/tests/mir-opt/instrument_coverage_cleanup.rs b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs
similarity index 95%
rename from tests/mir-opt/instrument_coverage_cleanup.rs
rename to tests/mir-opt/coverage/instrument_coverage_cleanup.rs
index 7db7633..acc544a 100644
--- a/tests/mir-opt/instrument_coverage_cleanup.rs
+++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs
@@ -5,7 +5,7 @@
 // Removed statement kinds: BlockMarker, SpanMarker
 // Retained statement kinds: CounterIncrement, ExpressionUsed
 
-//@ unit-test: InstrumentCoverage
+//@ test-mir-pass: InstrumentCoverage
 //@ compile-flags: -Cinstrument-coverage -Zcoverage-options=branch -Zno-profiler-runtime
 
 // EMIT_MIR instrument_coverage_cleanup.main.InstrumentCoverage.diff
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.rs b/tests/mir-opt/dataflow-const-prop/array_index.rs
index df8baf7..daf9c77 100644
--- a/tests/mir-opt/dataflow-const-prop/array_index.rs
+++ b/tests/mir-opt/dataflow-const-prop/array_index.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 // EMIT_MIR array_index.main.DataflowConstProp.diff
diff --git a/tests/mir-opt/dataflow-const-prop/boolean_identities.rs b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs
index e2b7dbc..11faece 100644
--- a/tests/mir-opt/dataflow-const-prop/boolean_identities.rs
+++ b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // EMIT_MIR boolean_identities.test.DataflowConstProp.diff
 
diff --git a/tests/mir-opt/dataflow-const-prop/cast.rs b/tests/mir-opt/dataflow-const-prop/cast.rs
index bd6141e..a70cc8e 100644
--- a/tests/mir-opt/dataflow-const-prop/cast.rs
+++ b/tests/mir-opt/dataflow-const-prop/cast.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // EMIT_MIR cast.main.DataflowConstProp.diff
 
diff --git a/tests/mir-opt/dataflow-const-prop/checked.rs b/tests/mir-opt/dataflow-const-prop/checked.rs
index d3d0938..a736934 100644
--- a/tests/mir-opt/dataflow-const-prop/checked.rs
+++ b/tests/mir-opt/dataflow-const-prop/checked.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 //@ compile-flags: -Coverflow-checks=on
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
index 44e73b5..8005bc2 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
@@ -12,16 +12,13 @@
           let _3: std::ptr::Unique<[bool]>;
           let mut _4: std::ptr::Unique<[bool; 0]>;
           scope 3 {
-              debug ptr => _3;
           }
           scope 4 (inlined Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
                   let _6: *mut [bool; 0];
                   scope 6 {
-                      debug ptr => _6;
                       scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
-                          debug ptr => _6;
                           let mut _8: bool;
                           let _9: ();
                           let mut _10: *mut ();
@@ -37,7 +34,6 @@
                       scope 8 (inlined align_of::<[bool; 0]>) {
                       }
                       scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
-                          debug addr => _7;
                       }
                   }
               }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
index 6cef8b6..42b1be3 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -12,16 +12,13 @@
           let _3: std::ptr::Unique<[bool]>;
           let mut _4: std::ptr::Unique<[bool; 0]>;
           scope 3 {
-              debug ptr => _3;
           }
           scope 4 (inlined Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
                   let _6: *mut [bool; 0];
                   scope 6 {
-                      debug ptr => _6;
                       scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
-                          debug ptr => _6;
                           let mut _8: bool;
                           let _9: ();
                           let mut _10: *mut ();
@@ -37,7 +34,6 @@
                       scope 8 (inlined align_of::<[bool; 0]>) {
                       }
                       scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
-                          debug addr => _7;
                       }
                   }
               }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
index 6efccde..7b57b0d 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
@@ -12,16 +12,13 @@
           let _3: std::ptr::Unique<[bool]>;
           let mut _4: std::ptr::Unique<[bool; 0]>;
           scope 3 {
-              debug ptr => _3;
           }
           scope 4 (inlined Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
                   let _6: *mut [bool; 0];
                   scope 6 {
-                      debug ptr => _6;
                       scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
-                          debug ptr => _6;
                           let mut _8: bool;
                           let _9: ();
                           let mut _10: *mut ();
@@ -37,7 +34,6 @@
                       scope 8 (inlined align_of::<[bool; 0]>) {
                       }
                       scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
-                          debug addr => _7;
                       }
                   }
               }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
index a705d00..2e75a63 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -12,16 +12,13 @@
           let _3: std::ptr::Unique<[bool]>;
           let mut _4: std::ptr::Unique<[bool; 0]>;
           scope 3 {
-              debug ptr => _3;
           }
           scope 4 (inlined Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
                   let _6: *mut [bool; 0];
                   scope 6 {
-                      debug ptr => _6;
                       scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
-                          debug ptr => _6;
                           let mut _8: bool;
                           let _9: ();
                           let mut _10: *mut ();
@@ -37,7 +34,6 @@
                       scope 8 (inlined align_of::<[bool; 0]>) {
                       }
                       scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
-                          debug addr => _7;
                       }
                   }
               }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
index f9c854c..ca44504 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
@@ -12,16 +12,13 @@
           let _3: std::ptr::Unique<[bool]>;
           let mut _4: std::ptr::Unique<[bool; 0]>;
           scope 3 {
-              debug ptr => _3;
           }
           scope 4 (inlined Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
                   let _6: *mut [bool; 0];
                   scope 6 {
-                      debug ptr => _6;
                       scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
-                          debug ptr => _6;
                           let mut _8: bool;
                           let _9: ();
                           let mut _10: *mut ();
@@ -37,7 +34,6 @@
                       scope 8 (inlined align_of::<[bool; 0]>) {
                       }
                       scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
-                          debug addr => _7;
                       }
                   }
               }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
index 3337266..1f498c6 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
@@ -12,16 +12,13 @@
           let _3: std::ptr::Unique<[bool]>;
           let mut _4: std::ptr::Unique<[bool; 0]>;
           scope 3 {
-              debug ptr => _3;
           }
           scope 4 (inlined Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
                   let _6: *mut [bool; 0];
                   scope 6 {
-                      debug ptr => _6;
                       scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
-                          debug ptr => _6;
                           let mut _8: bool;
                           let _9: ();
                           let mut _10: *mut ();
@@ -37,7 +34,6 @@
                       scope 8 (inlined align_of::<[bool; 0]>) {
                       }
                       scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
-                          debug addr => _7;
                       }
                   }
               }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
index e184176..da72e58 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
@@ -12,16 +12,13 @@
           let _3: std::ptr::Unique<[bool]>;
           let mut _4: std::ptr::Unique<[bool; 0]>;
           scope 3 {
-              debug ptr => _3;
           }
           scope 4 (inlined Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
                   let _6: *mut [bool; 0];
                   scope 6 {
-                      debug ptr => _6;
                       scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
-                          debug ptr => _6;
                           let mut _8: bool;
                           let _9: ();
                           let mut _10: *mut ();
@@ -37,7 +34,6 @@
                       scope 8 (inlined align_of::<[bool; 0]>) {
                       }
                       scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
-                          debug addr => _7;
                       }
                   }
               }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
index 7aa02556..920e664 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
@@ -12,16 +12,13 @@
           let _3: std::ptr::Unique<[bool]>;
           let mut _4: std::ptr::Unique<[bool; 0]>;
           scope 3 {
-              debug ptr => _3;
           }
           scope 4 (inlined Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
                   let _6: *mut [bool; 0];
                   scope 6 {
-                      debug ptr => _6;
                       scope 10 (inlined NonNull::<[bool; 0]>::new_unchecked) {
-                          debug ptr => _6;
                           let mut _8: bool;
                           let _9: ();
                           let mut _10: *mut ();
@@ -37,7 +34,6 @@
                       scope 8 (inlined align_of::<[bool; 0]>) {
                       }
                       scope 9 (inlined without_provenance_mut::<[bool; 0]>) {
-                          debug addr => _7;
                       }
                   }
               }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs
index 6175012..3a0cbac 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 //@ compile-flags: -Zmir-enable-passes=+GVN,+Inline
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
diff --git a/tests/mir-opt/dataflow-const-prop/enum.rs b/tests/mir-opt/dataflow-const-prop/enum.rs
index 8275275..5c52f92 100644
--- a/tests/mir-opt/dataflow-const-prop/enum.rs
+++ b/tests/mir-opt/dataflow-const-prop/enum.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 #![feature(custom_mir, core_intrinsics, rustc_attrs)]
diff --git a/tests/mir-opt/dataflow-const-prop/if.rs b/tests/mir-opt/dataflow-const-prop/if.rs
index 7df3bb9..8cd8b2c 100644
--- a/tests/mir-opt/dataflow-const-prop/if.rs
+++ b/tests/mir-opt/dataflow-const-prop/if.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // EMIT_MIR if.main.DataflowConstProp.diff
 // CHECK-LABEL: fn main(
diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-abort.diff
index 09fc480..ca1bd73 100644
--- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-abort.diff
@@ -9,8 +9,6 @@
       scope 1 {
       }
       scope 2 (inlined #[track_caller] <u8 as Add>::add) {
-          debug self => _2;
-          debug other => _3;
           let mut _4: (u8, bool);
       }
   
diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-unwind.diff
index c0b2608..0d7fe93 100644
--- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-unwind.diff
@@ -9,8 +9,6 @@
       scope 1 {
       }
       scope 2 (inlined #[track_caller] <u8 as Add>::add) {
-          debug self => _2;
-          debug other => _3;
           let mut _4: (u8, bool);
       }
   
diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs b/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs
index d0063a8..7ac59be 100644
--- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs
+++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 //@ compile-flags: -Zmir-enable-passes=+Inline
 
 // EMIT_MIR inherit_overflow.main.DataflowConstProp.diff
diff --git a/tests/mir-opt/dataflow-const-prop/issue_81605.rs b/tests/mir-opt/dataflow-const-prop/issue_81605.rs
index 9231bb2..e7960e3 100644
--- a/tests/mir-opt/dataflow-const-prop/issue_81605.rs
+++ b/tests/mir-opt/dataflow-const-prop/issue_81605.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // EMIT_MIR issue_81605.f.DataflowConstProp.diff
 
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.rs b/tests/mir-opt/dataflow-const-prop/large_array_index.rs
index 3a4159a..e74fd88 100644
--- a/tests/mir-opt/dataflow-const-prop/large_array_index.rs
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
diff --git a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs
index b15fba2..3cd0b71 100644
--- a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs
+++ b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // EMIT_MIR mult_by_zero.test.DataflowConstProp.diff
 // CHECK-LABEL: fn test(
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.rs b/tests/mir-opt/dataflow-const-prop/offset_of.rs
index 867890d..cd4e1f6 100644
--- a/tests/mir-opt/dataflow-const-prop/offset_of.rs
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 #![feature(offset_of_nested)]
diff --git a/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs b/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs
index aa669ff..399de92 100644
--- a/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs
+++ b/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 #[inline(never)]
 fn escape<T>(x: &T) {}
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.rs b/tests/mir-opt/dataflow-const-prop/repeat.rs
index bebedbb..e32c0d0 100644
--- a/tests/mir-opt/dataflow-const-prop/repeat.rs
+++ b/tests/mir-opt/dataflow-const-prop/repeat.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
diff --git a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
index ace3836..6152724 100644
--- a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
+++ b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // The struct has scalar ABI, but is not a scalar type.
 // Make sure that we handle this correctly.
diff --git a/tests/mir-opt/dataflow-const-prop/self_assign.rs b/tests/mir-opt/dataflow-const-prop/self_assign.rs
index 4171d29..f5897bf 100644
--- a/tests/mir-opt/dataflow-const-prop/self_assign.rs
+++ b/tests/mir-opt/dataflow-const-prop/self_assign.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // EMIT_MIR self_assign.main.DataflowConstProp.diff
 
diff --git a/tests/mir-opt/dataflow-const-prop/self_assign_add.rs b/tests/mir-opt/dataflow-const-prop/self_assign_add.rs
index d958025..11fe384 100644
--- a/tests/mir-opt/dataflow-const-prop/self_assign_add.rs
+++ b/tests/mir-opt/dataflow-const-prop/self_assign_add.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // EMIT_MIR self_assign_add.main.DataflowConstProp.diff
 
diff --git a/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs b/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs
index ad24c585..be7f311 100644
--- a/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs
+++ b/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs
@@ -6,7 +6,7 @@
 // used to modify `x.1` - if it did not, then it might incorrectly assume that it
 // can infer the value of `x.1` at the end of this function.
 
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 // EMIT_MIR sibling_ptr.main.DataflowConstProp.diff
 
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.rs b/tests/mir-opt/dataflow-const-prop/slice_len.rs
index 0870777..5d9733f 100644
--- a/tests/mir-opt/dataflow-const-prop/slice_len.rs
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 //@ compile-flags: -Zmir-enable-passes=+InstSimplify
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
diff --git a/tests/mir-opt/dataflow-const-prop/struct.rs b/tests/mir-opt/dataflow-const-prop/struct.rs
index 0180e97..eed782c9 100644
--- a/tests/mir-opt/dataflow-const-prop/struct.rs
+++ b/tests/mir-opt/dataflow-const-prop/struct.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 #[derive(Copy, Clone)]
diff --git a/tests/mir-opt/dataflow-const-prop/terminator.rs b/tests/mir-opt/dataflow-const-prop/terminator.rs
index d33f321..aac5d11 100644
--- a/tests/mir-opt/dataflow-const-prop/terminator.rs
+++ b/tests/mir-opt/dataflow-const-prop/terminator.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 
 fn foo(n: i32) {}
 
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.rs b/tests/mir-opt/dataflow-const-prop/transmute.rs
index 7cf0dad..e7f93f4 100644
--- a/tests/mir-opt/dataflow-const-prop/transmute.rs
+++ b/tests/mir-opt/dataflow-const-prop/transmute.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 //@ compile-flags: -O --crate-type=lib
 //@ ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
diff --git a/tests/mir-opt/dataflow-const-prop/tuple.rs b/tests/mir-opt/dataflow-const-prop/tuple.rs
index 5d7c389..d624e21 100644
--- a/tests/mir-opt/dataflow-const-prop/tuple.rs
+++ b/tests/mir-opt/dataflow-const-prop/tuple.rs
@@ -1,4 +1,4 @@
-//@ unit-test: DataflowConstProp
+//@ test-mir-pass: DataflowConstProp
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 // EMIT_MIR tuple.main.DataflowConstProp.diff
diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs
index 2ce1e90..edb3506 100644
--- a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs
+++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: DeadStoreElimination-final
+//@ test-mir-pass: DeadStoreElimination-final
 //@ compile-flags: -Zmir-enable-passes=+CopyProp
 
 #![feature(core_intrinsics)]
diff --git a/tests/mir-opt/dead-store-elimination/cycle.rs b/tests/mir-opt/dead-store-elimination/cycle.rs
index ddbc89a..795d57d 100644
--- a/tests/mir-opt/dead-store-elimination/cycle.rs
+++ b/tests/mir-opt/dead-store-elimination/cycle.rs
@@ -2,7 +2,7 @@
 // report that *all* of these stores are live.
 //
 //@ needs-unwind
-//@ unit-test: DeadStoreElimination-initial
+//@ test-mir-pass: DeadStoreElimination-initial
 
 #![feature(core_intrinsics, custom_mir)]
 use std::intrinsics::mir::*;
diff --git a/tests/mir-opt/dead-store-elimination/place_mention.rs b/tests/mir-opt/dead-store-elimination/place_mention.rs
index d276f6f..5e4a286 100644
--- a/tests/mir-opt/dead-store-elimination/place_mention.rs
+++ b/tests/mir-opt/dead-store-elimination/place_mention.rs
@@ -1,7 +1,7 @@
 // Verify that we account for the `PlaceMention` statement as a use of the tuple,
 // and don't remove it as a dead store.
 //
-//@ unit-test: DeadStoreElimination-initial
+//@ test-mir-pass: DeadStoreElimination-initial
 //@ compile-flags: -Zmir-keep-place-mention
 
 // EMIT_MIR place_mention.main.DeadStoreElimination-initial.diff
diff --git a/tests/mir-opt/dead-store-elimination/provenance_soundness.rs b/tests/mir-opt/dead-store-elimination/provenance_soundness.rs
index 20517a0..b252368 100644
--- a/tests/mir-opt/dead-store-elimination/provenance_soundness.rs
+++ b/tests/mir-opt/dead-store-elimination/provenance_soundness.rs
@@ -1,5 +1,5 @@
 // Test that we don't remove pointer to int casts or retags
-//@ unit-test: DeadStoreElimination-initial
+//@ test-mir-pass: DeadStoreElimination-initial
 //@ compile-flags: -Zmir-emit-retag
 
 // EMIT_MIR provenance_soundness.pointer_to_int.DeadStoreElimination-initial.diff
diff --git a/tests/mir-opt/deduplicate_blocks.rs b/tests/mir-opt/deduplicate_blocks.rs
index 7979fdf..3a164cb 100644
--- a/tests/mir-opt/deduplicate_blocks.rs
+++ b/tests/mir-opt/deduplicate_blocks.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: DeduplicateBlocks
+//@ test-mir-pass: DeduplicateBlocks
 
 // EMIT_MIR deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff
 pub const fn is_line_doc_comment_2(s: &str) -> bool {
diff --git a/tests/mir-opt/derefer_complex_case.rs b/tests/mir-opt/derefer_complex_case.rs
index bdaf83f..b1fa2c8 100644
--- a/tests/mir-opt/derefer_complex_case.rs
+++ b/tests/mir-opt/derefer_complex_case.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: Derefer
+//@ test-mir-pass: Derefer
 // EMIT_MIR derefer_complex_case.main.Derefer.diff
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
diff --git a/tests/mir-opt/derefer_inline_test.rs b/tests/mir-opt/derefer_inline_test.rs
index 89de514..7f9272b 100644
--- a/tests/mir-opt/derefer_inline_test.rs
+++ b/tests/mir-opt/derefer_inline_test.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: Derefer
+//@ test-mir-pass: Derefer
 // EMIT_MIR derefer_inline_test.main.Derefer.diff
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
diff --git a/tests/mir-opt/derefer_terminator_test.rs b/tests/mir-opt/derefer_terminator_test.rs
index e225db5..5de6a61 100644
--- a/tests/mir-opt/derefer_terminator_test.rs
+++ b/tests/mir-opt/derefer_terminator_test.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: Derefer
+//@ test-mir-pass: Derefer
 // EMIT_MIR derefer_terminator_test.main.Derefer.diff
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
diff --git a/tests/mir-opt/derefer_test.rs b/tests/mir-opt/derefer_test.rs
index e30a286..3ca2144 100644
--- a/tests/mir-opt/derefer_test.rs
+++ b/tests/mir-opt/derefer_test.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: Derefer
+//@ test-mir-pass: Derefer
 // EMIT_MIR derefer_test.main.Derefer.diff
 fn main() {
     let mut a = (42,43);
diff --git a/tests/mir-opt/derefer_test_multiple.rs b/tests/mir-opt/derefer_test_multiple.rs
index 4efc735..145a19e 100644
--- a/tests/mir-opt/derefer_test_multiple.rs
+++ b/tests/mir-opt/derefer_test_multiple.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: Derefer
+//@ test-mir-pass: Derefer
 // EMIT_MIR derefer_test_multiple.main.Derefer.diff
 fn main () {
     let mut a = (42, 43);
diff --git a/tests/mir-opt/dest-prop/branch.rs b/tests/mir-opt/dest-prop/branch.rs
index cd55130..481d413 100644
--- a/tests/mir-opt/dest-prop/branch.rs
+++ b/tests/mir-opt/dest-prop/branch.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //! Tests that assignment in both branches of an `if` are eliminated.
-//@ unit-test: DestinationPropagation
+//@ test-mir-pass: DestinationPropagation
 fn val() -> i32 {
     1
 }
diff --git a/tests/mir-opt/dest-prop/copy_propagation_arg.rs b/tests/mir-opt/dest-prop/copy_propagation_arg.rs
index f84b5fd..db49699 100644
--- a/tests/mir-opt/dest-prop/copy_propagation_arg.rs
+++ b/tests/mir-opt/dest-prop/copy_propagation_arg.rs
@@ -2,7 +2,7 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // Check that DestinationPropagation does not propagate an assignment to a function argument
 // (doing so can break usages of the original argument value)
-//@ unit-test: DestinationPropagation
+//@ test-mir-pass: DestinationPropagation
 fn dummy(x: u8) -> u8 {
     x
 }
diff --git a/tests/mir-opt/dest-prop/cycle.rs b/tests/mir-opt/dest-prop/cycle.rs
index e666395..e414daf 100644
--- a/tests/mir-opt/dest-prop/cycle.rs
+++ b/tests/mir-opt/dest-prop/cycle.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //! Tests that cyclic assignments don't hang DestinationPropagation, and result in reasonable code.
-//@ unit-test: DestinationPropagation
+//@ test-mir-pass: DestinationPropagation
 fn val() -> i32 {
     1
 }
diff --git a/tests/mir-opt/dest-prop/dead_stores_79191.rs b/tests/mir-opt/dest-prop/dead_stores_79191.rs
index b3e3709..5c218a3 100644
--- a/tests/mir-opt/dest-prop/dead_stores_79191.rs
+++ b/tests/mir-opt/dest-prop/dead_stores_79191.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: DestinationPropagation
+//@ test-mir-pass: DestinationPropagation
 
 fn id<T>(x: T) -> T {
     x
diff --git a/tests/mir-opt/dest-prop/dead_stores_better.rs b/tests/mir-opt/dest-prop/dead_stores_better.rs
index c241d71..06445dc 100644
--- a/tests/mir-opt/dest-prop/dead_stores_better.rs
+++ b/tests/mir-opt/dest-prop/dead_stores_better.rs
@@ -3,7 +3,7 @@
 // This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates
 // that that pass enables this one to do more optimizations.
 
-//@ unit-test: DestinationPropagation
+//@ test-mir-pass: DestinationPropagation
 //@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination
 
 fn id<T>(x: T) -> T {
diff --git a/tests/mir-opt/dest-prop/simple.rs b/tests/mir-opt/dest-prop/simple.rs
index 4aa6b6a..8e5d634 100644
--- a/tests/mir-opt/dest-prop/simple.rs
+++ b/tests/mir-opt/dest-prop/simple.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //! Copy of `nrvo-simple.rs`, to ensure that full dest-prop handles it too.
-//@ unit-test: DestinationPropagation
+//@ test-mir-pass: DestinationPropagation
 // EMIT_MIR simple.nrvo.DestinationPropagation.diff
 fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] {
     let mut buf = [0; 1024];
diff --git a/tests/mir-opt/dest-prop/union.rs b/tests/mir-opt/dest-prop/union.rs
index abd1f1b..66fadd8 100644
--- a/tests/mir-opt/dest-prop/union.rs
+++ b/tests/mir-opt/dest-prop/union.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //! Tests that we can propagate into places that are projections into unions
-//@ compile-flags: -Zunsound-mir-opts
+//@ compile-flags: -Zunsound-mir-opts -C debuginfo=full
 fn val() -> u32 {
     1
 }
diff --git a/tests/mir-opt/dont_inline_type_id.rs b/tests/mir-opt/dont_inline_type_id.rs
index ae72eb1..ab74894 100644
--- a/tests/mir-opt/dont_inline_type_id.rs
+++ b/tests/mir-opt/dont_inline_type_id.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: Inline
+//@ test-mir-pass: Inline
 //@ compile-flags: --crate-type=lib -C panic=abort
 
 use std::any::Any;
diff --git a/tests/mir-opt/early_otherwise_branch.rs b/tests/mir-opt/early_otherwise_branch.rs
index bfeb1f7..b047c50 100644
--- a/tests/mir-opt/early_otherwise_branch.rs
+++ b/tests/mir-opt/early_otherwise_branch.rs
@@ -1,4 +1,4 @@
-//@ unit-test: EarlyOtherwiseBranch
+//@ test-mir-pass: EarlyOtherwiseBranch
 //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching
 
 enum Option2<T> {
diff --git a/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs b/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs
index 2d21562..d2a3e1f 100644
--- a/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs
+++ b/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs
@@ -1,4 +1,4 @@
-//@ unit-test: EarlyOtherwiseBranch
+//@ test-mir-pass: EarlyOtherwiseBranch
 //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching
 
 enum Option2<T> {
diff --git a/tests/mir-opt/early_otherwise_branch_68867.rs b/tests/mir-opt/early_otherwise_branch_68867.rs
index 59bc19c..789b5eb 100644
--- a/tests/mir-opt/early_otherwise_branch_68867.rs
+++ b/tests/mir-opt/early_otherwise_branch_68867.rs
@@ -1,4 +1,4 @@
-//@ unit-test: EarlyOtherwiseBranch
+//@ test-mir-pass: EarlyOtherwiseBranch
 //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching
 
 // FIXME: This test was broken by the derefer change.
diff --git a/tests/mir-opt/early_otherwise_branch_noopt.rs b/tests/mir-opt/early_otherwise_branch_noopt.rs
index 6b48393..307c6e5 100644
--- a/tests/mir-opt/early_otherwise_branch_noopt.rs
+++ b/tests/mir-opt/early_otherwise_branch_noopt.rs
@@ -1,4 +1,4 @@
-//@ unit-test: EarlyOtherwiseBranch
+//@ test-mir-pass: EarlyOtherwiseBranch
 //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching
 
 // must not optimize as it does not follow the pattern of
diff --git a/tests/mir-opt/early_otherwise_branch_soundness.rs b/tests/mir-opt/early_otherwise_branch_soundness.rs
index 74a2af8..a22be31 100644
--- a/tests/mir-opt/early_otherwise_branch_soundness.rs
+++ b/tests/mir-opt/early_otherwise_branch_soundness.rs
@@ -1,4 +1,4 @@
-//@ unit-test: EarlyOtherwiseBranch
+//@ test-mir-pass: EarlyOtherwiseBranch
 //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching
 
 // Tests various cases that the `early_otherwise_branch` opt should *not* optimize
diff --git a/tests/mir-opt/enum_opt.rs b/tests/mir-opt/enum_opt.rs
index c5b3e61..cacc730 100644
--- a/tests/mir-opt/enum_opt.rs
+++ b/tests/mir-opt/enum_opt.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: EnumSizeOpt
+//@ test-mir-pass: EnumSizeOpt
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 //@ compile-flags: -Zunsound-mir-opts
 
diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-abort.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-abort.diff
index bd346af..8a70164 100644
--- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-abort.diff
+++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-abort.diff
@@ -29,13 +29,11 @@
                   debug precision => _8;
                   let _8: usize;
                   scope 5 (inlined Formatter::<'_>::precision) {
-                      debug self => _1;
                   }
               }
           }
       }
       scope 4 (inlined Formatter::<'_>::sign_plus) {
-          debug self => _1;
           let mut _20: u32;
           let mut _21: u32;
       }
diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-unwind.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-unwind.diff
index 422cbea..5e65700 100644
--- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-unwind.diff
+++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-unwind.diff
@@ -29,13 +29,11 @@
                   debug precision => _8;
                   let _8: usize;
                   scope 5 (inlined Formatter::<'_>::precision) {
-                      debug self => _1;
                   }
               }
           }
       }
       scope 4 (inlined Formatter::<'_>::sign_plus) {
-          debug self => _1;
           let mut _20: u32;
           let mut _21: u32;
       }
diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs
index 6f4d1e3..0484710 100644
--- a/tests/mir-opt/gvn.rs
+++ b/tests/mir-opt/gvn.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //@ only-64bit
 
diff --git a/tests/mir-opt/gvn_copy_moves.rs b/tests/mir-opt/gvn_copy_moves.rs
index 9d83a19..1812de1 100644
--- a/tests/mir-opt/gvn_copy_moves.rs
+++ b/tests/mir-opt/gvn_copy_moves.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 
 #![feature(custom_mir, core_intrinsics)]
 extern crate core;
diff --git a/tests/mir-opt/gvn_uninhabited.rs b/tests/mir-opt/gvn_uninhabited.rs
index 5f9df79..015949c 100644
--- a/tests/mir-opt/gvn_uninhabited.rs
+++ b/tests/mir-opt/gvn_uninhabited.rs
@@ -1,4 +1,4 @@
-//@ unit-test: GVN
+//@ test-mir-pass: GVN
 //@ compile-flags: -O
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // skip-filecheck
diff --git a/tests/mir-opt/if_condition_int.rs b/tests/mir-opt/if_condition_int.rs
index 2f3f643..4cc2c2b 100644
--- a/tests/mir-opt/if_condition_int.rs
+++ b/tests/mir-opt/if_condition_int.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: SimplifyComparisonIntegral
+//@ test-mir-pass: SimplifyComparisonIntegral
 // EMIT_MIR if_condition_int.opt_u32.SimplifyComparisonIntegral.diff
 // EMIT_MIR if_condition_int.opt_negative.SimplifyComparisonIntegral.diff
 // EMIT_MIR if_condition_int.opt_char.SimplifyComparisonIntegral.diff
diff --git a/tests/mir-opt/inline/asm_unwind.rs b/tests/mir-opt/inline/asm_unwind.rs
index 7708f56..8a102b2 100644
--- a/tests/mir-opt/inline/asm_unwind.rs
+++ b/tests/mir-opt/inline/asm_unwind.rs
@@ -3,7 +3,7 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //@ needs-asm-support
 //@ needs-unwind
-//@ compile-flags: -Zinline-mir-hint-threshold=1000
+//@ compile-flags: -Zinline-mir-hint-threshold=1000 -C debuginfo=full
 #![feature(asm_unwind)]
 
 struct D;
diff --git a/tests/mir-opt/inline/cycle.rs b/tests/mir-opt/inline/cycle.rs
index b2eacfe..cb50638 100644
--- a/tests/mir-opt/inline/cycle.rs
+++ b/tests/mir-opt/inline/cycle.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ compile-flags: -Zinline-mir-hint-threshold=1000
+//@ compile-flags: -Zinline-mir-hint-threshold=1000 -C debuginfo=full
 
 // EMIT_MIR cycle.f.Inline.diff
 #[inline(always)]
diff --git a/tests/mir-opt/inline/dyn_trait.rs b/tests/mir-opt/inline/dyn_trait.rs
index ecf220a..d991403 100644
--- a/tests/mir-opt/inline/dyn_trait.rs
+++ b/tests/mir-opt/inline/dyn_trait.rs
@@ -1,4 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+//@ compile-flags: -C debuginfo=full
 #![crate_type = "lib"]
 
 use std::fmt::Debug;
diff --git a/tests/mir-opt/inline/indirect_destination.rs b/tests/mir-opt/inline/indirect_destination.rs
index 337f617..4246eef 100644
--- a/tests/mir-opt/inline/indirect_destination.rs
+++ b/tests/mir-opt/inline/indirect_destination.rs
@@ -1,6 +1,6 @@
 // Test for inlining with an indirect destination place.
 //
-//@ unit-test: Inline
+//@ test-mir-pass: Inline
 //@ edition: 2021
 //@ needs-unwind
 #![crate_type = "lib"]
diff --git a/tests/mir-opt/inline/inline_any_operand.rs b/tests/mir-opt/inline/inline_any_operand.rs
index 8151949..b34f71a 100644
--- a/tests/mir-opt/inline/inline_any_operand.rs
+++ b/tests/mir-opt/inline/inline_any_operand.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Z span_free_formats
+//@ compile-flags: -Z span_free_formats -C debuginfo=full
 
 // Tests that MIR inliner works for any operand
 
diff --git a/tests/mir-opt/inline/inline_box_fn.rs b/tests/mir-opt/inline/inline_box_fn.rs
index 3e00601..bb2da3a 100644
--- a/tests/mir-opt/inline/inline_box_fn.rs
+++ b/tests/mir-opt/inline/inline_box_fn.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: Inline
+//@ test-mir-pass: Inline
 //@ compile-flags: --crate-type=lib
 
 // EMIT_MIR inline_box_fn.call.Inline.diff
diff --git a/tests/mir-opt/inline/inline_closure.rs b/tests/mir-opt/inline/inline_closure.rs
index 9a3cf54..a595495 100644
--- a/tests/mir-opt/inline/inline_closure.rs
+++ b/tests/mir-opt/inline/inline_closure.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Z span_free_formats
+//@ compile-flags: -Z span_free_formats -C debuginfo=full
 
 // Tests that MIR inliner can handle closure arguments. (#45894)
 
diff --git a/tests/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir b/tests/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir
index 6e77a9b..a6198ca 100644
--- a/tests/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir
+++ b/tests/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir
@@ -14,10 +14,7 @@
     scope 1 {
         debug x => _3;
         scope 2 (inlined foo::<T>::{closure#0}) {
-            debug r => _8;
-            debug _s => _9;
             scope 3 {
-                debug variable => _8;
             }
         }
     }
diff --git a/tests/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir b/tests/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
index 10b81e5..2f9d28e 100644
--- a/tests/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
+++ b/tests/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
@@ -14,9 +14,6 @@
     scope 1 {
         debug x => _3;
         scope 2 (inlined foo::<T>::{closure#0}) {
-            debug _q => _9;
-            debug q => (*((*_6).0: &i32));
-            debug t => (*((*_6).1: &T));
             let mut _10: &i32;
             let mut _11: i32;
             let mut _12: &T;
diff --git a/tests/mir-opt/inline/inline_coroutine.rs b/tests/mir-opt/inline/inline_coroutine.rs
index 9500c22..180f9d4 100644
--- a/tests/mir-opt/inline/inline_coroutine.rs
+++ b/tests/mir-opt/inline/inline_coroutine.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ compile-flags: -Zinline-mir-hint-threshold=1000
+//@ compile-flags: -Zinline-mir-hint-threshold=1000 -C debuginfo=full
 #![feature(coroutines, coroutine_trait)]
 
 use std::ops::Coroutine;
diff --git a/tests/mir-opt/inline/inline_cycle.rs b/tests/mir-opt/inline/inline_cycle.rs
index e3dd082..1826e38 100644
--- a/tests/mir-opt/inline/inline_cycle.rs
+++ b/tests/mir-opt/inline/inline_cycle.rs
@@ -1,4 +1,5 @@
 // skip-filecheck
+//@ compile-flags: -C debuginfo=full
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // Check that inliner handles various forms of recursion and doesn't fall into
 // an infinite inlining cycle. The particular outcome of inlining is not
diff --git a/tests/mir-opt/inline/inline_diverging.rs b/tests/mir-opt/inline/inline_diverging.rs
index 89ef0fc..e1cea36 100644
--- a/tests/mir-opt/inline/inline_diverging.rs
+++ b/tests/mir-opt/inline/inline_diverging.rs
@@ -1,7 +1,7 @@
 // Tests inlining of diverging calls.
 //
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ compile-flags: -Zinline-mir-hint-threshold=1000
+//@ compile-flags: -Zinline-mir-hint-threshold=1000 -C debuginfo=full
 #![crate_type = "lib"]
 
 // EMIT_MIR inline_diverging.f.Inline.diff
diff --git a/tests/mir-opt/inline/inline_retag.rs b/tests/mir-opt/inline/inline_retag.rs
index 5cf7f76..81e6363 100644
--- a/tests/mir-opt/inline/inline_retag.rs
+++ b/tests/mir-opt/inline/inline_retag.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Z span_free_formats -Z mir-emit-retag
+//@ compile-flags: -Z span_free_formats -Z mir-emit-retag -C debuginfo=full
 
 // Tests that MIR inliner fixes up `Retag`'s `fn_entry` flag
 
diff --git a/tests/mir-opt/inline/inline_trait_method_2.rs b/tests/mir-opt/inline/inline_trait_method_2.rs
index c3a71e6..2ca71af 100644
--- a/tests/mir-opt/inline/inline_trait_method_2.rs
+++ b/tests/mir-opt/inline/inline_trait_method_2.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ compile-flags: -Z span_free_formats -Z mir-opt-level=4
+//@ compile-flags: -Z span_free_formats -Z mir-opt-level=4 -C debuginfo=full
 
 // EMIT_MIR inline_trait_method_2.test2.Inline.after.mir
 fn test2(x: &dyn X) -> bool {
diff --git a/tests/mir-opt/inline/issue_106141.rs b/tests/mir-opt/inline/issue_106141.rs
index 592b4d9..1cca7d7 100644
--- a/tests/mir-opt/inline/issue_106141.rs
+++ b/tests/mir-opt/inline/issue_106141.rs
@@ -1,3 +1,4 @@
+//@ compile-flags: -C debuginfo=full
 // Verify that we do not ICE inlining a function which uses _0 as an index.
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
diff --git a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.rs b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.rs
index 4517c88..d8a5c47 100644
--- a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.rs
+++ b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.rs
@@ -1,3 +1,5 @@
+//@ compile-flags: -C debuginfo=full
+
 // EMIT_MIR issue_58867_inline_as_ref_as_mut.a.Inline.after.mir
 pub fn a<T>(x: &mut [T]) -> &mut [T] {
     // CHECK-LABEL: fn a(
diff --git a/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir b/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir
index ba4f91b..63d3ae2 100644
--- a/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir
+++ b/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir
@@ -2,8 +2,8 @@
 
 fn main() -> () {
     let mut _0: ();
-    let _1: {closure@$DIR/issue_76997_inline_scopes_parenting.rs:15:13: 15:16};
-    let mut _2: &{closure@$DIR/issue_76997_inline_scopes_parenting.rs:15:13: 15:16};
+    let _1: {closure@$DIR/issue_76997_inline_scopes_parenting.rs:16:13: 16:16};
+    let mut _2: &{closure@$DIR/issue_76997_inline_scopes_parenting.rs:16:13: 16:16};
     let mut _3: ((),);
     let mut _4: ();
     let mut _5: ();
@@ -19,7 +19,7 @@
 
     bb0: {
         StorageLive(_1);
-        _1 = {closure@$DIR/issue_76997_inline_scopes_parenting.rs:15:13: 15:16};
+        _1 = {closure@$DIR/issue_76997_inline_scopes_parenting.rs:16:13: 16:16};
         StorageLive(_2);
         _2 = &_1;
         StorageLive(_3);
diff --git a/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.rs b/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.rs
index 2fb363c..75772c1 100644
--- a/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.rs
+++ b/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.rs
@@ -1,3 +1,4 @@
+//@ compile-flags: -C debuginfo=full
 // Tests that MIR inliner can handle `SourceScopeData` parenting correctly. (#76997)
 
 // EMIT_MIR issue_76997_inline_scopes_parenting.main.Inline.after.mir
diff --git a/tests/mir-opt/inline/issue_78442.rs b/tests/mir-opt/inline/issue_78442.rs
index 2fbe0c6..6dc875f 100644
--- a/tests/mir-opt/inline/issue_78442.rs
+++ b/tests/mir-opt/inline/issue_78442.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Z mir-opt-level=3 -Z inline-mir
+//@ compile-flags: -Z mir-opt-level=3 -Z inline-mir -C debuginfo=full
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 #![crate_type = "lib"]
 
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
index cc1b8b9..a624ab7 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
@@ -8,8 +8,6 @@
       let mut _3: u16;
       let mut _4: u32;
 +     scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
-+         debug self => _3;
-+         debug rhs => _4;
 +     }
   
       bb0: {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
index f244f37..81a0a5b 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
@@ -8,8 +8,6 @@
       let mut _3: u16;
       let mut _4: u32;
 +     scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
-+         debug self => _3;
-+         debug rhs => _4;
 +     }
   
       bb0: {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
index c96983c..9e3802b 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
@@ -5,8 +5,6 @@
     debug b => _2;
     let mut _0: u16;
     scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
-        debug self => _1;
-        debug rhs => _2;
     }
 
     bb0: {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
index c96983c..9e3802b 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
@@ -5,8 +5,6 @@
     debug b => _2;
     let mut _0: u16;
     scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
-        debug self => _1;
-        debug rhs => _2;
     }
 
     bb0: {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff
index 74518db..a2b3ad4 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff
@@ -8,8 +8,6 @@
       let mut _3: i64;
       let mut _4: u32;
 +     scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
-+         debug self => _3;
-+         debug rhs => _4;
 +     }
   
       bb0: {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff
index aab0462..2ff6b53 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff
@@ -8,8 +8,6 @@
       let mut _3: i64;
       let mut _4: u32;
 +     scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
-+         debug self => _3;
-+         debug rhs => _4;
 +     }
   
       bb0: {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir
index 1dd8cb2..f396198 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir
@@ -5,8 +5,6 @@
     debug b => _2;
     let mut _0: i64;
     scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
-        debug self => _1;
-        debug rhs => _2;
     }
 
     bb0: {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir
index 1dd8cb2..f396198 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir
@@ -5,8 +5,6 @@
     debug b => _2;
     let mut _0: i64;
     scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
-        debug self => _1;
-        debug rhs => _2;
     }
 
     bb0: {
diff --git a/tests/mir-opt/inline/unit_test.rs b/tests/mir-opt/inline/unit_test.rs
index f6c3d6a..bebe693 100644
--- a/tests/mir-opt/inline/unit_test.rs
+++ b/tests/mir-opt/inline/unit_test.rs
@@ -1,5 +1,5 @@
 // Check that `-Zmir-enable-passes=+Inline` does not ICE because of stolen MIR.
-//@ unit-test: Inline
+//@ test-mir-pass: Inline
 // skip-filecheck
 #![crate_type = "lib"]
 
diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff
index 814eda1..c1f7879 100644
--- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff
@@ -6,10 +6,8 @@
       let mut _0: T;
       let mut _2: std::option::Option<T>;
 +     scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
-+         debug self => _2;
 +         let mut _3: isize;
 +         scope 2 {
-+             debug val => _0;
 +         }
 +         scope 3 (inlined unreachable_unchecked) {
 +             let mut _4: bool;
diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff
index d5d6907..5271e53 100644
--- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff
@@ -6,10 +6,8 @@
       let mut _0: T;
       let mut _2: std::option::Option<T>;
 +     scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
-+         debug self => _2;
 +         let mut _3: isize;
 +         scope 2 {
-+             debug val => _0;
 +         }
 +         scope 3 (inlined unreachable_unchecked) {
 +             let mut _4: bool;
diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir
index 7c24a97..dcab8a6 100644
--- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir
@@ -4,10 +4,8 @@
     debug slf => _1;
     let mut _0: T;
     scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
-        debug self => _1;
         let mut _2: isize;
         scope 2 {
-            debug val => _0;
         }
         scope 3 (inlined unreachable_unchecked) {
             scope 4 (inlined core::ub_checks::check_language_ub) {
diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir
index 7c24a97..dcab8a6 100644
--- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir
@@ -4,10 +4,8 @@
     debug slf => _1;
     let mut _0: T;
     scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
-        debug self => _1;
         let mut _2: isize;
         scope 2 {
-            debug val => _0;
         }
         scope 3 (inlined unreachable_unchecked) {
             scope 4 (inlined core::ub_checks::check_language_ub) {
diff --git a/tests/mir-opt/inline_coroutine_body.rs b/tests/mir-opt/inline_coroutine_body.rs
index be73bc4..4326ff8 100644
--- a/tests/mir-opt/inline_coroutine_body.rs
+++ b/tests/mir-opt/inline_coroutine_body.rs
@@ -1,6 +1,6 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // skip-filecheck
-//@ unit-test: Inline
+//@ test-mir-pass: Inline
 //@ edition: 2021
 //@ compile-flags: -Zinline-mir-hint-threshold=10000 -Zinline-mir-threshold=10000 --crate-type=lib
 
diff --git "a/tests/mir-opt/inline_coroutine_body.run2-\173closure\0430\175.Inline.panic-abort.diff" "b/tests/mir-opt/inline_coroutine_body.run2-\173closure\0430\175.Inline.panic-abort.diff"
index f8cceac..f871578 100644
--- "a/tests/mir-opt/inline_coroutine_body.run2-\173closure\0430\175.Inline.panic-abort.diff"
+++ "b/tests/mir-opt/inline_coroutine_body.run2-\173closure\0430\175.Inline.panic-abort.diff"
@@ -21,8 +21,6 @@
               scope 3 {
               }
 +             scope 6 (inlined ActionPermit::<'_, T>::perform::{closure#0}) {
-+                 debug _task_context => _31;
-+                 debug self => ((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})).0: ActionPermit<'_, T>);
 +                 let _11: ActionPermit<'_, T>;
 +                 let mut _12: std::future::Ready<()>;
 +                 let mut _13: std::future::Ready<()>;
@@ -52,28 +50,22 @@
 +                 let mut _39: &mut {async fn body of ActionPermit<'_, T>::perform()};
 +                 let mut _40: &mut {async fn body of ActionPermit<'_, T>::perform()};
 +                 scope 7 {
-+                     debug self => (((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})) as variant#3).0: ActionPermit<'_, T>);
 +                     let mut _15: std::future::Ready<()>;
 +                     scope 8 {
-+                         debug __awaitee => (((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})) as variant#3).1: std::future::Ready<()>);
 +                         let _26: ();
 +                         scope 9 {
-+                             debug result => _26;
 +                         }
 +                     }
 +                     scope 10 (inlined ready::<()>) {
-+                         debug t => _14;
 +                         let mut _41: std::option::Option<()>;
 +                     }
 +                 }
 +             }
           }
 +         scope 5 (inlined Pin::<&mut {async fn body of ActionPermit<'_, T>::perform()}>::new_unchecked) {
-+             debug pointer => _5;
 +         }
       }
 +     scope 4 (inlined ActionPermit::<'_, T>::perform) {
-+         debug self => _3;
 +     }
   
       bb0: {
diff --git "a/tests/mir-opt/inline_coroutine_body.run2-\173closure\0430\175.Inline.panic-unwind.diff" "b/tests/mir-opt/inline_coroutine_body.run2-\173closure\0430\175.Inline.panic-unwind.diff"
index fd080d2..494f559 100644
--- "a/tests/mir-opt/inline_coroutine_body.run2-\173closure\0430\175.Inline.panic-unwind.diff"
+++ "b/tests/mir-opt/inline_coroutine_body.run2-\173closure\0430\175.Inline.panic-unwind.diff"
@@ -21,8 +21,6 @@
               scope 3 {
               }
 +             scope 6 (inlined ActionPermit::<'_, T>::perform::{closure#0}) {
-+                 debug _task_context => _31;
-+                 debug self => ((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})).0: ActionPermit<'_, T>);
 +                 let _11: ActionPermit<'_, T>;
 +                 let mut _12: std::future::Ready<()>;
 +                 let mut _13: std::future::Ready<()>;
@@ -54,28 +52,22 @@
 +                 let mut _41: &mut {async fn body of ActionPermit<'_, T>::perform()};
 +                 let mut _42: &mut {async fn body of ActionPermit<'_, T>::perform()};
 +                 scope 7 {
-+                     debug self => (((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})) as variant#3).0: ActionPermit<'_, T>);
 +                     let mut _15: std::future::Ready<()>;
 +                     scope 8 {
-+                         debug __awaitee => (((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})) as variant#3).1: std::future::Ready<()>);
 +                         let _26: ();
 +                         scope 9 {
-+                             debug result => _26;
 +                         }
 +                     }
 +                     scope 10 (inlined ready::<()>) {
-+                         debug t => _14;
 +                         let mut _43: std::option::Option<()>;
 +                     }
 +                 }
 +             }
           }
 +         scope 5 (inlined Pin::<&mut {async fn body of ActionPermit<'_, T>::perform()}>::new_unchecked) {
-+             debug pointer => _5;
 +         }
       }
 +     scope 4 (inlined ActionPermit::<'_, T>::perform) {
-+         debug self => _3;
 +     }
   
       bb0: {
diff --git a/tests/mir-opt/inline_generically_if_sized.call.Inline.diff b/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
index 0cf4565..fc5833f 100644
--- a/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
+++ b/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
@@ -6,7 +6,6 @@
       let mut _0: i32;
       let mut _2: &T;
 +     scope 1 (inlined <T as Foo>::bar) {
-+         debug self => _2;
 +     }
   
       bb0: {
diff --git a/tests/mir-opt/inline_generically_if_sized.rs b/tests/mir-opt/inline_generically_if_sized.rs
index 794ce3d..e4fc94e 100644
--- a/tests/mir-opt/inline_generically_if_sized.rs
+++ b/tests/mir-opt/inline_generically_if_sized.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: Inline
+//@ test-mir-pass: Inline
 //@ compile-flags: --crate-type=lib -C panic=abort
 
 trait Foo {
diff --git a/tests/mir-opt/instsimplify/bool_compare.rs b/tests/mir-opt/instsimplify/bool_compare.rs
index 47984ed..d1d903f 100644
--- a/tests/mir-opt/instsimplify/bool_compare.rs
+++ b/tests/mir-opt/instsimplify/bool_compare.rs
@@ -1,4 +1,4 @@
-//@ unit-test: InstSimplify
+//@ test-mir-pass: InstSimplify
 
 // EMIT_MIR bool_compare.eq_true.InstSimplify.diff
 fn eq_true(x: bool) -> u32 {
diff --git a/tests/mir-opt/instsimplify/casts.redundant.InstSimplify.diff b/tests/mir-opt/instsimplify/casts.redundant.InstSimplify.diff
index 9e1bce1..e7451d5 100644
--- a/tests/mir-opt/instsimplify/casts.redundant.InstSimplify.diff
+++ b/tests/mir-opt/instsimplify/casts.redundant.InstSimplify.diff
@@ -8,7 +8,6 @@
       let mut _3: *const &u8;
       let mut _4: *const &u8;
       scope 1 (inlined generic_cast::<&u8, &u8>) {
-          debug x => _4;
           let mut _5: *const &u8;
       }
   
diff --git a/tests/mir-opt/instsimplify/casts.rs b/tests/mir-opt/instsimplify/casts.rs
index adcf325..a7786fa 100644
--- a/tests/mir-opt/instsimplify/casts.rs
+++ b/tests/mir-opt/instsimplify/casts.rs
@@ -1,6 +1,7 @@
-//@ unit-test: InstSimplify
+//@ test-mir-pass: InstSimplify
 //@ compile-flags: -Zinline-mir
 #![crate_type = "lib"]
+#![feature(core_intrinsics)]
 
 #[inline(always)]
 fn generic_cast<T, U>(x: *const T) -> *const U {
@@ -23,3 +24,11 @@
     // CHECK: _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer));
     x as *mut u8 as *const u8
 }
+
+// EMIT_MIR casts.roundtrip.InstSimplify.diff
+pub fn cast_thin_via_aggregate(x: *const u8) -> *const () {
+    // CHECK-LABEL: fn cast_thin_via_aggregate(
+    // CHECK: _2 = _1;
+    // CHECK: _0 = move _2 as *const () (PtrToPtr);
+    std::intrinsics::aggregate_raw_ptr(x, ())
+}
diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs
index 4b4054a..86455e8 100644
--- a/tests/mir-opt/instsimplify/combine_array_len.rs
+++ b/tests/mir-opt/instsimplify/combine_array_len.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: InstSimplify
+//@ test-mir-pass: InstSimplify
 
 // EMIT_MIR combine_array_len.norm2.InstSimplify.diff
 fn norm2(x: [f32; 2]) -> f32 {
diff --git a/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs b/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs
index d0c8559..7b1f3d1 100644
--- a/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs
+++ b/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs
@@ -1,4 +1,4 @@
-//@ unit-test: InstSimplify
+//@ test-mir-pass: InstSimplify
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 // EMIT_MIR combine_clone_of_primitives.{impl#0}-clone.InstSimplify.diff
diff --git a/tests/mir-opt/instsimplify/combine_transmutes.rs b/tests/mir-opt/instsimplify/combine_transmutes.rs
index 3707ee1..0be7466 100644
--- a/tests/mir-opt/instsimplify/combine_transmutes.rs
+++ b/tests/mir-opt/instsimplify/combine_transmutes.rs
@@ -1,9 +1,8 @@
-//@ unit-test: InstSimplify
+//@ test-mir-pass: InstSimplify
 //@ compile-flags: -C panic=abort
 #![crate_type = "lib"]
 #![feature(core_intrinsics)]
 #![feature(custom_mir)]
-#![feature(generic_nonzero)]
 
 use std::intrinsics::mir::*;
 use std::mem::{MaybeUninit, ManuallyDrop, transmute};
diff --git a/tests/mir-opt/instsimplify/duplicate_switch_targets.rs b/tests/mir-opt/instsimplify/duplicate_switch_targets.rs
index fd09d63..4547282 100644
--- a/tests/mir-opt/instsimplify/duplicate_switch_targets.rs
+++ b/tests/mir-opt/instsimplify/duplicate_switch_targets.rs
@@ -1,4 +1,4 @@
-//@ unit-test: InstSimplify
+//@ test-mir-pass: InstSimplify
 
 #![feature(custom_mir, core_intrinsics)]
 #![crate_type = "lib"]
diff --git a/tests/mir-opt/instsimplify/intrinsic_asserts.rs b/tests/mir-opt/instsimplify/intrinsic_asserts.rs
index c14b1ac..c031c97 100644
--- a/tests/mir-opt/instsimplify/intrinsic_asserts.rs
+++ b/tests/mir-opt/instsimplify/intrinsic_asserts.rs
@@ -1,4 +1,4 @@
-//@ unit-test: InstSimplify
+//@ test-mir-pass: InstSimplify
 
 #![crate_type = "lib"]
 #![feature(core_intrinsics)]
diff --git a/tests/mir-opt/instsimplify/ub_check.rs b/tests/mir-opt/instsimplify/ub_check.rs
index fc568ab..5f13f5b 100644
--- a/tests/mir-opt/instsimplify/ub_check.rs
+++ b/tests/mir-opt/instsimplify/ub_check.rs
@@ -1,4 +1,4 @@
-//@ unit-test: InstSimplify
+//@ test-mir-pass: InstSimplify
 //@ compile-flags: -Cdebug-assertions=no -Zinline-mir
 
 // EMIT_MIR ub_check.unwrap_unchecked.InstSimplify.diff
diff --git a/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify.diff b/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify.diff
index 6d6c9c9..4d8d658 100644
--- a/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify.diff
+++ b/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify.diff
@@ -6,10 +6,8 @@
       let mut _0: i32;
       let mut _2: std::option::Option<i32>;
       scope 1 (inlined #[track_caller] Option::<i32>::unwrap_unchecked) {
-          debug self => _2;
           let mut _3: isize;
           scope 2 {
-              debug val => _0;
           }
           scope 3 (inlined unreachable_unchecked) {
               let mut _4: bool;
diff --git a/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff b/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff
index 1872907..311de9e 100644
--- a/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff
+++ b/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff
@@ -17,15 +17,11 @@
       let mut _12: u32;
       let mut _13: bool;
       scope 1 (inlined imm8) {
-          debug x => _5;
           let mut _14: u32;
           scope 2 {
-              debug out => _4;
           }
       }
       scope 3 (inlined core::num::<impl u32>::rotate_right) {
-          debug self => _4;
-          debug n => _6;
       }
   
       bb0: {
@@ -34,7 +30,7 @@
           StorageLive(_4);
           StorageLive(_5);
           _5 = _1;
-          _4 = const 0_u32;
+          nop;
 -         StorageLive(_14);
 -         _14 = BitAnd(_5, const 255_u32);
 -         _4 = BitOr(const 0_u32, move _14);
@@ -74,7 +70,8 @@
       bb2: {
           _6 = Shl(move _7, const 1_i32);
           StorageDead(_7);
-          _3 = rotate_right::<u32>(move _4, move _6) -> [return: bb3, unwind unreachable];
+-         _3 = rotate_right::<u32>(move _4, move _6) -> [return: bb3, unwind unreachable];
++         _3 = rotate_right::<u32>(_14, move _6) -> [return: bb3, unwind unreachable];
       }
   
       bb3: {
diff --git a/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff b/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff
index 99350ba..c5fd042 100644
--- a/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff
+++ b/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff
@@ -17,15 +17,11 @@
       let mut _12: u32;
       let mut _13: bool;
       scope 1 (inlined imm8) {
-          debug x => _5;
           let mut _14: u32;
           scope 2 {
-              debug out => _4;
           }
       }
       scope 3 (inlined core::num::<impl u32>::rotate_right) {
-          debug self => _4;
-          debug n => _6;
       }
   
       bb0: {
@@ -34,7 +30,7 @@
           StorageLive(_4);
           StorageLive(_5);
           _5 = _1;
-          _4 = const 0_u32;
+          nop;
 -         StorageLive(_14);
 -         _14 = BitAnd(_5, const 255_u32);
 -         _4 = BitOr(const 0_u32, move _14);
@@ -74,7 +70,8 @@
       bb2: {
           _6 = Shl(move _7, const 1_i32);
           StorageDead(_7);
-          _3 = rotate_right::<u32>(move _4, move _6) -> [return: bb3, unwind unreachable];
+-         _3 = rotate_right::<u32>(move _4, move _6) -> [return: bb3, unwind unreachable];
++         _3 = rotate_right::<u32>(_14, move _6) -> [return: bb3, unwind unreachable];
       }
   
       bb3: {
diff --git a/tests/mir-opt/issue_101973.rs b/tests/mir-opt/issue_101973.rs
index c40eaa1..84a36f1 100644
--- a/tests/mir-opt/issue_101973.rs
+++ b/tests/mir-opt/issue_101973.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //@ compile-flags: -O -C debug-assertions=on
-// This needs inlining followed by GVN to reproduce, so we cannot use "unit-test".
+// This needs inlining followed by GVN to reproduce, so we cannot use "test-mir-pass".
 
 #[inline]
 pub fn imm8(x: u32) -> u32 {
diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir
index bc12002..8da56d5 100644
--- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir
@@ -3,66 +3,56 @@
 fn num_to_digit(_1: char) -> u32 {
     debug num => _1;
     let mut _0: u32;
-    let mut _5: std::option::Option<u32>;
+    let mut _4: std::option::Option<u32>;
     scope 1 (inlined char::methods::<impl char>::is_digit) {
-        debug self => _1;
-        debug radix => const 8_u32;
         let _2: std::option::Option<u32>;
-        let mut _3: &std::option::Option<u32>;
         scope 2 (inlined Option::<u32>::is_some) {
-            debug self => _3;
-            let mut _4: isize;
+            let mut _3: isize;
         }
     }
     scope 3 (inlined #[track_caller] Option::<u32>::unwrap) {
-        debug self => _5;
-        let mut _6: isize;
-        let mut _7: !;
+        let mut _5: isize;
+        let mut _6: !;
         scope 4 {
-            debug val => _0;
         }
     }
 
     bb0: {
-        StorageLive(_3);
         StorageLive(_2);
         _2 = char::methods::<impl char>::to_digit(_1, const 8_u32) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
-        _3 = &_2;
-        StorageLive(_4);
-        _4 = discriminant(_2);
-        switchInt(move _4) -> [1: bb2, 0: bb6, otherwise: bb8];
+        StorageLive(_3);
+        _3 = discriminant(_2);
+        switchInt(move _3) -> [1: bb2, 0: bb6, otherwise: bb8];
     }
 
     bb2: {
-        StorageDead(_4);
         StorageDead(_3);
         StorageDead(_2);
-        StorageLive(_5);
-        _5 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind unreachable];
+        StorageLive(_4);
+        _4 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageLive(_6);
-        _6 = discriminant(_5);
-        switchInt(move _6) -> [0: bb4, 1: bb5, otherwise: bb8];
+        StorageLive(_5);
+        _5 = discriminant(_4);
+        switchInt(move _5) -> [0: bb4, 1: bb5, otherwise: bb8];
     }
 
     bb4: {
-        _7 = option::unwrap_failed() -> unwind unreachable;
+        _6 = option::unwrap_failed() -> unwind unreachable;
     }
 
     bb5: {
-        _0 = move ((_5 as Some).0: u32);
-        StorageDead(_6);
+        _0 = move ((_4 as Some).0: u32);
         StorageDead(_5);
+        StorageDead(_4);
         goto -> bb7;
     }
 
     bb6: {
-        StorageDead(_4);
         StorageDead(_3);
         StorageDead(_2);
         _0 = const 0_u32;
diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir
index 6c7e10a..61bc09d 100644
--- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir
@@ -3,66 +3,56 @@
 fn num_to_digit(_1: char) -> u32 {
     debug num => _1;
     let mut _0: u32;
-    let mut _5: std::option::Option<u32>;
+    let mut _4: std::option::Option<u32>;
     scope 1 (inlined char::methods::<impl char>::is_digit) {
-        debug self => _1;
-        debug radix => const 8_u32;
         let _2: std::option::Option<u32>;
-        let mut _3: &std::option::Option<u32>;
         scope 2 (inlined Option::<u32>::is_some) {
-            debug self => _3;
-            let mut _4: isize;
+            let mut _3: isize;
         }
     }
     scope 3 (inlined #[track_caller] Option::<u32>::unwrap) {
-        debug self => _5;
-        let mut _6: isize;
-        let mut _7: !;
+        let mut _5: isize;
+        let mut _6: !;
         scope 4 {
-            debug val => _0;
         }
     }
 
     bb0: {
-        StorageLive(_3);
         StorageLive(_2);
         _2 = char::methods::<impl char>::to_digit(_1, const 8_u32) -> [return: bb1, unwind continue];
     }
 
     bb1: {
-        _3 = &_2;
-        StorageLive(_4);
-        _4 = discriminant(_2);
-        switchInt(move _4) -> [1: bb2, 0: bb6, otherwise: bb8];
+        StorageLive(_3);
+        _3 = discriminant(_2);
+        switchInt(move _3) -> [1: bb2, 0: bb6, otherwise: bb8];
     }
 
     bb2: {
-        StorageDead(_4);
         StorageDead(_3);
         StorageDead(_2);
-        StorageLive(_5);
-        _5 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind continue];
+        StorageLive(_4);
+        _4 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind continue];
     }
 
     bb3: {
-        StorageLive(_6);
-        _6 = discriminant(_5);
-        switchInt(move _6) -> [0: bb4, 1: bb5, otherwise: bb8];
+        StorageLive(_5);
+        _5 = discriminant(_4);
+        switchInt(move _5) -> [0: bb4, 1: bb5, otherwise: bb8];
     }
 
     bb4: {
-        _7 = option::unwrap_failed() -> unwind continue;
+        _6 = option::unwrap_failed() -> unwind continue;
     }
 
     bb5: {
-        _0 = move ((_5 as Some).0: u32);
-        StorageDead(_6);
+        _0 = move ((_4 as Some).0: u32);
         StorageDead(_5);
+        StorageDead(_4);
         goto -> bb7;
     }
 
     bb6: {
-        StorageDead(_4);
         StorageDead(_3);
         StorageDead(_2);
         _0 = const 0_u32;
diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff
index d67477a..65379ae 100644
--- a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff
+++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff
@@ -16,13 +16,10 @@
           debug residual => _6;
           scope 2 {
               scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) {
-                  debug residual => _8;
                   let _14: i32;
                   let mut _15: i32;
                   scope 9 {
-                      debug e => _14;
                       scope 10 (inlined <i32 as From<i32>>::from) {
-                          debug t => _14;
                       }
                   }
               }
@@ -34,16 +31,13 @@
           }
       }
       scope 5 (inlined <Result<i32, i32> as Try>::branch) {
-          debug self => _4;
           let mut _10: isize;
           let _11: i32;
           let _12: i32;
           let mut _13: std::result::Result<std::convert::Infallible, i32>;
           scope 6 {
-              debug v => _11;
           }
           scope 7 {
-              debug e => _12;
           }
       }
   
diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff
index d67477a..65379ae 100644
--- a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff
+++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff
@@ -16,13 +16,10 @@
           debug residual => _6;
           scope 2 {
               scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) {
-                  debug residual => _8;
                   let _14: i32;
                   let mut _15: i32;
                   scope 9 {
-                      debug e => _14;
                       scope 10 (inlined <i32 as From<i32>>::from) {
-                          debug t => _14;
                       }
                   }
               }
@@ -34,16 +31,13 @@
           }
       }
       scope 5 (inlined <Result<i32, i32> as Try>::branch) {
-          debug self => _4;
           let mut _10: isize;
           let _11: i32;
           let _12: i32;
           let mut _13: std::result::Result<std::convert::Infallible, i32>;
           scope 6 {
-              debug v => _11;
           }
           scope 7 {
-              debug e => _12;
           }
       }
   
diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs
index eedb26a..57f4e4a 100644
--- a/tests/mir-opt/jump_threading.rs
+++ b/tests/mir-opt/jump_threading.rs
@@ -1,4 +1,4 @@
-//@ unit-test: JumpThreading
+//@ test-mir-pass: JumpThreading
 //@ compile-flags: -Zmir-enable-passes=+Inline
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
diff --git a/tests/mir-opt/lower_array_len.rs b/tests/mir-opt/lower_array_len.rs
index 7fcea75..1c30c4c 100644
--- a/tests/mir-opt/lower_array_len.rs
+++ b/tests/mir-opt/lower_array_len.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: NormalizeArrayLen
+//@ test-mir-pass: NormalizeArrayLen
 //@ compile-flags: -Zmir-enable-passes=+LowerSliceLenCalls
 
 // EMIT_MIR lower_array_len.array_bound.NormalizeArrayLen.diff
diff --git a/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff
new file mode 100644
index 0000000..02934d4
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff
@@ -0,0 +1,95 @@
+- // MIR for `make_pointers` before LowerIntrinsics
++ // MIR for `make_pointers` after LowerIntrinsics
+  
+  fn make_pointers(_1: *const u8, _2: *mut (), _3: usize) -> () {
+      debug a => _1;
+      debug b => _2;
+      debug n => _3;
+      let mut _0: ();
+      let _4: *const i32;
+      let mut _5: *const u8;
+      let mut _6: ();
+      let mut _8: *mut ();
+      let mut _9: ();
+      let mut _11: *const u8;
+      let mut _12: usize;
+      let mut _14: *mut ();
+      let mut _15: usize;
+      scope 1 {
+          debug _thin_const => _4;
+          let _7: *mut u8;
+          scope 2 {
+              debug _thin_mut => _7;
+              let _10: *const [u16];
+              scope 3 {
+                  debug _slice_const => _10;
+                  let _13: *mut [u64];
+                  scope 4 {
+                      debug _slice_mut => _13;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _1;
+          StorageLive(_6);
+          _6 = ();
+-         _4 = aggregate_raw_ptr::<*const i32, *const u8, ()>(move _5, move _6) -> [return: bb1, unwind unreachable];
++         _4 = *const i32 from (move _5, move _6);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_7);
+          StorageLive(_8);
+          _8 = _2;
+          StorageLive(_9);
+          _9 = ();
+-         _7 = aggregate_raw_ptr::<*mut u8, *mut (), ()>(move _8, move _9) -> [return: bb2, unwind unreachable];
++         _7 = *mut u8 from (move _8, move _9);
++         goto -> bb2;
+      }
+  
+      bb2: {
+          StorageDead(_9);
+          StorageDead(_8);
+          StorageLive(_10);
+          StorageLive(_11);
+          _11 = _1;
+          StorageLive(_12);
+          _12 = _3;
+-         _10 = aggregate_raw_ptr::<*const [u16], *const u8, usize>(move _11, move _12) -> [return: bb3, unwind unreachable];
++         _10 = *const [u16] from (move _11, move _12);
++         goto -> bb3;
+      }
+  
+      bb3: {
+          StorageDead(_12);
+          StorageDead(_11);
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = _2;
+          StorageLive(_15);
+          _15 = _3;
+-         _13 = aggregate_raw_ptr::<*mut [u64], *mut (), usize>(move _14, move _15) -> [return: bb4, unwind unreachable];
++         _13 = *mut [u64] from (move _14, move _15);
++         goto -> bb4;
+      }
+  
+      bb4: {
+          StorageDead(_15);
+          StorageDead(_14);
+          _0 = const ();
+          StorageDead(_13);
+          StorageDead(_10);
+          StorageDead(_7);
+          StorageDead(_4);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff
new file mode 100644
index 0000000..02934d4
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff
@@ -0,0 +1,95 @@
+- // MIR for `make_pointers` before LowerIntrinsics
++ // MIR for `make_pointers` after LowerIntrinsics
+  
+  fn make_pointers(_1: *const u8, _2: *mut (), _3: usize) -> () {
+      debug a => _1;
+      debug b => _2;
+      debug n => _3;
+      let mut _0: ();
+      let _4: *const i32;
+      let mut _5: *const u8;
+      let mut _6: ();
+      let mut _8: *mut ();
+      let mut _9: ();
+      let mut _11: *const u8;
+      let mut _12: usize;
+      let mut _14: *mut ();
+      let mut _15: usize;
+      scope 1 {
+          debug _thin_const => _4;
+          let _7: *mut u8;
+          scope 2 {
+              debug _thin_mut => _7;
+              let _10: *const [u16];
+              scope 3 {
+                  debug _slice_const => _10;
+                  let _13: *mut [u64];
+                  scope 4 {
+                      debug _slice_mut => _13;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _1;
+          StorageLive(_6);
+          _6 = ();
+-         _4 = aggregate_raw_ptr::<*const i32, *const u8, ()>(move _5, move _6) -> [return: bb1, unwind unreachable];
++         _4 = *const i32 from (move _5, move _6);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_7);
+          StorageLive(_8);
+          _8 = _2;
+          StorageLive(_9);
+          _9 = ();
+-         _7 = aggregate_raw_ptr::<*mut u8, *mut (), ()>(move _8, move _9) -> [return: bb2, unwind unreachable];
++         _7 = *mut u8 from (move _8, move _9);
++         goto -> bb2;
+      }
+  
+      bb2: {
+          StorageDead(_9);
+          StorageDead(_8);
+          StorageLive(_10);
+          StorageLive(_11);
+          _11 = _1;
+          StorageLive(_12);
+          _12 = _3;
+-         _10 = aggregate_raw_ptr::<*const [u16], *const u8, usize>(move _11, move _12) -> [return: bb3, unwind unreachable];
++         _10 = *const [u16] from (move _11, move _12);
++         goto -> bb3;
+      }
+  
+      bb3: {
+          StorageDead(_12);
+          StorageDead(_11);
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = _2;
+          StorageLive(_15);
+          _15 = _3;
+-         _13 = aggregate_raw_ptr::<*mut [u64], *mut (), usize>(move _14, move _15) -> [return: bb4, unwind unreachable];
++         _13 = *mut [u64] from (move _14, move _15);
++         goto -> bb4;
+      }
+  
+      bb4: {
+          StorageDead(_15);
+          StorageDead(_14);
+          _0 = const ();
+          StorageDead(_13);
+          StorageDead(_10);
+          StorageDead(_7);
+          StorageDead(_4);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs
index 0836641..12e526a 100644
--- a/tests/mir-opt/lower_intrinsics.rs
+++ b/tests/mir-opt/lower_intrinsics.rs
@@ -1,4 +1,4 @@
-//@ unit-test: LowerIntrinsics
+//@ test-mir-pass: LowerIntrinsics
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 #![feature(core_intrinsics, intrinsics, rustc_attrs)]
@@ -248,3 +248,13 @@
 pub fn three_way_compare_unsigned(a: u32, b: u32) {
     let _x = core::intrinsics::three_way_compare(a, b);
 }
+
+// EMIT_MIR lower_intrinsics.make_pointers.LowerIntrinsics.diff
+pub fn make_pointers(a: *const u8, b: *mut (), n: usize) {
+    use std::intrinsics::aggregate_raw_ptr;
+
+    let _thin_const: *const i32 = aggregate_raw_ptr(a, ());
+    let _thin_mut: *mut u8 = aggregate_raw_ptr(b, ());
+    let _slice_const: *const [u16] = aggregate_raw_ptr(a, n);
+    let _slice_mut: *mut [u64] = aggregate_raw_ptr(b, n);
+}
diff --git a/tests/mir-opt/lower_slice_len.rs b/tests/mir-opt/lower_slice_len.rs
index 38d5e98..b82094d 100644
--- a/tests/mir-opt/lower_slice_len.rs
+++ b/tests/mir-opt/lower_slice_len.rs
@@ -1,4 +1,4 @@
-//@ unit-test: LowerSliceLenCalls
+//@ test-mir-pass: LowerSliceLenCalls
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 // EMIT_MIR lower_slice_len.bound.LowerSliceLenCalls.diff
diff --git a/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff
index 31ce51d..1f20349 100644
--- a/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff
+++ b/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff
@@ -5,42 +5,37 @@
       debug i => _1;
       let mut _0: u128;
       let mut _2: i128;
-+     let mut _3: i128;
   
       bb0: {
           _2 = discriminant(_1);
--         switchInt(move _2) -> [1: bb3, 2: bb4, 3: bb5, 340282366920938463463374607431768211455: bb2, otherwise: bb1];
--     }
-- 
--     bb1: {
--         unreachable;
--     }
-- 
--     bb2: {
--         _0 = const core::num::<impl u128>::MAX;
--         goto -> bb6;
--     }
-- 
--     bb3: {
--         _0 = const 1_u128;
--         goto -> bb6;
--     }
-- 
--     bb4: {
--         _0 = const 2_u128;
--         goto -> bb6;
--     }
-- 
--     bb5: {
--         _0 = const 3_u128;
--         goto -> bb6;
--     }
-- 
--     bb6: {
-+         StorageLive(_3);
-+         _3 = move _2;
-+         _0 = _3 as u128 (IntToInt);
-+         StorageDead(_3);
+          switchInt(move _2) -> [1: bb3, 2: bb4, 3: bb5, 340282366920938463463374607431768211455: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _0 = const core::num::<impl u128>::MAX;
+          goto -> bb6;
+      }
+  
+      bb3: {
+          _0 = const 1_u128;
+          goto -> bb6;
+      }
+  
+      bb4: {
+          _0 = const 2_u128;
+          goto -> bb6;
+      }
+  
+      bb5: {
+          _0 = const 3_u128;
+          goto -> bb6;
+      }
+  
+      bb6: {
           return;
       }
   }
diff --git a/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff
index e1b537b..4b43531 100644
--- a/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff
+++ b/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff
@@ -5,37 +5,32 @@
       debug i => _1;
       let mut _0: i8;
       let mut _2: i16;
-+     let mut _3: i16;
   
       bb0: {
           _2 = discriminant(_1);
--         switchInt(move _2) -> [65535: bb3, 2: bb4, 65533: bb2, otherwise: bb1];
--     }
-- 
--     bb1: {
--         unreachable;
--     }
-- 
--     bb2: {
--         _0 = const -3_i8;
--         goto -> bb5;
--     }
-- 
--     bb3: {
--         _0 = const -1_i8;
--         goto -> bb5;
--     }
-- 
--     bb4: {
--         _0 = const 2_i8;
--         goto -> bb5;
--     }
-- 
--     bb5: {
-+         StorageLive(_3);
-+         _3 = move _2;
-+         _0 = _3 as i8 (IntToInt);
-+         StorageDead(_3);
+          switchInt(move _2) -> [65535: bb3, 2: bb4, 65533: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _0 = const -3_i8;
+          goto -> bb5;
+      }
+  
+      bb3: {
+          _0 = const -1_i8;
+          goto -> bb5;
+      }
+  
+      bb4: {
+          _0 = const 2_i8;
+          goto -> bb5;
+      }
+  
+      bb5: {
           return;
       }
   }
diff --git a/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff
index cabc5a4..8a39073 100644
--- a/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff
+++ b/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff
@@ -5,37 +5,32 @@
       debug i => _1;
       let mut _0: i16;
       let mut _2: i8;
-+     let mut _3: i8;
   
       bb0: {
           _2 = discriminant(_1);
--         switchInt(move _2) -> [255: bb3, 2: bb4, 253: bb2, otherwise: bb1];
--     }
-- 
--     bb1: {
--         unreachable;
--     }
-- 
--     bb2: {
--         _0 = const -3_i16;
--         goto -> bb5;
--     }
-- 
--     bb3: {
--         _0 = const -1_i16;
--         goto -> bb5;
--     }
-- 
--     bb4: {
--         _0 = const 2_i16;
--         goto -> bb5;
--     }
-- 
--     bb5: {
-+         StorageLive(_3);
-+         _3 = move _2;
-+         _0 = _3 as i16 (IntToInt);
-+         StorageDead(_3);
+          switchInt(move _2) -> [255: bb3, 2: bb4, 253: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _0 = const -3_i16;
+          goto -> bb5;
+      }
+  
+      bb3: {
+          _0 = const -1_i16;
+          goto -> bb5;
+      }
+  
+      bb4: {
+          _0 = const 2_i16;
+          goto -> bb5;
+      }
+  
+      bb5: {
           return;
       }
   }
diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff
index 9ee01a8..72ad609 100644
--- a/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff
+++ b/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff
@@ -5,32 +5,27 @@
       debug i => _1;
       let mut _0: i16;
       let mut _2: u8;
-+     let mut _3: u8;
   
       bb0: {
           _2 = discriminant(_1);
--         switchInt(move _2) -> [1: bb3, 2: bb2, otherwise: bb1];
--     }
-- 
--     bb1: {
--         unreachable;
--     }
-- 
--     bb2: {
--         _0 = const 2_i16;
--         goto -> bb4;
--     }
-- 
--     bb3: {
--         _0 = const 1_i16;
--         goto -> bb4;
--     }
-- 
--     bb4: {
-+         StorageLive(_3);
-+         _3 = move _2;
-+         _0 = _3 as i16 (IntToInt);
-+         StorageDead(_3);
+          switchInt(move _2) -> [1: bb3, 2: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _0 = const 2_i16;
+          goto -> bb4;
+      }
+  
+      bb3: {
+          _0 = const 1_i16;
+          goto -> bb4;
+      }
+  
+      bb4: {
           return;
       }
   }
diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff
index aa9fcc6..043fdb1 100644
--- a/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff
+++ b/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff
@@ -5,37 +5,32 @@
       debug i => _1;
       let mut _0: u16;
       let mut _2: u8;
-+     let mut _3: u8;
   
       bb0: {
           _2 = discriminant(_1);
--         switchInt(move _2) -> [1: bb3, 2: bb4, 5: bb2, otherwise: bb1];
--     }
-- 
--     bb1: {
--         unreachable;
--     }
-- 
--     bb2: {
--         _0 = const 5_u16;
--         goto -> bb5;
--     }
-- 
--     bb3: {
--         _0 = const 1_u16;
--         goto -> bb5;
--     }
-- 
--     bb4: {
--         _0 = const 2_u16;
--         goto -> bb5;
--     }
-- 
--     bb5: {
-+         StorageLive(_3);
-+         _3 = move _2;
-+         _0 = _3 as u16 (IntToInt);
-+         StorageDead(_3);
+          switchInt(move _2) -> [1: bb3, 2: bb4, 5: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _0 = const 5_u16;
+          goto -> bb5;
+      }
+  
+      bb3: {
+          _0 = const 1_u16;
+          goto -> bb5;
+      }
+  
+      bb4: {
+          _0 = const 2_u16;
+          goto -> bb5;
+      }
+  
+      bb5: {
           return;
       }
   }
diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs
index ca3e5f7..fa46622 100644
--- a/tests/mir-opt/matches_reduce_branches.rs
+++ b/tests/mir-opt/matches_reduce_branches.rs
@@ -1,4 +1,4 @@
-//@ unit-test: MatchBranchSimplification
+//@ test-mir-pass: MatchBranchSimplification
 
 #![feature(repr128)]
 #![feature(core_intrinsics)]
@@ -75,9 +75,7 @@
 // EMIT_MIR matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff
 fn match_u8_i16(i: EnumAu8) -> i16 {
     // CHECK-LABEL: fn match_u8_i16(
-    // CHECK-NOT: switchInt
-    // CHECK: _0 = _3 as i16 (IntToInt);
-    // CHECH: return
+    // CHECK: switchInt
     match i {
         EnumAu8::A => 1,
         EnumAu8::B => 2,
@@ -146,9 +144,7 @@
 // EMIT_MIR matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff
 fn match_u8_u16(i: EnumBu8) -> u16 {
     // CHECK-LABEL: fn match_u8_u16(
-    // CHECK-NOT: switchInt
-    // CHECK: _0 = _3 as u16 (IntToInt);
-    // CHECH: return
+    // CHECK: switchInt
     match i {
         EnumBu8::A => 1,
         EnumBu8::B => 2,
@@ -204,9 +200,7 @@
 // EMIT_MIR matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff
 fn match_i8_i16(i: EnumAi8) -> i16 {
     // CHECK-LABEL: fn match_i8_i16(
-    // CHECK-NOT: switchInt
-    // CHECK: _0 = _3 as i16 (IntToInt);
-    // CHECH: return
+    // CHECK: switchInt
     match i {
         EnumAi8::A => -1,
         EnumAi8::B => 2,
@@ -235,9 +229,7 @@
 // EMIT_MIR matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff
 fn match_i16_i8(i: EnumAi16) -> i8 {
     // CHECK-LABEL: fn match_i16_i8(
-    // CHECK-NOT: switchInt
-    // CHECK: _0 = _3 as i8 (IntToInt);
-    // CHECH: return
+    // CHECK: switchInt
     match i {
         EnumAi16::A => -1,
         EnumAi16::B => 2,
@@ -256,9 +248,7 @@
 // EMIT_MIR matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff
 fn match_i128_u128(i: EnumAi128) -> u128 {
     // CHECK-LABEL: fn match_i128_u128(
-    // CHECK-NOT: switchInt
-    // CHECK: _0 = _3 as u128 (IntToInt);
-    // CHECH: return
+    // CHECK: switchInt
     match i {
         EnumAi128::A => 1,
         EnumAi128::B => 2,
diff --git a/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff b/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff
index 11a18f5..157f9c9 100644
--- a/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff
+++ b/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff
@@ -5,32 +5,27 @@
       debug e => _1;
       let mut _0: u8;
       let mut _2: isize;
-+     let mut _3: isize;
   
       bb0: {
           _2 = discriminant(_1);
--         switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
--     }
-- 
--     bb1: {
--         unreachable;
--     }
-- 
--     bb2: {
--         _0 = const 1_u8;
--         goto -> bb4;
--     }
-- 
--     bb3: {
--         _0 = const 0_u8;
--         goto -> bb4;
--     }
-- 
--     bb4: {
-+         StorageLive(_3);
-+         _3 = move _2;
-+         _0 = _3 as u8 (IntToInt);
-+         StorageDead(_3);
+          switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _0 = const 1_u8;
+          goto -> bb4;
+      }
+  
+      bb3: {
+          _0 = const 0_u8;
+          goto -> bb4;
+      }
+  
+      bb4: {
           return;
       }
   }
diff --git a/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff b/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff
index 809badc..1908377 100644
--- a/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff
+++ b/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff
@@ -5,32 +5,27 @@
       debug e => _1;
       let mut _0: i8;
       let mut _2: isize;
-+     let mut _3: isize;
   
       bb0: {
           _2 = discriminant(_1);
--         switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
--     }
-- 
--     bb1: {
--         unreachable;
--     }
-- 
--     bb2: {
--         _0 = const 1_i8;
--         goto -> bb4;
--     }
-- 
--     bb3: {
--         _0 = const 0_i8;
--         goto -> bb4;
--     }
-- 
--     bb4: {
-+         StorageLive(_3);
-+         _3 = move _2;
-+         _0 = _3 as i8 (IntToInt);
-+         StorageDead(_3);
+          switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          unreachable;
+      }
+  
+      bb2: {
+          _0 = const 1_i8;
+          goto -> bb4;
+      }
+  
+      bb3: {
+          _0 = const 0_i8;
+          goto -> bb4;
+      }
+  
+      bb4: {
           return;
       }
   }
diff --git a/tests/mir-opt/matches_u8.rs b/tests/mir-opt/matches_u8.rs
index e855c91..f0be82d 100644
--- a/tests/mir-opt/matches_u8.rs
+++ b/tests/mir-opt/matches_u8.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: MatchBranchSimplification
+//@ test-mir-pass: MatchBranchSimplification
 
 
 // EMIT_MIR matches_u8.exhaustive_match.MatchBranchSimplification.diff
diff --git a/tests/mir-opt/nrvo_miscompile_111005.rs b/tests/mir-opt/nrvo_miscompile_111005.rs
index 3087c98..18814b0 100644
--- a/tests/mir-opt/nrvo_miscompile_111005.rs
+++ b/tests/mir-opt/nrvo_miscompile_111005.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // This is a miscompilation, #111005 to track
 
-//@ unit-test: RenameReturnPlace
+//@ test-mir-pass: RenameReturnPlace
 
 #![feature(custom_mir, core_intrinsics)]
 extern crate core;
diff --git a/tests/mir-opt/nrvo_simple.rs b/tests/mir-opt/nrvo_simple.rs
index adb787a..5d2894a 100644
--- a/tests/mir-opt/nrvo_simple.rs
+++ b/tests/mir-opt/nrvo_simple.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: RenameReturnPlace
+//@ test-mir-pass: RenameReturnPlace
 
 // EMIT_MIR nrvo_simple.nrvo.RenameReturnPlace.diff
 fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] {
diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir
deleted file mode 100644
index 8456736..0000000
--- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir
+++ /dev/null
@@ -1,68 +0,0 @@
-// MIR for `checked_shl` after PreCodegen
-
-fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
-    debug x => _1;
-    debug rhs => _2;
-    let mut _0: std::option::Option<u32>;
-    scope 1 (inlined core::num::<impl u32>::checked_shl) {
-        debug self => _1;
-        debug rhs => _2;
-        let mut _6: bool;
-        scope 2 {
-            debug a => _4;
-            debug b => _5;
-        }
-        scope 3 (inlined core::num::<impl u32>::overflowing_shl) {
-            debug self => _1;
-            debug rhs => _2;
-            let mut _4: u32;
-            let mut _5: bool;
-            scope 4 (inlined core::num::<impl u32>::wrapping_shl) {
-                debug self => _1;
-                debug rhs => _2;
-                let mut _3: u32;
-                scope 5 (inlined core::num::<impl u32>::unchecked_shl) {
-                    debug self => _1;
-                    debug rhs => _3;
-                }
-            }
-        }
-    }
-
-    bb0: {
-        StorageLive(_4);
-        StorageLive(_5);
-        StorageLive(_3);
-        _3 = BitAnd(_2, const 31_u32);
-        _4 = ShlUnchecked(_1, _3);
-        StorageDead(_3);
-        _5 = Ge(_2, const core::num::<impl u32>::BITS);
-        StorageLive(_6);
-        _6 = unlikely(move _5) -> [return: bb1, unwind unreachable];
-    }
-
-    bb1: {
-        switchInt(move _6) -> [0: bb2, otherwise: bb3];
-    }
-
-    bb2: {
-        _0 = Option::<u32>::Some(_4);
-        goto -> bb4;
-    }
-
-    bb3: {
-        _0 = const Option::<u32>::None;
-        goto -> bb4;
-    }
-
-    bb4: {
-        StorageDead(_6);
-        StorageDead(_5);
-        StorageDead(_4);
-        return;
-    }
-}
-
-ALLOC0 (size: 8, align: 4) {
-    00 00 00 00 __ __ __ __                         │ ....░░░░
-}
diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir
new file mode 100644
index 0000000..af0a0ef
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,41 @@
+// MIR for `checked_shl` after PreCodegen
+
+fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
+    debug x => _1;
+    debug rhs => _2;
+    let mut _0: std::option::Option<u32>;
+    scope 1 (inlined core::num::<impl u32>::checked_shl) {
+        let mut _3: bool;
+        let mut _4: u32;
+        scope 2 (inlined core::num::<impl u32>::unchecked_shl) {
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = Lt(_2, const core::num::<impl u32>::BITS);
+        switchInt(move _3) -> [0: bb1, otherwise: bb2];
+    }
+
+    bb1: {
+        _0 = const Option::<u32>::None;
+        goto -> bb3;
+    }
+
+    bb2: {
+        StorageLive(_4);
+        _4 = ShlUnchecked(_1, _2);
+        _0 = Option::<u32>::Some(move _4);
+        StorageDead(_4);
+        goto -> bb3;
+    }
+
+    bb3: {
+        StorageDead(_3);
+        return;
+    }
+}
+
+ALLOC0 (size: 8, align: 4) {
+    00 00 00 00 __ __ __ __                         │ ....░░░░
+}
diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 0000000..af0a0ef
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,41 @@
+// MIR for `checked_shl` after PreCodegen
+
+fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
+    debug x => _1;
+    debug rhs => _2;
+    let mut _0: std::option::Option<u32>;
+    scope 1 (inlined core::num::<impl u32>::checked_shl) {
+        let mut _3: bool;
+        let mut _4: u32;
+        scope 2 (inlined core::num::<impl u32>::unchecked_shl) {
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = Lt(_2, const core::num::<impl u32>::BITS);
+        switchInt(move _3) -> [0: bb1, otherwise: bb2];
+    }
+
+    bb1: {
+        _0 = const Option::<u32>::None;
+        goto -> bb3;
+    }
+
+    bb2: {
+        StorageLive(_4);
+        _4 = ShlUnchecked(_1, _2);
+        _0 = Option::<u32>::Some(move _4);
+        StorageDead(_4);
+        goto -> bb3;
+    }
+
+    bb3: {
+        StorageDead(_3);
+        return;
+    }
+}
+
+ALLOC0 (size: 8, align: 4) {
+    00 00 00 00 __ __ __ __                         │ ....░░░░
+}
diff --git a/tests/mir-opt/pre-codegen/checked_ops.rs b/tests/mir-opt/pre-codegen/checked_ops.rs
index 3ff1123..56f8e3f 100644
--- a/tests/mir-opt/pre-codegen/checked_ops.rs
+++ b/tests/mir-opt/pre-codegen/checked_ops.rs
@@ -1,12 +1,13 @@
 // skip-filecheck
-//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=2
-//@ needs-unwind
+//@ compile-flags: -O -Zmir-opt-level=2
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 #![crate_type = "lib"]
 #![feature(step_trait)]
 
 // EMIT_MIR checked_ops.step_forward.PreCodegen.after.mir
-pub fn step_forward(x: u32, n: usize) -> u32 {
+pub fn step_forward(x: u16, n: usize) -> u16 {
+    // This uses `u16` so that the conversion to usize is always widening.
     std::iter::Step::forward(x, n)
 }
 
diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.mir
deleted file mode 100644
index f1d0da2..0000000
--- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.mir
+++ /dev/null
@@ -1,15 +0,0 @@
-// MIR for `step_forward` after PreCodegen
-
-fn step_forward(_1: u32, _2: usize) -> u32 {
-    debug x => _1;
-    debug n => _2;
-    let mut _0: u32;
-
-    bb0: {
-        _0 = <u32 as Step>::forward(move _1, move _2) -> [return: bb1, unwind continue];
-    }
-
-    bb1: {
-        return;
-    }
-}
diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir
new file mode 100644
index 0000000..cfb9134
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,15 @@
+// MIR for `step_forward` after PreCodegen
+
+fn step_forward(_1: u16, _2: usize) -> u16 {
+    debug x => _1;
+    debug n => _2;
+    let mut _0: u16;
+
+    bb0: {
+        _0 = <u16 as Step>::forward(move _1, move _2) -> [return: bb1, unwind unreachable];
+    }
+
+    bb1: {
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 0000000..cacc122
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,15 @@
+// MIR for `step_forward` after PreCodegen
+
+fn step_forward(_1: u16, _2: usize) -> u16 {
+    debug x => _1;
+    debug n => _2;
+    let mut _0: u16;
+
+    bb0: {
+        _0 = <u16 as Step>::forward(move _1, move _2) -> [return: bb1, unwind continue];
+    }
+
+    bb1: {
+        return;
+    }
+}
diff --git "a/tests/mir-opt/pre-codegen/derived_ord.\173impl\0430\175-partial_cmp.PreCodegen.after.mir" "b/tests/mir-opt/pre-codegen/derived_ord.\173impl\0430\175-partial_cmp.PreCodegen.after.mir"
index a6c6442..47f1045 100644
--- "a/tests/mir-opt/pre-codegen/derived_ord.\173impl\0430\175-partial_cmp.PreCodegen.after.mir"
+++ "b/tests/mir-opt/pre-codegen/derived_ord.\173impl\0430\175-partial_cmp.PreCodegen.after.mir"
@@ -4,71 +4,51 @@
     debug self => _1;
     debug other => _2;
     let mut _0: std::option::Option<std::cmp::Ordering>;
-    let mut _3: &char;
-    let mut _4: &char;
-    let mut _8: std::option::Option<std::cmp::Ordering>;
-    let mut _9: i8;
-    let mut _10: &i16;
-    let mut _11: &i16;
+    let mut _6: std::option::Option<std::cmp::Ordering>;
+    let mut _7: i8;
     scope 1 {
-        debug cmp => _8;
+        debug cmp => _6;
     }
     scope 2 (inlined std::cmp::impls::<impl PartialOrd for char>::partial_cmp) {
-        debug self => _3;
-        debug other => _4;
-        let mut _5: char;
-        let mut _6: char;
-        let mut _7: std::cmp::Ordering;
+        let mut _3: char;
+        let mut _4: char;
+        let mut _5: std::cmp::Ordering;
     }
     scope 3 (inlined std::cmp::impls::<impl PartialOrd for i16>::partial_cmp) {
-        debug self => _10;
-        debug other => _11;
-        let mut _12: i16;
-        let mut _13: i16;
-        let mut _14: std::cmp::Ordering;
+        let mut _8: i16;
+        let mut _9: i16;
+        let mut _10: std::cmp::Ordering;
     }
 
     bb0: {
         StorageLive(_3);
-        _3 = &((*_1).0: char);
+        _3 = ((*_1).0: char);
         StorageLive(_4);
-        _4 = &((*_2).0: char);
-        StorageLive(_5);
-        _5 = ((*_1).0: char);
-        StorageLive(_6);
-        _6 = ((*_2).0: char);
-        _7 = Cmp(move _5, move _6);
-        StorageDead(_6);
-        StorageDead(_5);
-        _8 = Option::<std::cmp::Ordering>::Some(_7);
+        _4 = ((*_2).0: char);
+        _5 = Cmp(move _3, move _4);
         StorageDead(_4);
         StorageDead(_3);
-        _9 = discriminant(_7);
-        switchInt(move _9) -> [0: bb1, otherwise: bb2];
+        _6 = Option::<std::cmp::Ordering>::Some(_5);
+        _7 = discriminant(_5);
+        switchInt(move _7) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
         StorageLive(_10);
-        _10 = &((*_1).1: i16);
-        StorageLive(_11);
-        _11 = &((*_2).1: i16);
-        StorageLive(_14);
-        StorageLive(_12);
-        _12 = ((*_1).1: i16);
-        StorageLive(_13);
-        _13 = ((*_2).1: i16);
-        _14 = Cmp(move _12, move _13);
-        StorageDead(_13);
-        StorageDead(_12);
-        _0 = Option::<std::cmp::Ordering>::Some(move _14);
-        StorageDead(_14);
-        StorageDead(_11);
+        StorageLive(_8);
+        _8 = ((*_1).1: i16);
+        StorageLive(_9);
+        _9 = ((*_2).1: i16);
+        _10 = Cmp(move _8, move _9);
+        StorageDead(_9);
+        StorageDead(_8);
+        _0 = Option::<std::cmp::Ordering>::Some(move _10);
         StorageDead(_10);
         goto -> bb3;
     }
 
     bb2: {
-        _0 = _8;
+        _0 = _6;
         goto -> bb3;
     }
 
diff --git a/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir
index 174fb2c..1109da3 100644
--- a/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir
@@ -3,7 +3,6 @@
 fn f_u64() -> () {
     let mut _0: ();
     scope 1 (inlined f_dispatch::<u64>) {
-        debug t => const 0_u64;
         let _1: ();
         scope 2 (inlined std::mem::size_of::<u64>) {
         }
diff --git a/tests/mir-opt/pre-codegen/intrinsics.f_unit.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/intrinsics.f_unit.PreCodegen.after.mir
index 578cb2d..2768c2b 100644
--- a/tests/mir-opt/pre-codegen/intrinsics.f_unit.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/intrinsics.f_unit.PreCodegen.after.mir
@@ -3,7 +3,6 @@
 fn f_unit() -> () {
     let mut _0: ();
     scope 1 (inlined f_dispatch::<()>) {
-        debug t => const ();
         let _1: ();
         scope 2 (inlined std::mem::size_of::<()>) {
         }
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
index 2865b82..17d8375 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
@@ -18,11 +18,8 @@
               debug ptr => _3;
           }
           scope 5 (inlined <std::alloc::Global as Allocator>::allocate) {
-              debug self => _9;
-              debug layout => _8;
           }
           scope 6 (inlined #[track_caller] Result::<NonNull<[u8]>, std::alloc::AllocError>::unwrap) {
-              debug self => _6;
               let mut _12: isize;
               let _13: std::alloc::AllocError;
               let mut _14: !;
@@ -30,23 +27,18 @@
               let mut _16: &dyn std::fmt::Debug;
               let mut _17: &std::alloc::AllocError;
               scope 7 {
-                  debug t => _5;
               }
               scope 8 {
-                  debug e => const std::alloc::AllocError;
               }
           }
           scope 9 (inlined NonNull::<[u8]>::as_ptr) {
-              debug self => _5;
               let mut _18: *const [u8];
           }
       }
       scope 3 (inlined #[track_caller] Option::<Layout>::unwrap) {
-          debug self => _2;
           let mut _10: isize;
           let mut _11: !;
           scope 4 {
-              debug val => _1;
           }
       }
   
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
index 603b140..4a37c86 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
@@ -18,20 +18,15 @@
               debug ptr => _3;
           }
           scope 5 (inlined <std::alloc::Global as Allocator>::allocate) {
-              debug self => _9;
-              debug layout => _8;
           }
           scope 6 (inlined NonNull::<[u8]>::as_ptr) {
-              debug self => _5;
               let mut _12: *const [u8];
           }
       }
       scope 3 (inlined #[track_caller] Option::<Layout>::unwrap) {
-          debug self => _2;
           let mut _10: isize;
           let mut _11: !;
           scope 4 {
-              debug val => _1;
           }
       }
   
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
index 4496439..1cf9504 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
@@ -18,11 +18,8 @@
               debug ptr => _3;
           }
           scope 5 (inlined <std::alloc::Global as Allocator>::allocate) {
-              debug self => _9;
-              debug layout => _8;
           }
           scope 6 (inlined #[track_caller] Result::<NonNull<[u8]>, std::alloc::AllocError>::unwrap) {
-              debug self => _6;
               let mut _12: isize;
               let _13: std::alloc::AllocError;
               let mut _14: !;
@@ -30,23 +27,18 @@
               let mut _16: &dyn std::fmt::Debug;
               let mut _17: &std::alloc::AllocError;
               scope 7 {
-                  debug t => _5;
               }
               scope 8 {
-                  debug e => const std::alloc::AllocError;
               }
           }
           scope 9 (inlined NonNull::<[u8]>::as_ptr) {
-              debug self => _5;
               let mut _18: *const [u8];
           }
       }
       scope 3 (inlined #[track_caller] Option::<Layout>::unwrap) {
-          debug self => _2;
           let mut _10: isize;
           let mut _11: !;
           scope 4 {
-              debug val => _1;
           }
       }
   
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
index 5fd4af2..ec2e95f 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
@@ -18,20 +18,15 @@
               debug ptr => _3;
           }
           scope 5 (inlined <std::alloc::Global as Allocator>::allocate) {
-              debug self => _9;
-              debug layout => _8;
           }
           scope 6 (inlined NonNull::<[u8]>::as_ptr) {
-              debug self => _5;
               let mut _12: *const [u8];
           }
       }
       scope 3 (inlined #[track_caller] Option::<Layout>::unwrap) {
-          debug self => _2;
           let mut _10: isize;
           let mut _11: !;
           scope 4 {
-              debug val => _1;
           }
       }
   
diff --git a/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.panic-abort.mir
index c744787..a3dc54f 100644
--- a/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.panic-abort.mir
@@ -5,17 +5,11 @@
     debug v => _2;
     let mut _0: u32;
     scope 1 (inlined std::mem::replace::<u32>) {
-        debug dest => _1;
-        debug src => _2;
         scope 2 {
-            debug result => _0;
             scope 4 (inlined std::ptr::write::<u32>) {
-                debug dst => _1;
-                debug src => _2;
             }
         }
         scope 3 (inlined std::ptr::read::<u32>) {
-            debug src => _1;
         }
     }
 
diff --git a/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.panic-unwind.mir
index c744787..a3dc54f 100644
--- a/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/mem_replace.mem_replace.PreCodegen.after.panic-unwind.mir
@@ -5,17 +5,11 @@
     debug v => _2;
     let mut _0: u32;
     scope 1 (inlined std::mem::replace::<u32>) {
-        debug dest => _1;
-        debug src => _2;
         scope 2 {
-            debug result => _0;
             scope 4 (inlined std::ptr::write::<u32>) {
-                debug dst => _1;
-                debug src => _2;
             }
         }
         scope 3 (inlined std::ptr::read::<u32>) {
-            debug src => _1;
         }
     }
 
diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir
new file mode 100644
index 0000000..db0c84b
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,44 @@
+// MIR for `demo_byte_add_fat` after PreCodegen
+
+fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
+    debug p => _1;
+    debug n => _2;
+    let mut _0: *const [u32];
+    scope 1 (inlined std::ptr::const_ptr::<impl *const [u32]>::byte_add) {
+        let mut _3: *const u8;
+        let mut _4: *const u8;
+        scope 2 (inlined std::ptr::const_ptr::<impl *const [u32]>::cast::<u8>) {
+        }
+        scope 3 (inlined std::ptr::const_ptr::<impl *const u8>::add) {
+        }
+        scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
+            let mut _5: *const ();
+            let mut _7: usize;
+            scope 5 (inlined std::ptr::metadata::<[u32]>) {
+                let mut _6: std::ptr::metadata::PtrRepr<[u32]>;
+            }
+            scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) {
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_4);
+        StorageLive(_3);
+        _3 = _1 as *const u8 (PtrToPtr);
+        _4 = Offset(_3, _2);
+        StorageDead(_3);
+        StorageLive(_5);
+        _5 = _4 as *const () (PtrToPtr);
+        StorageLive(_7);
+        StorageLive(_6);
+        _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 };
+        _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize);
+        StorageDead(_6);
+        _0 = *const [u32] from (_5, _7);
+        StorageDead(_7);
+        StorageDead(_5);
+        StorageDead(_4);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 0000000..db0c84b
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,44 @@
+// MIR for `demo_byte_add_fat` after PreCodegen
+
+fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
+    debug p => _1;
+    debug n => _2;
+    let mut _0: *const [u32];
+    scope 1 (inlined std::ptr::const_ptr::<impl *const [u32]>::byte_add) {
+        let mut _3: *const u8;
+        let mut _4: *const u8;
+        scope 2 (inlined std::ptr::const_ptr::<impl *const [u32]>::cast::<u8>) {
+        }
+        scope 3 (inlined std::ptr::const_ptr::<impl *const u8>::add) {
+        }
+        scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
+            let mut _5: *const ();
+            let mut _7: usize;
+            scope 5 (inlined std::ptr::metadata::<[u32]>) {
+                let mut _6: std::ptr::metadata::PtrRepr<[u32]>;
+            }
+            scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) {
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_4);
+        StorageLive(_3);
+        _3 = _1 as *const u8 (PtrToPtr);
+        _4 = Offset(_3, _2);
+        StorageDead(_3);
+        StorageLive(_5);
+        _5 = _4 as *const () (PtrToPtr);
+        StorageLive(_7);
+        StorageLive(_6);
+        _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 };
+        _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize);
+        StorageDead(_6);
+        _0 = *const [u32] from (_5, _7);
+        StorageDead(_7);
+        StorageDead(_5);
+        StorageDead(_4);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir
new file mode 100644
index 0000000..766bd29
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,30 @@
+// MIR for `demo_byte_add_thin` after PreCodegen
+
+fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 {
+    debug p => _1;
+    debug n => _2;
+    let mut _0: *const u32;
+    scope 1 (inlined std::ptr::const_ptr::<impl *const u32>::byte_add) {
+        let mut _3: *const u8;
+        let mut _4: *const u8;
+        scope 2 (inlined std::ptr::const_ptr::<impl *const u32>::cast::<u8>) {
+        }
+        scope 3 (inlined std::ptr::const_ptr::<impl *const u8>::add) {
+        }
+        scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<u32>) {
+            scope 5 (inlined std::ptr::metadata::<u32>) {
+            }
+            scope 6 (inlined std::ptr::from_raw_parts::<u32>) {
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = _1 as *const u8 (PtrToPtr);
+        _4 = Offset(_3, _2);
+        StorageDead(_3);
+        _0 = _4 as *const u32 (PtrToPtr);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 0000000..766bd29
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,30 @@
+// MIR for `demo_byte_add_thin` after PreCodegen
+
+fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 {
+    debug p => _1;
+    debug n => _2;
+    let mut _0: *const u32;
+    scope 1 (inlined std::ptr::const_ptr::<impl *const u32>::byte_add) {
+        let mut _3: *const u8;
+        let mut _4: *const u8;
+        scope 2 (inlined std::ptr::const_ptr::<impl *const u32>::cast::<u8>) {
+        }
+        scope 3 (inlined std::ptr::const_ptr::<impl *const u8>::add) {
+        }
+        scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<u32>) {
+            scope 5 (inlined std::ptr::metadata::<u32>) {
+            }
+            scope 6 (inlined std::ptr::from_raw_parts::<u32>) {
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_3);
+        _3 = _1 as *const u8 (PtrToPtr);
+        _4 = Offset(_3, _2);
+        StorageDead(_3);
+        _0 = _4 as *const u32 (PtrToPtr);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/ptr_offset.rs b/tests/mir-opt/pre-codegen/ptr_offset.rs
new file mode 100644
index 0000000..88ee002
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/ptr_offset.rs
@@ -0,0 +1,16 @@
+// skip-filecheck
+//@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -Zinline-mir
+//@ ignore-debug: precondition checks are under cfg(debug_assertions)
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+
+#![crate_type = "lib"]
+
+// EMIT_MIR ptr_offset.demo_byte_add_thin.PreCodegen.after.mir
+pub unsafe fn demo_byte_add_thin(p: *const u32, n: usize) -> *const u32 {
+    p.byte_add(n)
+}
+
+// EMIT_MIR ptr_offset.demo_byte_add_fat.PreCodegen.after.mir
+pub unsafe fn demo_byte_add_fat(p: *const [u32], n: usize) -> *const [u32] {
+    p.byte_add(n)
+}
diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir
index 002d55a..96b4962 100644
--- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir
@@ -5,77 +5,56 @@
     debug end => _2;
     debug f => _3;
     let mut _0: ();
-    let mut _4: std::ops::Range<u32>;
-    let mut _5: std::ops::Range<u32>;
-    let mut _6: &mut std::ops::Range<u32>;
-    let mut _14: std::option::Option<u32>;
-    let mut _16: &impl Fn(u32);
-    let mut _17: (u32,);
-    let _18: ();
+    let mut _4: u32;
+    let mut _9: std::option::Option<u32>;
+    let mut _11: &impl Fn(u32);
+    let mut _12: (u32,);
+    let _13: ();
     scope 1 {
-        debug iter => _5;
-        let _15: u32;
+        debug ((iter: std::ops::Range<u32>).0: u32) => _4;
+        debug ((iter: std::ops::Range<u32>).1: u32) => _2;
+        let _10: u32;
         scope 2 {
-            debug x => _15;
+            debug x => _10;
         }
         scope 4 (inlined iter::range::<impl Iterator for std::ops::Range<u32>>::next) {
-            debug self => _6;
             scope 5 (inlined <std::ops::Range<u32> as iter::range::RangeIteratorImpl>::spec_next) {
-                debug self => _6;
-                let mut _7: &u32;
-                let mut _8: &u32;
-                let mut _11: bool;
-                let _12: u32;
-                let mut _13: u32;
+                let mut _6: bool;
+                let _7: u32;
+                let mut _8: u32;
                 scope 6 {
-                    debug old => _12;
                 }
                 scope 7 (inlined std::cmp::impls::<impl PartialOrd for u32>::lt) {
-                    debug self => _7;
-                    debug other => _8;
-                    let mut _9: u32;
-                    let mut _10: u32;
+                    let mut _5: u32;
                 }
             }
         }
     }
     scope 3 (inlined <std::ops::Range<u32> as IntoIterator>::into_iter) {
-        debug self => _4;
     }
 
     bb0: {
-        _4 = std::ops::Range::<u32> { start: _1, end: _2 };
-        StorageLive(_5);
-        _5 = _4;
+        StorageLive(_4);
+        _4 = _1;
         goto -> bb1;
     }
 
     bb1: {
-        StorageLive(_14);
-        _6 = &mut _5;
-        StorageLive(_12);
-        StorageLive(_11);
-        StorageLive(_7);
-        _7 = &(_5.0: u32);
-        StorageLive(_8);
-        _8 = &(_5.1: u32);
         StorageLive(_9);
-        _9 = (_5.0: u32);
-        StorageLive(_10);
-        _10 = (_5.1: u32);
-        _11 = Lt(move _9, move _10);
-        StorageDead(_10);
-        StorageDead(_9);
-        switchInt(move _11) -> [0: bb2, otherwise: bb4];
+        StorageLive(_7);
+        StorageLive(_6);
+        StorageLive(_5);
+        _5 = _4;
+        _6 = Lt(move _5, _2);
+        StorageDead(_5);
+        switchInt(move _6) -> [0: bb2, otherwise: bb4];
     }
 
     bb2: {
-        StorageDead(_8);
+        StorageDead(_6);
         StorageDead(_7);
-        StorageDead(_11);
-        StorageDead(_12);
-        StorageDead(_14);
-        StorageDead(_5);
+        StorageDead(_9);
+        StorageDead(_4);
         drop(_3) -> [return: bb3, unwind unreachable];
     }
 
@@ -84,31 +63,29 @@
     }
 
     bb4: {
-        StorageDead(_8);
-        StorageDead(_7);
-        _12 = (_5.0: u32);
-        StorageLive(_13);
-        _13 = <u32 as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb5, unwind unreachable];
+        _7 = _4;
+        StorageLive(_8);
+        _8 = <u32 as Step>::forward_unchecked(_7, const 1_usize) -> [return: bb5, unwind unreachable];
     }
 
     bb5: {
-        (_5.0: u32) = move _13;
-        StorageDead(_13);
-        _14 = Option::<u32>::Some(_12);
-        StorageDead(_11);
-        StorageDead(_12);
-        _15 = ((_14 as Some).0: u32);
-        StorageLive(_16);
-        _16 = &_3;
-        StorageLive(_17);
-        _17 = (_15,);
-        _18 = <impl Fn(u32) as Fn<(u32,)>>::call(move _16, move _17) -> [return: bb6, unwind unreachable];
+        _4 = move _8;
+        StorageDead(_8);
+        _9 = Option::<u32>::Some(_7);
+        StorageDead(_6);
+        StorageDead(_7);
+        _10 = ((_9 as Some).0: u32);
+        StorageLive(_11);
+        _11 = &_3;
+        StorageLive(_12);
+        _12 = (_10,);
+        _13 = <impl Fn(u32) as Fn<(u32,)>>::call(move _11, move _12) -> [return: bb6, unwind unreachable];
     }
 
     bb6: {
-        StorageDead(_17);
-        StorageDead(_16);
-        StorageDead(_14);
+        StorageDead(_12);
+        StorageDead(_11);
+        StorageDead(_9);
         goto -> bb1;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir
index d5021ac..ce8e2bd 100644
--- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir
@@ -5,77 +5,56 @@
     debug end => _2;
     debug f => _3;
     let mut _0: ();
-    let mut _4: std::ops::Range<u32>;
-    let mut _5: std::ops::Range<u32>;
-    let mut _6: &mut std::ops::Range<u32>;
-    let mut _14: std::option::Option<u32>;
-    let mut _16: &impl Fn(u32);
-    let mut _17: (u32,);
-    let _18: ();
+    let mut _4: u32;
+    let mut _9: std::option::Option<u32>;
+    let mut _11: &impl Fn(u32);
+    let mut _12: (u32,);
+    let _13: ();
     scope 1 {
-        debug iter => _5;
-        let _15: u32;
+        debug ((iter: std::ops::Range<u32>).0: u32) => _4;
+        debug ((iter: std::ops::Range<u32>).1: u32) => _2;
+        let _10: u32;
         scope 2 {
-            debug x => _15;
+            debug x => _10;
         }
         scope 4 (inlined iter::range::<impl Iterator for std::ops::Range<u32>>::next) {
-            debug self => _6;
             scope 5 (inlined <std::ops::Range<u32> as iter::range::RangeIteratorImpl>::spec_next) {
-                debug self => _6;
-                let mut _7: &u32;
-                let mut _8: &u32;
-                let mut _11: bool;
-                let _12: u32;
-                let mut _13: u32;
+                let mut _6: bool;
+                let _7: u32;
+                let mut _8: u32;
                 scope 6 {
-                    debug old => _12;
                 }
                 scope 7 (inlined std::cmp::impls::<impl PartialOrd for u32>::lt) {
-                    debug self => _7;
-                    debug other => _8;
-                    let mut _9: u32;
-                    let mut _10: u32;
+                    let mut _5: u32;
                 }
             }
         }
     }
     scope 3 (inlined <std::ops::Range<u32> as IntoIterator>::into_iter) {
-        debug self => _4;
     }
 
     bb0: {
-        _4 = std::ops::Range::<u32> { start: _1, end: _2 };
-        StorageLive(_5);
-        _5 = _4;
+        StorageLive(_4);
+        _4 = _1;
         goto -> bb1;
     }
 
     bb1: {
-        StorageLive(_14);
-        _6 = &mut _5;
-        StorageLive(_12);
-        StorageLive(_11);
-        StorageLive(_7);
-        _7 = &(_5.0: u32);
-        StorageLive(_8);
-        _8 = &(_5.1: u32);
         StorageLive(_9);
-        _9 = (_5.0: u32);
-        StorageLive(_10);
-        _10 = (_5.1: u32);
-        _11 = Lt(move _9, move _10);
-        StorageDead(_10);
-        StorageDead(_9);
-        switchInt(move _11) -> [0: bb2, otherwise: bb4];
+        StorageLive(_7);
+        StorageLive(_6);
+        StorageLive(_5);
+        _5 = _4;
+        _6 = Lt(move _5, _2);
+        StorageDead(_5);
+        switchInt(move _6) -> [0: bb2, otherwise: bb4];
     }
 
     bb2: {
-        StorageDead(_8);
+        StorageDead(_6);
         StorageDead(_7);
-        StorageDead(_11);
-        StorageDead(_12);
-        StorageDead(_14);
-        StorageDead(_5);
+        StorageDead(_9);
+        StorageDead(_4);
         drop(_3) -> [return: bb3, unwind continue];
     }
 
@@ -84,31 +63,29 @@
     }
 
     bb4: {
-        StorageDead(_8);
-        StorageDead(_7);
-        _12 = (_5.0: u32);
-        StorageLive(_13);
-        _13 = <u32 as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb5, unwind: bb7];
+        _7 = _4;
+        StorageLive(_8);
+        _8 = <u32 as Step>::forward_unchecked(_7, const 1_usize) -> [return: bb5, unwind: bb7];
     }
 
     bb5: {
-        (_5.0: u32) = move _13;
-        StorageDead(_13);
-        _14 = Option::<u32>::Some(_12);
-        StorageDead(_11);
-        StorageDead(_12);
-        _15 = ((_14 as Some).0: u32);
-        StorageLive(_16);
-        _16 = &_3;
-        StorageLive(_17);
-        _17 = (_15,);
-        _18 = <impl Fn(u32) as Fn<(u32,)>>::call(move _16, move _17) -> [return: bb6, unwind: bb7];
+        _4 = move _8;
+        StorageDead(_8);
+        _9 = Option::<u32>::Some(_7);
+        StorageDead(_6);
+        StorageDead(_7);
+        _10 = ((_9 as Some).0: u32);
+        StorageLive(_11);
+        _11 = &_3;
+        StorageLive(_12);
+        _12 = (_10,);
+        _13 = <impl Fn(u32) as Fn<(u32,)>>::call(move _11, move _12) -> [return: bb6, unwind: bb7];
     }
 
     bb6: {
-        StorageDead(_17);
-        StorageDead(_16);
-        StorageDead(_14);
+        StorageDead(_12);
+        StorageDead(_11);
+        StorageDead(_9);
         goto -> bb1;
     }
 
diff --git a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir
index 0836600..ce79a33 100644
--- a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir
@@ -20,15 +20,11 @@
             debug x => _9;
         }
         scope 5 (inlined iter::range::<impl Iterator for RangeInclusive<u32>>::next) {
-            debug self => _6;
         }
     }
     scope 3 (inlined RangeInclusive::<u32>::new) {
-        debug start => _1;
-        debug end => _2;
     }
     scope 4 (inlined <RangeInclusive<u32> as IntoIterator>::into_iter) {
-        debug self => _4;
     }
 
     bb0: {
diff --git a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir
index 8c1794d..602ecb7 100644
--- a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir
@@ -20,15 +20,11 @@
             debug x => _9;
         }
         scope 5 (inlined iter::range::<impl Iterator for RangeInclusive<u32>>::next) {
-            debug self => _6;
         }
     }
     scope 3 (inlined RangeInclusive::<u32>::new) {
-        debug start => _1;
-        debug end => _2;
     }
     scope 4 (inlined <RangeInclusive<u32> as IntoIterator>::into_iter) {
-        debug self => _4;
     }
 
     bb0: {
diff --git a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir
index b0f475b..8e03824 100644
--- a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir
@@ -4,7 +4,6 @@
     debug it => _1;
     let mut _0: std::option::Option<u32>;
     scope 1 (inlined iter::range::<impl Iterator for RangeInclusive<u32>>::next) {
-        debug self => _1;
     }
 
     bb0: {
diff --git a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir
index 663ec22..f54d003 100644
--- a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir
@@ -4,7 +4,6 @@
     debug it => _1;
     let mut _0: std::option::Option<u32>;
     scope 1 (inlined iter::range::<impl Iterator for RangeInclusive<u32>>::next) {
-        debug self => _1;
     }
 
     bb0: {
diff --git a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir
index 7faae1d..2ac7e88 100644
--- a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir
@@ -4,68 +4,53 @@
     debug it => _1;
     let mut _0: std::option::Option<u32>;
     scope 1 (inlined iter::range::<impl Iterator for std::ops::Range<u32>>::next) {
-        debug self => _1;
         scope 2 (inlined <std::ops::Range<u32> as iter::range::RangeIteratorImpl>::spec_next) {
-            debug self => _1;
-            let mut _2: &u32;
-            let mut _3: &u32;
-            let mut _6: bool;
-            let _7: u32;
-            let mut _8: u32;
+            let mut _4: bool;
+            let _5: u32;
+            let mut _6: u32;
             scope 3 {
-                debug old => _7;
             }
             scope 4 (inlined std::cmp::impls::<impl PartialOrd for u32>::lt) {
-                debug self => _2;
-                debug other => _3;
-                let mut _4: u32;
-                let mut _5: u32;
+                let mut _2: u32;
+                let mut _3: u32;
             }
         }
     }
 
     bb0: {
-        StorageLive(_7);
-        StorageLive(_6);
-        StorageLive(_2);
-        _2 = &((*_1).0: u32);
-        StorageLive(_3);
-        _3 = &((*_1).1: u32);
-        StorageLive(_4);
-        _4 = ((*_1).0: u32);
         StorageLive(_5);
-        _5 = ((*_1).1: u32);
-        _6 = Lt(move _4, move _5);
-        StorageDead(_5);
-        StorageDead(_4);
-        switchInt(move _6) -> [0: bb1, otherwise: bb2];
+        StorageLive(_4);
+        StorageLive(_2);
+        _2 = ((*_1).0: u32);
+        StorageLive(_3);
+        _3 = ((*_1).1: u32);
+        _4 = Lt(move _2, move _3);
+        StorageDead(_3);
+        StorageDead(_2);
+        switchInt(move _4) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageDead(_3);
-        StorageDead(_2);
         _0 = const Option::<u32>::None;
         goto -> bb4;
     }
 
     bb2: {
-        StorageDead(_3);
-        StorageDead(_2);
-        _7 = ((*_1).0: u32);
-        StorageLive(_8);
-        _8 = <u32 as Step>::forward_unchecked(_7, const 1_usize) -> [return: bb3, unwind unreachable];
+        _5 = ((*_1).0: u32);
+        StorageLive(_6);
+        _6 = <u32 as Step>::forward_unchecked(_5, const 1_usize) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        ((*_1).0: u32) = move _8;
-        StorageDead(_8);
-        _0 = Option::<u32>::Some(_7);
+        ((*_1).0: u32) = move _6;
+        StorageDead(_6);
+        _0 = Option::<u32>::Some(_5);
         goto -> bb4;
     }
 
     bb4: {
-        StorageDead(_6);
-        StorageDead(_7);
+        StorageDead(_4);
+        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir
index 37f0053..60bf644 100644
--- a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir
@@ -4,68 +4,53 @@
     debug it => _1;
     let mut _0: std::option::Option<u32>;
     scope 1 (inlined iter::range::<impl Iterator for std::ops::Range<u32>>::next) {
-        debug self => _1;
         scope 2 (inlined <std::ops::Range<u32> as iter::range::RangeIteratorImpl>::spec_next) {
-            debug self => _1;
-            let mut _2: &u32;
-            let mut _3: &u32;
-            let mut _6: bool;
-            let _7: u32;
-            let mut _8: u32;
+            let mut _4: bool;
+            let _5: u32;
+            let mut _6: u32;
             scope 3 {
-                debug old => _7;
             }
             scope 4 (inlined std::cmp::impls::<impl PartialOrd for u32>::lt) {
-                debug self => _2;
-                debug other => _3;
-                let mut _4: u32;
-                let mut _5: u32;
+                let mut _2: u32;
+                let mut _3: u32;
             }
         }
     }
 
     bb0: {
-        StorageLive(_7);
-        StorageLive(_6);
-        StorageLive(_2);
-        _2 = &((*_1).0: u32);
-        StorageLive(_3);
-        _3 = &((*_1).1: u32);
-        StorageLive(_4);
-        _4 = ((*_1).0: u32);
         StorageLive(_5);
-        _5 = ((*_1).1: u32);
-        _6 = Lt(move _4, move _5);
-        StorageDead(_5);
-        StorageDead(_4);
-        switchInt(move _6) -> [0: bb1, otherwise: bb2];
+        StorageLive(_4);
+        StorageLive(_2);
+        _2 = ((*_1).0: u32);
+        StorageLive(_3);
+        _3 = ((*_1).1: u32);
+        _4 = Lt(move _2, move _3);
+        StorageDead(_3);
+        StorageDead(_2);
+        switchInt(move _4) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageDead(_3);
-        StorageDead(_2);
         _0 = const Option::<u32>::None;
         goto -> bb4;
     }
 
     bb2: {
-        StorageDead(_3);
-        StorageDead(_2);
-        _7 = ((*_1).0: u32);
-        StorageLive(_8);
-        _8 = <u32 as Step>::forward_unchecked(_7, const 1_usize) -> [return: bb3, unwind continue];
+        _5 = ((*_1).0: u32);
+        StorageLive(_6);
+        _6 = <u32 as Step>::forward_unchecked(_5, const 1_usize) -> [return: bb3, unwind continue];
     }
 
     bb3: {
-        ((*_1).0: u32) = move _8;
-        StorageDead(_8);
-        _0 = Option::<u32>::Some(_7);
+        ((*_1).0: u32) = move _6;
+        StorageDead(_6);
+        _0 = Option::<u32>::Some(_5);
         goto -> bb4;
     }
 
     bb4: {
-        StorageDead(_6);
-        StorageDead(_7);
+        StorageDead(_4);
+        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/simple_option_map.ezmap.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/simple_option_map.ezmap.PreCodegen.after.mir
index 7265a4f..030f9c3 100644
--- a/tests/mir-opt/pre-codegen/simple_option_map.ezmap.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/simple_option_map.ezmap.PreCodegen.after.mir
@@ -4,15 +4,11 @@
     debug x => _1;
     let mut _0: std::option::Option<i32>;
     scope 1 (inlined map::<i32, i32, {closure@$DIR/simple_option_map.rs:17:12: 17:15}>) {
-        debug slf => _1;
-        debug f => const ZeroSized: {closure@$DIR/simple_option_map.rs:17:12: 17:15};
         let mut _2: isize;
         let _3: i32;
         let mut _4: i32;
         scope 2 {
-            debug x => _3;
             scope 3 (inlined ezmap::{closure#0}) {
-                debug n => _3;
             }
         }
     }
diff --git a/tests/mir-opt/pre-codegen/slice_index.rs b/tests/mir-opt/pre-codegen/slice_index.rs
index c9dd72d..04bbbff 100644
--- a/tests/mir-opt/pre-codegen/slice_index.rs
+++ b/tests/mir-opt/pre-codegen/slice_index.rs
@@ -3,6 +3,7 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 #![crate_type = "lib"]
+#![feature(slice_ptr_get)]
 
 use std::ops::Range;
 
@@ -25,3 +26,11 @@
 pub unsafe fn slice_get_unchecked_mut_range(slice: &mut [u32], index: Range<usize>) -> &mut [u32] {
     slice.get_unchecked_mut(index)
 }
+
+// EMIT_MIR slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.mir
+pub unsafe fn slice_ptr_get_unchecked_range(
+    slice: *const [u32],
+    index: Range<usize>,
+) -> *const [u32] {
+    slice.get_unchecked(index)
+}
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir
index 153505b..7e20817 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir
@@ -5,8 +5,6 @@
     debug index => _2;
     let mut _0: std::option::Option<&mut u32>;
     scope 1 (inlined core::slice::<impl [u32]>::get_mut::<usize>) {
-        debug self => _1;
-        debug index => _2;
     }
 
     bb0: {
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir
index d37ee78..2f65b8c 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir
@@ -5,8 +5,6 @@
     debug index => _2;
     let mut _0: std::option::Option<&mut u32>;
     scope 1 (inlined core::slice::<impl [u32]>::get_mut::<usize>) {
-        debug self => _1;
-        debug index => _2;
     }
 
     bb0: {
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
index e549095..ef3f4a2 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
@@ -5,8 +5,6 @@
     debug index => _2;
     let mut _0: &mut [u32];
     scope 1 (inlined core::slice::<impl [u32]>::get_unchecked_mut::<std::ops::Range<usize>>) {
-        debug self => _1;
-        debug index => _2;
         let mut _3: *mut [u32];
         let mut _4: *mut [u32];
     }
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
index 810fee9..9e93a43 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
@@ -5,8 +5,6 @@
     debug index => _2;
     let mut _0: &mut [u32];
     scope 1 (inlined core::slice::<impl [u32]>::get_unchecked_mut::<std::ops::Range<usize>>) {
-        debug self => _1;
-        debug index => _2;
         let mut _3: *mut [u32];
         let mut _4: *mut [u32];
     }
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir
index d97c96a..731f643 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir
@@ -5,8 +5,6 @@
     debug index => _2;
     let mut _0: &[u32];
     scope 1 (inlined #[track_caller] core::slice::index::<impl Index<std::ops::Range<usize>> for [u32]>::index) {
-        debug self => _1;
-        debug index => _2;
     }
 
     bb0: {
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir
index 4a97600..d879d06 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir
@@ -5,8 +5,6 @@
     debug index => _2;
     let mut _0: &[u32];
     scope 1 (inlined #[track_caller] core::slice::index::<impl Index<std::ops::Range<usize>> for [u32]>::index) {
-        debug self => _1;
-        debug index => _2;
     }
 
     bb0: {
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir
new file mode 100644
index 0000000..018ff6c
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,52 @@
+// MIR for `slice_ptr_get_unchecked_range` after PreCodegen
+
+fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range<usize>) -> *const [u32] {
+    debug slice => _1;
+    debug index => _2;
+    let mut _0: *const [u32];
+    let mut _3: usize;
+    let mut _4: usize;
+    scope 1 (inlined std::ptr::const_ptr::<impl *const [u32]>::get_unchecked::<std::ops::Range<usize>>) {
+        scope 2 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked) {
+            let _5: usize;
+            let mut _6: *const u32;
+            let mut _7: *const u32;
+            scope 3 {
+                scope 6 (inlined std::ptr::const_ptr::<impl *const [u32]>::as_ptr) {
+                }
+                scope 7 (inlined std::ptr::const_ptr::<impl *const u32>::add) {
+                }
+                scope 8 (inlined slice_from_raw_parts::<u32>) {
+                    let mut _8: *const ();
+                    scope 9 (inlined std::ptr::const_ptr::<impl *const u32>::cast::<()>) {
+                    }
+                    scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) {
+                    }
+                }
+            }
+            scope 4 (inlined std::ptr::const_ptr::<impl *const [u32]>::len) {
+                scope 5 (inlined std::ptr::metadata::<[u32]>) {
+                }
+            }
+        }
+    }
+
+    bb0: {
+        _3 = move (_2.0: usize);
+        _4 = move (_2.1: usize);
+        StorageLive(_5);
+        _5 = SubUnchecked(_4, _3);
+        StorageLive(_7);
+        StorageLive(_6);
+        _6 = _1 as *const u32 (PtrToPtr);
+        _7 = Offset(_6, _3);
+        StorageDead(_6);
+        StorageLive(_8);
+        _8 = _7 as *const () (PtrToPtr);
+        _0 = *const [u32] from (_8, _5);
+        StorageDead(_8);
+        StorageDead(_7);
+        StorageDead(_5);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 0000000..018ff6c
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,52 @@
+// MIR for `slice_ptr_get_unchecked_range` after PreCodegen
+
+fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range<usize>) -> *const [u32] {
+    debug slice => _1;
+    debug index => _2;
+    let mut _0: *const [u32];
+    let mut _3: usize;
+    let mut _4: usize;
+    scope 1 (inlined std::ptr::const_ptr::<impl *const [u32]>::get_unchecked::<std::ops::Range<usize>>) {
+        scope 2 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked) {
+            let _5: usize;
+            let mut _6: *const u32;
+            let mut _7: *const u32;
+            scope 3 {
+                scope 6 (inlined std::ptr::const_ptr::<impl *const [u32]>::as_ptr) {
+                }
+                scope 7 (inlined std::ptr::const_ptr::<impl *const u32>::add) {
+                }
+                scope 8 (inlined slice_from_raw_parts::<u32>) {
+                    let mut _8: *const ();
+                    scope 9 (inlined std::ptr::const_ptr::<impl *const u32>::cast::<()>) {
+                    }
+                    scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) {
+                    }
+                }
+            }
+            scope 4 (inlined std::ptr::const_ptr::<impl *const [u32]>::len) {
+                scope 5 (inlined std::ptr::metadata::<[u32]>) {
+                }
+            }
+        }
+    }
+
+    bb0: {
+        _3 = move (_2.0: usize);
+        _4 = move (_2.1: usize);
+        StorageLive(_5);
+        _5 = SubUnchecked(_4, _3);
+        StorageLive(_7);
+        StorageLive(_6);
+        _6 = _1 as *const u32 (PtrToPtr);
+        _7 = Offset(_6, _3);
+        StorageDead(_6);
+        StorageLive(_8);
+        _8 = _7 as *const () (PtrToPtr);
+        _0 = *const [u32] from (_8, _5);
+        StorageDead(_8);
+        StorageDead(_7);
+        StorageDead(_5);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir
index 1ec8590..d979c5e 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir
@@ -4,147 +4,128 @@
     debug slice => _1;
     debug f => _2;
     let mut _0: ();
-    let mut _13: std::slice::Iter<'_, T>;
+    let mut _12: std::slice::Iter<'_, T>;
+    let mut _13: std::iter::Enumerate<std::slice::Iter<'_, T>>;
     let mut _14: std::iter::Enumerate<std::slice::Iter<'_, T>>;
-    let mut _15: std::iter::Enumerate<std::slice::Iter<'_, T>>;
-    let mut _16: &mut std::iter::Enumerate<std::slice::Iter<'_, T>>;
-    let mut _17: std::option::Option<(usize, &T)>;
-    let mut _18: isize;
-    let mut _21: &impl Fn(usize, &T);
-    let mut _22: (usize, &T);
-    let _23: ();
+    let mut _15: &mut std::iter::Enumerate<std::slice::Iter<'_, T>>;
+    let mut _16: std::option::Option<(usize, &T)>;
+    let mut _17: isize;
+    let mut _20: &impl Fn(usize, &T);
+    let mut _21: (usize, &T);
+    let _22: ();
     scope 1 {
-        debug iter => _15;
-        let _19: usize;
-        let _20: &T;
+        debug iter => _14;
+        let _18: usize;
+        let _19: &T;
         scope 2 {
-            debug i => _19;
-            debug x => _20;
+            debug i => _18;
+            debug x => _19;
         }
     }
     scope 3 (inlined core::slice::<impl [T]>::iter) {
-        debug self => _1;
         scope 4 (inlined std::slice::Iter::<'_, T>::new) {
-            debug slice => _1;
             let _3: usize;
-            let mut _5: std::ptr::NonNull<[T]>;
-            let mut _8: bool;
+            let mut _7: bool;
+            let mut _8: *mut T;
             let mut _9: *mut T;
-            let mut _10: *mut T;
-            let mut _12: *const T;
+            let mut _11: *const T;
             scope 5 {
-                debug len => _3;
-                let _7: std::ptr::NonNull<T>;
+                let _6: std::ptr::NonNull<T>;
                 scope 6 {
-                    debug ptr => _7;
-                    let _11: *const T;
+                    let _10: *const T;
                     scope 7 {
-                        debug end_or_len => _11;
                     }
                     scope 11 (inlined without_provenance::<T>) {
-                        debug addr => _3;
                     }
                     scope 12 (inlined NonNull::<T>::as_ptr) {
-                        debug self => _7;
                     }
                     scope 13 (inlined std::ptr::mut_ptr::<impl *mut T>::add) {
-                        debug self => _9;
-                        debug count => _3;
                     }
                 }
                 scope 8 (inlined <NonNull<[T]> as From<&[T]>>::from) {
-                    debug reference => _1;
                     let mut _4: *const [T];
                 }
                 scope 9 (inlined NonNull::<[T]>::cast::<T>) {
-                    debug self => _5;
-                    let mut _6: *const T;
+                    let mut _5: *const T;
                     scope 10 (inlined NonNull::<[T]>::as_ptr) {
-                        debug self => _5;
                     }
                 }
             }
         }
     }
     scope 14 (inlined <std::slice::Iter<'_, T> as Iterator>::enumerate) {
-        debug self => _13;
         scope 15 (inlined Enumerate::<std::slice::Iter<'_, T>>::new) {
-            debug iter => _13;
         }
     }
     scope 16 (inlined <Enumerate<std::slice::Iter<'_, T>> as IntoIterator>::into_iter) {
-        debug self => _14;
     }
 
     bb0: {
-        StorageLive(_13);
+        StorageLive(_12);
         StorageLive(_3);
-        StorageLive(_7);
-        StorageLive(_4);
         StorageLive(_6);
-        _3 = Len((*_1));
+        StorageLive(_4);
         StorageLive(_5);
+        _3 = Len((*_1));
         _4 = &raw const (*_1);
-        _5 = NonNull::<[T]> { pointer: _4 };
-        _6 = _4 as *const T (PtrToPtr);
-        _7 = NonNull::<T> { pointer: _6 };
-        StorageDead(_5);
-        StorageLive(_11);
-        StorageLive(_8);
-        _8 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
-        switchInt(move _8) -> [0: bb1, otherwise: bb2];
+        _5 = _4 as *const T (PtrToPtr);
+        _6 = NonNull::<T> { pointer: _5 };
+        StorageLive(_10);
+        StorageLive(_7);
+        _7 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
+        switchInt(move _7) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageLive(_10);
         StorageLive(_9);
-        _9 = _4 as *mut T (PtrToPtr);
-        _10 = Offset(_9, _3);
+        StorageLive(_8);
+        _8 = _4 as *mut T (PtrToPtr);
+        _9 = Offset(_8, _3);
+        StorageDead(_8);
+        _10 = move _9 as *const T (PointerCoercion(MutToConstPointer));
         StorageDead(_9);
-        _11 = move _10 as *const T (PointerCoercion(MutToConstPointer));
-        StorageDead(_10);
         goto -> bb3;
     }
 
     bb2: {
-        _11 = _3 as *const T (Transmute);
+        _10 = _3 as *const T (Transmute);
         goto -> bb3;
     }
 
     bb3: {
-        StorageDead(_8);
-        StorageLive(_12);
-        _12 = _11;
-        _13 = std::slice::Iter::<'_, T> { ptr: _7, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
-        StorageDead(_12);
-        StorageDead(_11);
-        StorageDead(_6);
-        StorageDead(_4);
         StorageDead(_7);
+        StorageLive(_11);
+        _11 = _10;
+        _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> };
+        StorageDead(_11);
+        StorageDead(_10);
+        StorageDead(_5);
+        StorageDead(_4);
+        StorageDead(_6);
         StorageDead(_3);
-        _14 = Enumerate::<std::slice::Iter<'_, T>> { iter: _13, count: const 0_usize };
-        StorageDead(_13);
-        StorageLive(_15);
-        _15 = _14;
+        _13 = Enumerate::<std::slice::Iter<'_, T>> { iter: _12, count: const 0_usize };
+        StorageDead(_12);
+        StorageLive(_14);
+        _14 = _13;
         goto -> bb4;
     }
 
     bb4: {
-        StorageLive(_17);
         StorageLive(_16);
-        _16 = &mut _15;
-        _17 = <Enumerate<std::slice::Iter<'_, T>> as Iterator>::next(move _16) -> [return: bb5, unwind unreachable];
+        StorageLive(_15);
+        _15 = &mut _14;
+        _16 = <Enumerate<std::slice::Iter<'_, T>> as Iterator>::next(move _15) -> [return: bb5, unwind unreachable];
     }
 
     bb5: {
-        StorageDead(_16);
-        _18 = discriminant(_17);
-        switchInt(move _18) -> [0: bb6, 1: bb8, otherwise: bb10];
+        StorageDead(_15);
+        _17 = discriminant(_16);
+        switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10];
     }
 
     bb6: {
-        StorageDead(_17);
-        StorageDead(_15);
+        StorageDead(_16);
+        StorageDead(_14);
         drop(_2) -> [return: bb7, unwind unreachable];
     }
 
@@ -153,19 +134,19 @@
     }
 
     bb8: {
-        _19 = (((_17 as Some).0: (usize, &T)).0: usize);
-        _20 = (((_17 as Some).0: (usize, &T)).1: &T);
+        _18 = (((_16 as Some).0: (usize, &T)).0: usize);
+        _19 = (((_16 as Some).0: (usize, &T)).1: &T);
+        StorageLive(_20);
+        _20 = &_2;
         StorageLive(_21);
-        _21 = &_2;
-        StorageLive(_22);
-        _22 = (_19, _20);
-        _23 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _21, move _22) -> [return: bb9, unwind unreachable];
+        _21 = (_18, _19);
+        _22 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _20, move _21) -> [return: bb9, unwind unreachable];
     }
 
     bb9: {
-        StorageDead(_22);
         StorageDead(_21);
-        StorageDead(_17);
+        StorageDead(_20);
+        StorageDead(_16);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
index 70cdf3f..8491c49 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
@@ -4,147 +4,128 @@
     debug slice => _1;
     debug f => _2;
     let mut _0: ();
-    let mut _13: std::slice::Iter<'_, T>;
+    let mut _12: std::slice::Iter<'_, T>;
+    let mut _13: std::iter::Enumerate<std::slice::Iter<'_, T>>;
     let mut _14: std::iter::Enumerate<std::slice::Iter<'_, T>>;
-    let mut _15: std::iter::Enumerate<std::slice::Iter<'_, T>>;
-    let mut _16: &mut std::iter::Enumerate<std::slice::Iter<'_, T>>;
-    let mut _17: std::option::Option<(usize, &T)>;
-    let mut _18: isize;
-    let mut _21: &impl Fn(usize, &T);
-    let mut _22: (usize, &T);
-    let _23: ();
+    let mut _15: &mut std::iter::Enumerate<std::slice::Iter<'_, T>>;
+    let mut _16: std::option::Option<(usize, &T)>;
+    let mut _17: isize;
+    let mut _20: &impl Fn(usize, &T);
+    let mut _21: (usize, &T);
+    let _22: ();
     scope 1 {
-        debug iter => _15;
-        let _19: usize;
-        let _20: &T;
+        debug iter => _14;
+        let _18: usize;
+        let _19: &T;
         scope 2 {
-            debug i => _19;
-            debug x => _20;
+            debug i => _18;
+            debug x => _19;
         }
     }
     scope 3 (inlined core::slice::<impl [T]>::iter) {
-        debug self => _1;
         scope 4 (inlined std::slice::Iter::<'_, T>::new) {
-            debug slice => _1;
             let _3: usize;
-            let mut _5: std::ptr::NonNull<[T]>;
-            let mut _8: bool;
+            let mut _7: bool;
+            let mut _8: *mut T;
             let mut _9: *mut T;
-            let mut _10: *mut T;
-            let mut _12: *const T;
+            let mut _11: *const T;
             scope 5 {
-                debug len => _3;
-                let _7: std::ptr::NonNull<T>;
+                let _6: std::ptr::NonNull<T>;
                 scope 6 {
-                    debug ptr => _7;
-                    let _11: *const T;
+                    let _10: *const T;
                     scope 7 {
-                        debug end_or_len => _11;
                     }
                     scope 11 (inlined without_provenance::<T>) {
-                        debug addr => _3;
                     }
                     scope 12 (inlined NonNull::<T>::as_ptr) {
-                        debug self => _7;
                     }
                     scope 13 (inlined std::ptr::mut_ptr::<impl *mut T>::add) {
-                        debug self => _9;
-                        debug count => _3;
                     }
                 }
                 scope 8 (inlined <NonNull<[T]> as From<&[T]>>::from) {
-                    debug reference => _1;
                     let mut _4: *const [T];
                 }
                 scope 9 (inlined NonNull::<[T]>::cast::<T>) {
-                    debug self => _5;
-                    let mut _6: *const T;
+                    let mut _5: *const T;
                     scope 10 (inlined NonNull::<[T]>::as_ptr) {
-                        debug self => _5;
                     }
                 }
             }
         }
     }
     scope 14 (inlined <std::slice::Iter<'_, T> as Iterator>::enumerate) {
-        debug self => _13;
         scope 15 (inlined Enumerate::<std::slice::Iter<'_, T>>::new) {
-            debug iter => _13;
         }
     }
     scope 16 (inlined <Enumerate<std::slice::Iter<'_, T>> as IntoIterator>::into_iter) {
-        debug self => _14;
     }
 
     bb0: {
-        StorageLive(_13);
+        StorageLive(_12);
         StorageLive(_3);
-        StorageLive(_7);
-        StorageLive(_4);
         StorageLive(_6);
-        _3 = Len((*_1));
+        StorageLive(_4);
         StorageLive(_5);
+        _3 = Len((*_1));
         _4 = &raw const (*_1);
-        _5 = NonNull::<[T]> { pointer: _4 };
-        _6 = _4 as *const T (PtrToPtr);
-        _7 = NonNull::<T> { pointer: _6 };
-        StorageDead(_5);
-        StorageLive(_11);
-        StorageLive(_8);
-        _8 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
-        switchInt(move _8) -> [0: bb1, otherwise: bb2];
+        _5 = _4 as *const T (PtrToPtr);
+        _6 = NonNull::<T> { pointer: _5 };
+        StorageLive(_10);
+        StorageLive(_7);
+        _7 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
+        switchInt(move _7) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageLive(_10);
         StorageLive(_9);
-        _9 = _4 as *mut T (PtrToPtr);
-        _10 = Offset(_9, _3);
+        StorageLive(_8);
+        _8 = _4 as *mut T (PtrToPtr);
+        _9 = Offset(_8, _3);
+        StorageDead(_8);
+        _10 = move _9 as *const T (PointerCoercion(MutToConstPointer));
         StorageDead(_9);
-        _11 = move _10 as *const T (PointerCoercion(MutToConstPointer));
-        StorageDead(_10);
         goto -> bb3;
     }
 
     bb2: {
-        _11 = _3 as *const T (Transmute);
+        _10 = _3 as *const T (Transmute);
         goto -> bb3;
     }
 
     bb3: {
-        StorageDead(_8);
-        StorageLive(_12);
-        _12 = _11;
-        _13 = std::slice::Iter::<'_, T> { ptr: _7, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
-        StorageDead(_12);
-        StorageDead(_11);
-        StorageDead(_6);
-        StorageDead(_4);
         StorageDead(_7);
+        StorageLive(_11);
+        _11 = _10;
+        _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> };
+        StorageDead(_11);
+        StorageDead(_10);
+        StorageDead(_5);
+        StorageDead(_4);
+        StorageDead(_6);
         StorageDead(_3);
-        _14 = Enumerate::<std::slice::Iter<'_, T>> { iter: _13, count: const 0_usize };
-        StorageDead(_13);
-        StorageLive(_15);
-        _15 = _14;
+        _13 = Enumerate::<std::slice::Iter<'_, T>> { iter: _12, count: const 0_usize };
+        StorageDead(_12);
+        StorageLive(_14);
+        _14 = _13;
         goto -> bb4;
     }
 
     bb4: {
-        StorageLive(_17);
         StorageLive(_16);
-        _16 = &mut _15;
-        _17 = <Enumerate<std::slice::Iter<'_, T>> as Iterator>::next(move _16) -> [return: bb5, unwind: bb11];
+        StorageLive(_15);
+        _15 = &mut _14;
+        _16 = <Enumerate<std::slice::Iter<'_, T>> as Iterator>::next(move _15) -> [return: bb5, unwind: bb11];
     }
 
     bb5: {
-        StorageDead(_16);
-        _18 = discriminant(_17);
-        switchInt(move _18) -> [0: bb6, 1: bb8, otherwise: bb10];
+        StorageDead(_15);
+        _17 = discriminant(_16);
+        switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10];
     }
 
     bb6: {
-        StorageDead(_17);
-        StorageDead(_15);
+        StorageDead(_16);
+        StorageDead(_14);
         drop(_2) -> [return: bb7, unwind continue];
     }
 
@@ -153,19 +134,19 @@
     }
 
     bb8: {
-        _19 = (((_17 as Some).0: (usize, &T)).0: usize);
-        _20 = (((_17 as Some).0: (usize, &T)).1: &T);
+        _18 = (((_16 as Some).0: (usize, &T)).0: usize);
+        _19 = (((_16 as Some).0: (usize, &T)).1: &T);
+        StorageLive(_20);
+        _20 = &_2;
         StorageLive(_21);
-        _21 = &_2;
-        StorageLive(_22);
-        _22 = (_19, _20);
-        _23 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _21, move _22) -> [return: bb9, unwind: bb11];
+        _21 = (_18, _19);
+        _22 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _20, move _21) -> [return: bb9, unwind: bb11];
     }
 
     bb9: {
-        StorageDead(_22);
         StorageDead(_21);
-        StorageDead(_17);
+        StorageDead(_20);
+        StorageDead(_16);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir
index d8252e7..67dd0c8 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir
@@ -4,135 +4,118 @@
     debug slice => _1;
     debug f => _2;
     let mut _0: ();
+    let mut _12: std::slice::Iter<'_, T>;
     let mut _13: std::slice::Iter<'_, T>;
-    let mut _14: std::slice::Iter<'_, T>;
-    let mut _15: &mut std::slice::Iter<'_, T>;
-    let mut _16: std::option::Option<&T>;
-    let mut _17: isize;
-    let mut _19: &impl Fn(&T);
-    let mut _20: (&T,);
-    let _21: ();
+    let mut _14: &mut std::slice::Iter<'_, T>;
+    let mut _15: std::option::Option<&T>;
+    let mut _16: isize;
+    let mut _18: &impl Fn(&T);
+    let mut _19: (&T,);
+    let _20: ();
     scope 1 {
-        debug iter => _14;
-        let _18: &T;
+        debug iter => _13;
+        let _17: &T;
         scope 2 {
-            debug x => _18;
+            debug x => _17;
         }
     }
     scope 3 (inlined core::slice::<impl [T]>::iter) {
-        debug self => _1;
         scope 4 (inlined std::slice::Iter::<'_, T>::new) {
-            debug slice => _1;
             let _3: usize;
-            let mut _5: std::ptr::NonNull<[T]>;
-            let mut _8: bool;
+            let mut _7: bool;
+            let mut _8: *mut T;
             let mut _9: *mut T;
-            let mut _10: *mut T;
-            let mut _12: *const T;
+            let mut _11: *const T;
             scope 5 {
-                debug len => _3;
-                let _7: std::ptr::NonNull<T>;
+                let _6: std::ptr::NonNull<T>;
                 scope 6 {
-                    debug ptr => _7;
-                    let _11: *const T;
+                    let _10: *const T;
                     scope 7 {
-                        debug end_or_len => _11;
                     }
                     scope 11 (inlined without_provenance::<T>) {
-                        debug addr => _3;
                     }
                     scope 12 (inlined NonNull::<T>::as_ptr) {
-                        debug self => _7;
                     }
                     scope 13 (inlined std::ptr::mut_ptr::<impl *mut T>::add) {
-                        debug self => _9;
-                        debug count => _3;
                     }
                 }
                 scope 8 (inlined <NonNull<[T]> as From<&[T]>>::from) {
-                    debug reference => _1;
                     let mut _4: *const [T];
                 }
                 scope 9 (inlined NonNull::<[T]>::cast::<T>) {
-                    debug self => _5;
-                    let mut _6: *const T;
+                    let mut _5: *const T;
                     scope 10 (inlined NonNull::<[T]>::as_ptr) {
-                        debug self => _5;
                     }
                 }
             }
         }
     }
     scope 14 (inlined <std::slice::Iter<'_, T> as IntoIterator>::into_iter) {
-        debug self => _13;
     }
 
     bb0: {
         StorageLive(_3);
-        StorageLive(_7);
-        StorageLive(_4);
         StorageLive(_6);
-        _3 = Len((*_1));
+        StorageLive(_4);
         StorageLive(_5);
+        _3 = Len((*_1));
         _4 = &raw const (*_1);
-        _5 = NonNull::<[T]> { pointer: _4 };
-        _6 = _4 as *const T (PtrToPtr);
-        _7 = NonNull::<T> { pointer: _6 };
-        StorageDead(_5);
-        StorageLive(_11);
-        StorageLive(_8);
-        _8 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
-        switchInt(move _8) -> [0: bb1, otherwise: bb2];
+        _5 = _4 as *const T (PtrToPtr);
+        _6 = NonNull::<T> { pointer: _5 };
+        StorageLive(_10);
+        StorageLive(_7);
+        _7 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
+        switchInt(move _7) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageLive(_10);
         StorageLive(_9);
-        _9 = _4 as *mut T (PtrToPtr);
-        _10 = Offset(_9, _3);
+        StorageLive(_8);
+        _8 = _4 as *mut T (PtrToPtr);
+        _9 = Offset(_8, _3);
+        StorageDead(_8);
+        _10 = move _9 as *const T (PointerCoercion(MutToConstPointer));
         StorageDead(_9);
-        _11 = move _10 as *const T (PointerCoercion(MutToConstPointer));
-        StorageDead(_10);
         goto -> bb3;
     }
 
     bb2: {
-        _11 = _3 as *const T (Transmute);
+        _10 = _3 as *const T (Transmute);
         goto -> bb3;
     }
 
     bb3: {
-        StorageDead(_8);
-        StorageLive(_12);
-        _12 = _11;
-        _13 = std::slice::Iter::<'_, T> { ptr: _7, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
-        StorageDead(_12);
-        StorageDead(_11);
-        StorageDead(_6);
-        StorageDead(_4);
         StorageDead(_7);
+        StorageLive(_11);
+        _11 = _10;
+        _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> };
+        StorageDead(_11);
+        StorageDead(_10);
+        StorageDead(_5);
+        StorageDead(_4);
+        StorageDead(_6);
         StorageDead(_3);
-        StorageLive(_14);
-        _14 = _13;
+        StorageLive(_13);
+        _13 = _12;
         goto -> bb4;
     }
 
     bb4: {
-        StorageLive(_16);
         StorageLive(_15);
-        _15 = &mut _14;
-        _16 = <std::slice::Iter<'_, T> as Iterator>::next(move _15) -> [return: bb5, unwind unreachable];
+        StorageLive(_14);
+        _14 = &mut _13;
+        _15 = <std::slice::Iter<'_, T> as Iterator>::next(move _14) -> [return: bb5, unwind unreachable];
     }
 
     bb5: {
-        StorageDead(_15);
-        _17 = discriminant(_16);
-        switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10];
+        StorageDead(_14);
+        _16 = discriminant(_15);
+        switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10];
     }
 
     bb6: {
-        StorageDead(_16);
-        StorageDead(_14);
+        StorageDead(_15);
+        StorageDead(_13);
         drop(_2) -> [return: bb7, unwind unreachable];
     }
 
@@ -141,18 +124,18 @@
     }
 
     bb8: {
-        _18 = ((_16 as Some).0: &T);
+        _17 = ((_15 as Some).0: &T);
+        StorageLive(_18);
+        _18 = &_2;
         StorageLive(_19);
-        _19 = &_2;
-        StorageLive(_20);
-        _20 = (_18,);
-        _21 = <impl Fn(&T) as Fn<(&T,)>>::call(move _19, move _20) -> [return: bb9, unwind unreachable];
+        _19 = (_17,);
+        _20 = <impl Fn(&T) as Fn<(&T,)>>::call(move _18, move _19) -> [return: bb9, unwind unreachable];
     }
 
     bb9: {
-        StorageDead(_20);
         StorageDead(_19);
-        StorageDead(_16);
+        StorageDead(_18);
+        StorageDead(_15);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir
index b3904dc..7c41e9e 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir
@@ -4,135 +4,118 @@
     debug slice => _1;
     debug f => _2;
     let mut _0: ();
+    let mut _12: std::slice::Iter<'_, T>;
     let mut _13: std::slice::Iter<'_, T>;
-    let mut _14: std::slice::Iter<'_, T>;
-    let mut _15: &mut std::slice::Iter<'_, T>;
-    let mut _16: std::option::Option<&T>;
-    let mut _17: isize;
-    let mut _19: &impl Fn(&T);
-    let mut _20: (&T,);
-    let _21: ();
+    let mut _14: &mut std::slice::Iter<'_, T>;
+    let mut _15: std::option::Option<&T>;
+    let mut _16: isize;
+    let mut _18: &impl Fn(&T);
+    let mut _19: (&T,);
+    let _20: ();
     scope 1 {
-        debug iter => _14;
-        let _18: &T;
+        debug iter => _13;
+        let _17: &T;
         scope 2 {
-            debug x => _18;
+            debug x => _17;
         }
     }
     scope 3 (inlined core::slice::<impl [T]>::iter) {
-        debug self => _1;
         scope 4 (inlined std::slice::Iter::<'_, T>::new) {
-            debug slice => _1;
             let _3: usize;
-            let mut _5: std::ptr::NonNull<[T]>;
-            let mut _8: bool;
+            let mut _7: bool;
+            let mut _8: *mut T;
             let mut _9: *mut T;
-            let mut _10: *mut T;
-            let mut _12: *const T;
+            let mut _11: *const T;
             scope 5 {
-                debug len => _3;
-                let _7: std::ptr::NonNull<T>;
+                let _6: std::ptr::NonNull<T>;
                 scope 6 {
-                    debug ptr => _7;
-                    let _11: *const T;
+                    let _10: *const T;
                     scope 7 {
-                        debug end_or_len => _11;
                     }
                     scope 11 (inlined without_provenance::<T>) {
-                        debug addr => _3;
                     }
                     scope 12 (inlined NonNull::<T>::as_ptr) {
-                        debug self => _7;
                     }
                     scope 13 (inlined std::ptr::mut_ptr::<impl *mut T>::add) {
-                        debug self => _9;
-                        debug count => _3;
                     }
                 }
                 scope 8 (inlined <NonNull<[T]> as From<&[T]>>::from) {
-                    debug reference => _1;
                     let mut _4: *const [T];
                 }
                 scope 9 (inlined NonNull::<[T]>::cast::<T>) {
-                    debug self => _5;
-                    let mut _6: *const T;
+                    let mut _5: *const T;
                     scope 10 (inlined NonNull::<[T]>::as_ptr) {
-                        debug self => _5;
                     }
                 }
             }
         }
     }
     scope 14 (inlined <std::slice::Iter<'_, T> as IntoIterator>::into_iter) {
-        debug self => _13;
     }
 
     bb0: {
         StorageLive(_3);
-        StorageLive(_7);
-        StorageLive(_4);
         StorageLive(_6);
-        _3 = Len((*_1));
+        StorageLive(_4);
         StorageLive(_5);
+        _3 = Len((*_1));
         _4 = &raw const (*_1);
-        _5 = NonNull::<[T]> { pointer: _4 };
-        _6 = _4 as *const T (PtrToPtr);
-        _7 = NonNull::<T> { pointer: _6 };
-        StorageDead(_5);
-        StorageLive(_11);
-        StorageLive(_8);
-        _8 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
-        switchInt(move _8) -> [0: bb1, otherwise: bb2];
+        _5 = _4 as *const T (PtrToPtr);
+        _6 = NonNull::<T> { pointer: _5 };
+        StorageLive(_10);
+        StorageLive(_7);
+        _7 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
+        switchInt(move _7) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageLive(_10);
         StorageLive(_9);
-        _9 = _4 as *mut T (PtrToPtr);
-        _10 = Offset(_9, _3);
+        StorageLive(_8);
+        _8 = _4 as *mut T (PtrToPtr);
+        _9 = Offset(_8, _3);
+        StorageDead(_8);
+        _10 = move _9 as *const T (PointerCoercion(MutToConstPointer));
         StorageDead(_9);
-        _11 = move _10 as *const T (PointerCoercion(MutToConstPointer));
-        StorageDead(_10);
         goto -> bb3;
     }
 
     bb2: {
-        _11 = _3 as *const T (Transmute);
+        _10 = _3 as *const T (Transmute);
         goto -> bb3;
     }
 
     bb3: {
-        StorageDead(_8);
-        StorageLive(_12);
-        _12 = _11;
-        _13 = std::slice::Iter::<'_, T> { ptr: _7, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
-        StorageDead(_12);
-        StorageDead(_11);
-        StorageDead(_6);
-        StorageDead(_4);
         StorageDead(_7);
+        StorageLive(_11);
+        _11 = _10;
+        _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> };
+        StorageDead(_11);
+        StorageDead(_10);
+        StorageDead(_5);
+        StorageDead(_4);
+        StorageDead(_6);
         StorageDead(_3);
-        StorageLive(_14);
-        _14 = _13;
+        StorageLive(_13);
+        _13 = _12;
         goto -> bb4;
     }
 
     bb4: {
-        StorageLive(_16);
         StorageLive(_15);
-        _15 = &mut _14;
-        _16 = <std::slice::Iter<'_, T> as Iterator>::next(move _15) -> [return: bb5, unwind: bb11];
+        StorageLive(_14);
+        _14 = &mut _13;
+        _15 = <std::slice::Iter<'_, T> as Iterator>::next(move _14) -> [return: bb5, unwind: bb11];
     }
 
     bb5: {
-        StorageDead(_15);
-        _17 = discriminant(_16);
-        switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10];
+        StorageDead(_14);
+        _16 = discriminant(_15);
+        switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10];
     }
 
     bb6: {
-        StorageDead(_16);
-        StorageDead(_14);
+        StorageDead(_15);
+        StorageDead(_13);
         drop(_2) -> [return: bb7, unwind continue];
     }
 
@@ -141,18 +124,18 @@
     }
 
     bb8: {
-        _18 = ((_16 as Some).0: &T);
+        _17 = ((_15 as Some).0: &T);
+        StorageLive(_18);
+        _18 = &_2;
         StorageLive(_19);
-        _19 = &_2;
-        StorageLive(_20);
-        _20 = (_18,);
-        _21 = <impl Fn(&T) as Fn<(&T,)>>::call(move _19, move _20) -> [return: bb9, unwind: bb11];
+        _19 = (_17,);
+        _20 = <impl Fn(&T) as Fn<(&T,)>>::call(move _18, move _19) -> [return: bb9, unwind: bb11];
     }
 
     bb9: {
-        StorageDead(_20);
         StorageDead(_19);
-        StorageDead(_16);
+        StorageDead(_18);
+        StorageDead(_15);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir
index 5ab88c9..dbe6f39 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir
@@ -5,86 +5,63 @@
     debug f => _2;
     let mut _0: ();
     let mut _3: usize;
-    let mut _4: std::ops::Range<usize>;
-    let mut _5: std::ops::Range<usize>;
-    let mut _6: &mut std::ops::Range<usize>;
-    let mut _14: std::option::Option<usize>;
-    let mut _16: usize;
-    let mut _17: bool;
-    let mut _19: &impl Fn(usize, &T);
-    let mut _20: (usize, &T);
-    let _21: ();
+    let mut _4: usize;
+    let mut _9: std::option::Option<usize>;
+    let mut _11: usize;
+    let mut _12: bool;
+    let mut _14: &impl Fn(usize, &T);
+    let mut _15: (usize, &T);
+    let _16: ();
     scope 1 {
-        debug iter => _5;
-        let _15: usize;
+        debug ((iter: std::ops::Range<usize>).0: usize) => _4;
+        debug ((iter: std::ops::Range<usize>).1: usize) => _3;
+        let _10: usize;
         scope 2 {
-            debug i => _15;
-            let _18: &T;
+            debug i => _10;
+            let _13: &T;
             scope 3 {
-                debug x => _18;
+                debug x => _13;
             }
         }
         scope 5 (inlined iter::range::<impl Iterator for std::ops::Range<usize>>::next) {
-            debug self => _6;
             scope 6 (inlined <std::ops::Range<usize> as iter::range::RangeIteratorImpl>::spec_next) {
-                debug self => _6;
-                let mut _7: &usize;
-                let mut _8: &usize;
-                let mut _11: bool;
-                let _12: usize;
-                let mut _13: usize;
+                let mut _6: bool;
+                let _7: usize;
+                let mut _8: usize;
                 scope 7 {
-                    debug old => _12;
                 }
                 scope 8 (inlined std::cmp::impls::<impl PartialOrd for usize>::lt) {
-                    debug self => _7;
-                    debug other => _8;
-                    let mut _9: usize;
-                    let mut _10: usize;
+                    let mut _5: usize;
                 }
             }
         }
     }
     scope 4 (inlined <std::ops::Range<usize> as IntoIterator>::into_iter) {
-        debug self => _4;
     }
 
     bb0: {
-        StorageLive(_3);
         _3 = Len((*_1));
-        _4 = std::ops::Range::<usize> { start: const 0_usize, end: move _3 };
-        StorageDead(_3);
-        StorageLive(_5);
-        _5 = _4;
+        StorageLive(_4);
+        _4 = const 0_usize;
         goto -> bb1;
     }
 
     bb1: {
-        StorageLive(_14);
-        _6 = &mut _5;
-        StorageLive(_12);
-        StorageLive(_11);
-        StorageLive(_7);
-        _7 = &(_5.0: usize);
-        StorageLive(_8);
-        _8 = &(_5.1: usize);
         StorageLive(_9);
-        _9 = (_5.0: usize);
-        StorageLive(_10);
-        _10 = (_5.1: usize);
-        _11 = Lt(move _9, move _10);
-        StorageDead(_10);
-        StorageDead(_9);
-        switchInt(move _11) -> [0: bb2, otherwise: bb4];
+        StorageLive(_7);
+        StorageLive(_6);
+        StorageLive(_5);
+        _5 = _4;
+        _6 = Lt(move _5, _3);
+        StorageDead(_5);
+        switchInt(move _6) -> [0: bb2, otherwise: bb4];
     }
 
     bb2: {
-        StorageDead(_8);
+        StorageDead(_6);
         StorageDead(_7);
-        StorageDead(_11);
-        StorageDead(_12);
-        StorageDead(_14);
-        StorageDead(_5);
+        StorageDead(_9);
+        StorageDead(_4);
         drop(_2) -> [return: bb3, unwind unreachable];
     }
 
@@ -93,38 +70,36 @@
     }
 
     bb4: {
-        StorageDead(_8);
-        StorageDead(_7);
-        _12 = (_5.0: usize);
-        StorageLive(_13);
-        _13 = <usize as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb5, unwind unreachable];
+        _7 = _4;
+        StorageLive(_8);
+        _8 = <usize as Step>::forward_unchecked(_7, const 1_usize) -> [return: bb5, unwind unreachable];
     }
 
     bb5: {
-        (_5.0: usize) = move _13;
-        StorageDead(_13);
-        _14 = Option::<usize>::Some(_12);
-        StorageDead(_11);
-        StorageDead(_12);
-        _15 = ((_14 as Some).0: usize);
-        _16 = Len((*_1));
-        _17 = Lt(_15, _16);
-        assert(move _17, "index out of bounds: the length is {} but the index is {}", move _16, _15) -> [success: bb6, unwind unreachable];
+        _4 = move _8;
+        StorageDead(_8);
+        _9 = Option::<usize>::Some(_7);
+        StorageDead(_6);
+        StorageDead(_7);
+        _10 = ((_9 as Some).0: usize);
+        _11 = Len((*_1));
+        _12 = Lt(_10, _11);
+        assert(move _12, "index out of bounds: the length is {} but the index is {}", move _11, _10) -> [success: bb6, unwind unreachable];
     }
 
     bb6: {
-        _18 = &(*_1)[_15];
-        StorageLive(_19);
-        _19 = &_2;
-        StorageLive(_20);
-        _20 = (_15, _18);
-        _21 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _19, move _20) -> [return: bb7, unwind unreachable];
+        _13 = &(*_1)[_10];
+        StorageLive(_14);
+        _14 = &_2;
+        StorageLive(_15);
+        _15 = (_10, _13);
+        _16 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _14, move _15) -> [return: bb7, unwind unreachable];
     }
 
     bb7: {
-        StorageDead(_20);
-        StorageDead(_19);
+        StorageDead(_15);
         StorageDead(_14);
+        StorageDead(_9);
         goto -> bb1;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir
index 5136510..5b6441c 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir
@@ -5,86 +5,63 @@
     debug f => _2;
     let mut _0: ();
     let mut _3: usize;
-    let mut _4: std::ops::Range<usize>;
-    let mut _5: std::ops::Range<usize>;
-    let mut _6: &mut std::ops::Range<usize>;
-    let mut _14: std::option::Option<usize>;
-    let mut _16: usize;
-    let mut _17: bool;
-    let mut _19: &impl Fn(usize, &T);
-    let mut _20: (usize, &T);
-    let _21: ();
+    let mut _4: usize;
+    let mut _9: std::option::Option<usize>;
+    let mut _11: usize;
+    let mut _12: bool;
+    let mut _14: &impl Fn(usize, &T);
+    let mut _15: (usize, &T);
+    let _16: ();
     scope 1 {
-        debug iter => _5;
-        let _15: usize;
+        debug ((iter: std::ops::Range<usize>).0: usize) => _4;
+        debug ((iter: std::ops::Range<usize>).1: usize) => _3;
+        let _10: usize;
         scope 2 {
-            debug i => _15;
-            let _18: &T;
+            debug i => _10;
+            let _13: &T;
             scope 3 {
-                debug x => _18;
+                debug x => _13;
             }
         }
         scope 5 (inlined iter::range::<impl Iterator for std::ops::Range<usize>>::next) {
-            debug self => _6;
             scope 6 (inlined <std::ops::Range<usize> as iter::range::RangeIteratorImpl>::spec_next) {
-                debug self => _6;
-                let mut _7: &usize;
-                let mut _8: &usize;
-                let mut _11: bool;
-                let _12: usize;
-                let mut _13: usize;
+                let mut _6: bool;
+                let _7: usize;
+                let mut _8: usize;
                 scope 7 {
-                    debug old => _12;
                 }
                 scope 8 (inlined std::cmp::impls::<impl PartialOrd for usize>::lt) {
-                    debug self => _7;
-                    debug other => _8;
-                    let mut _9: usize;
-                    let mut _10: usize;
+                    let mut _5: usize;
                 }
             }
         }
     }
     scope 4 (inlined <std::ops::Range<usize> as IntoIterator>::into_iter) {
-        debug self => _4;
     }
 
     bb0: {
-        StorageLive(_3);
         _3 = Len((*_1));
-        _4 = std::ops::Range::<usize> { start: const 0_usize, end: move _3 };
-        StorageDead(_3);
-        StorageLive(_5);
-        _5 = _4;
+        StorageLive(_4);
+        _4 = const 0_usize;
         goto -> bb1;
     }
 
     bb1: {
-        StorageLive(_14);
-        _6 = &mut _5;
-        StorageLive(_12);
-        StorageLive(_11);
-        StorageLive(_7);
-        _7 = &(_5.0: usize);
-        StorageLive(_8);
-        _8 = &(_5.1: usize);
         StorageLive(_9);
-        _9 = (_5.0: usize);
-        StorageLive(_10);
-        _10 = (_5.1: usize);
-        _11 = Lt(move _9, move _10);
-        StorageDead(_10);
-        StorageDead(_9);
-        switchInt(move _11) -> [0: bb2, otherwise: bb4];
+        StorageLive(_7);
+        StorageLive(_6);
+        StorageLive(_5);
+        _5 = _4;
+        _6 = Lt(move _5, _3);
+        StorageDead(_5);
+        switchInt(move _6) -> [0: bb2, otherwise: bb4];
     }
 
     bb2: {
-        StorageDead(_8);
+        StorageDead(_6);
         StorageDead(_7);
-        StorageDead(_11);
-        StorageDead(_12);
-        StorageDead(_14);
-        StorageDead(_5);
+        StorageDead(_9);
+        StorageDead(_4);
         drop(_2) -> [return: bb3, unwind continue];
     }
 
@@ -93,38 +70,36 @@
     }
 
     bb4: {
-        StorageDead(_8);
-        StorageDead(_7);
-        _12 = (_5.0: usize);
-        StorageLive(_13);
-        _13 = <usize as Step>::forward_unchecked(_12, const 1_usize) -> [return: bb5, unwind: bb8];
+        _7 = _4;
+        StorageLive(_8);
+        _8 = <usize as Step>::forward_unchecked(_7, const 1_usize) -> [return: bb5, unwind: bb8];
     }
 
     bb5: {
-        (_5.0: usize) = move _13;
-        StorageDead(_13);
-        _14 = Option::<usize>::Some(_12);
-        StorageDead(_11);
-        StorageDead(_12);
-        _15 = ((_14 as Some).0: usize);
-        _16 = Len((*_1));
-        _17 = Lt(_15, _16);
-        assert(move _17, "index out of bounds: the length is {} but the index is {}", move _16, _15) -> [success: bb6, unwind: bb8];
+        _4 = move _8;
+        StorageDead(_8);
+        _9 = Option::<usize>::Some(_7);
+        StorageDead(_6);
+        StorageDead(_7);
+        _10 = ((_9 as Some).0: usize);
+        _11 = Len((*_1));
+        _12 = Lt(_10, _11);
+        assert(move _12, "index out of bounds: the length is {} but the index is {}", move _11, _10) -> [success: bb6, unwind: bb8];
     }
 
     bb6: {
-        _18 = &(*_1)[_15];
-        StorageLive(_19);
-        _19 = &_2;
-        StorageLive(_20);
-        _20 = (_15, _18);
-        _21 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _19, move _20) -> [return: bb7, unwind: bb8];
+        _13 = &(*_1)[_10];
+        StorageLive(_14);
+        _14 = &_2;
+        StorageLive(_15);
+        _15 = (_10, _13);
+        _16 = <impl Fn(usize, &T) as Fn<(usize, &T)>>::call(move _14, move _15) -> [return: bb7, unwind: bb8];
     }
 
     bb7: {
-        StorageDead(_20);
-        StorageDead(_19);
+        StorageDead(_15);
         StorageDead(_14);
+        StorageDead(_9);
         goto -> bb1;
     }
 
diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
index 091c8a0..ffeef1e 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
@@ -4,150 +4,128 @@
     debug slice => _1;
     debug f => _2;
     let mut _0: ();
-    let mut _13: std::slice::Iter<'_, T>;
+    let mut _12: std::slice::Iter<'_, T>;
+    let mut _13: std::iter::Rev<std::slice::Iter<'_, T>>;
     let mut _14: std::iter::Rev<std::slice::Iter<'_, T>>;
-    let mut _15: std::iter::Rev<std::slice::Iter<'_, T>>;
-    let mut _16: &mut std::iter::Rev<std::slice::Iter<'_, T>>;
-    let mut _18: std::option::Option<&T>;
-    let mut _19: isize;
-    let mut _21: &impl Fn(&T);
-    let mut _22: (&T,);
-    let _23: ();
+    let mut _16: std::option::Option<&T>;
+    let mut _17: isize;
+    let mut _19: &impl Fn(&T);
+    let mut _20: (&T,);
+    let _21: ();
     scope 1 {
-        debug iter => _15;
-        let _20: &T;
+        debug iter => _14;
+        let _18: &T;
         scope 2 {
-            debug x => _20;
+            debug x => _18;
         }
         scope 17 (inlined <Rev<std::slice::Iter<'_, T>> as Iterator>::next) {
-            debug self => _16;
-            let mut _17: &mut std::slice::Iter<'_, T>;
+            let mut _15: &mut std::slice::Iter<'_, T>;
         }
     }
     scope 3 (inlined core::slice::<impl [T]>::iter) {
-        debug self => _1;
         scope 4 (inlined std::slice::Iter::<'_, T>::new) {
-            debug slice => _1;
             let _3: usize;
-            let mut _5: std::ptr::NonNull<[T]>;
-            let mut _8: bool;
+            let mut _7: bool;
+            let mut _8: *mut T;
             let mut _9: *mut T;
-            let mut _10: *mut T;
-            let mut _12: *const T;
+            let mut _11: *const T;
             scope 5 {
-                debug len => _3;
-                let _7: std::ptr::NonNull<T>;
+                let _6: std::ptr::NonNull<T>;
                 scope 6 {
-                    debug ptr => _7;
-                    let _11: *const T;
+                    let _10: *const T;
                     scope 7 {
-                        debug end_or_len => _11;
                     }
                     scope 11 (inlined without_provenance::<T>) {
-                        debug addr => _3;
                     }
                     scope 12 (inlined NonNull::<T>::as_ptr) {
-                        debug self => _7;
                     }
                     scope 13 (inlined std::ptr::mut_ptr::<impl *mut T>::add) {
-                        debug self => _9;
-                        debug count => _3;
                     }
                 }
                 scope 8 (inlined <NonNull<[T]> as From<&[T]>>::from) {
-                    debug reference => _1;
                     let mut _4: *const [T];
                 }
                 scope 9 (inlined NonNull::<[T]>::cast::<T>) {
-                    debug self => _5;
-                    let mut _6: *const T;
+                    let mut _5: *const T;
                     scope 10 (inlined NonNull::<[T]>::as_ptr) {
-                        debug self => _5;
                     }
                 }
             }
         }
     }
     scope 14 (inlined <std::slice::Iter<'_, T> as Iterator>::rev) {
-        debug self => _13;
         scope 15 (inlined Rev::<std::slice::Iter<'_, T>>::new) {
-            debug iter => _13;
         }
     }
     scope 16 (inlined <Rev<std::slice::Iter<'_, T>> as IntoIterator>::into_iter) {
-        debug self => _14;
     }
 
     bb0: {
-        StorageLive(_13);
+        StorageLive(_12);
         StorageLive(_3);
-        StorageLive(_7);
-        StorageLive(_4);
         StorageLive(_6);
-        _3 = Len((*_1));
+        StorageLive(_4);
         StorageLive(_5);
+        _3 = Len((*_1));
         _4 = &raw const (*_1);
-        _5 = NonNull::<[T]> { pointer: _4 };
-        _6 = _4 as *const T (PtrToPtr);
-        _7 = NonNull::<T> { pointer: _6 };
-        StorageDead(_5);
-        StorageLive(_11);
-        StorageLive(_8);
-        _8 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
-        switchInt(move _8) -> [0: bb1, otherwise: bb2];
+        _5 = _4 as *const T (PtrToPtr);
+        _6 = NonNull::<T> { pointer: _5 };
+        StorageLive(_10);
+        StorageLive(_7);
+        _7 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
+        switchInt(move _7) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageLive(_10);
         StorageLive(_9);
-        _9 = _4 as *mut T (PtrToPtr);
-        _10 = Offset(_9, _3);
+        StorageLive(_8);
+        _8 = _4 as *mut T (PtrToPtr);
+        _9 = Offset(_8, _3);
+        StorageDead(_8);
+        _10 = move _9 as *const T (PointerCoercion(MutToConstPointer));
         StorageDead(_9);
-        _11 = move _10 as *const T (PointerCoercion(MutToConstPointer));
-        StorageDead(_10);
         goto -> bb3;
     }
 
     bb2: {
-        _11 = _3 as *const T (Transmute);
+        _10 = _3 as *const T (Transmute);
         goto -> bb3;
     }
 
     bb3: {
-        StorageDead(_8);
-        StorageLive(_12);
-        _12 = _11;
-        _13 = std::slice::Iter::<'_, T> { ptr: _7, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
-        StorageDead(_12);
-        StorageDead(_11);
-        StorageDead(_6);
-        StorageDead(_4);
         StorageDead(_7);
+        StorageLive(_11);
+        _11 = _10;
+        _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> };
+        StorageDead(_11);
+        StorageDead(_10);
+        StorageDead(_5);
+        StorageDead(_4);
+        StorageDead(_6);
         StorageDead(_3);
-        _14 = Rev::<std::slice::Iter<'_, T>> { iter: _13 };
-        StorageDead(_13);
-        StorageLive(_15);
-        _15 = _14;
+        _13 = Rev::<std::slice::Iter<'_, T>> { iter: _12 };
+        StorageDead(_12);
+        StorageLive(_14);
+        _14 = _13;
         goto -> bb4;
     }
 
     bb4: {
-        StorageLive(_18);
-        _16 = &mut _15;
-        StorageLive(_17);
-        _17 = &mut (_15.0: std::slice::Iter<'_, T>);
-        _18 = <std::slice::Iter<'_, T> as DoubleEndedIterator>::next_back(move _17) -> [return: bb5, unwind unreachable];
+        StorageLive(_16);
+        StorageLive(_15);
+        _15 = &mut (_14.0: std::slice::Iter<'_, T>);
+        _16 = <std::slice::Iter<'_, T> as DoubleEndedIterator>::next_back(move _15) -> [return: bb5, unwind unreachable];
     }
 
     bb5: {
-        StorageDead(_17);
-        _19 = discriminant(_18);
-        switchInt(move _19) -> [0: bb6, 1: bb8, otherwise: bb10];
+        StorageDead(_15);
+        _17 = discriminant(_16);
+        switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10];
     }
 
     bb6: {
-        StorageDead(_18);
-        StorageDead(_15);
+        StorageDead(_16);
+        StorageDead(_14);
         drop(_2) -> [return: bb7, unwind unreachable];
     }
 
@@ -156,18 +134,18 @@
     }
 
     bb8: {
-        _20 = ((_18 as Some).0: &T);
-        StorageLive(_21);
-        _21 = &_2;
-        StorageLive(_22);
-        _22 = (_20,);
-        _23 = <impl Fn(&T) as Fn<(&T,)>>::call(move _21, move _22) -> [return: bb9, unwind unreachable];
+        _18 = ((_16 as Some).0: &T);
+        StorageLive(_19);
+        _19 = &_2;
+        StorageLive(_20);
+        _20 = (_18,);
+        _21 = <impl Fn(&T) as Fn<(&T,)>>::call(move _19, move _20) -> [return: bb9, unwind unreachable];
     }
 
     bb9: {
-        StorageDead(_22);
-        StorageDead(_21);
-        StorageDead(_18);
+        StorageDead(_20);
+        StorageDead(_19);
+        StorageDead(_16);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
index 1873d45..c7cd37a 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
@@ -4,150 +4,128 @@
     debug slice => _1;
     debug f => _2;
     let mut _0: ();
-    let mut _13: std::slice::Iter<'_, T>;
+    let mut _12: std::slice::Iter<'_, T>;
+    let mut _13: std::iter::Rev<std::slice::Iter<'_, T>>;
     let mut _14: std::iter::Rev<std::slice::Iter<'_, T>>;
-    let mut _15: std::iter::Rev<std::slice::Iter<'_, T>>;
-    let mut _16: &mut std::iter::Rev<std::slice::Iter<'_, T>>;
-    let mut _18: std::option::Option<&T>;
-    let mut _19: isize;
-    let mut _21: &impl Fn(&T);
-    let mut _22: (&T,);
-    let _23: ();
+    let mut _16: std::option::Option<&T>;
+    let mut _17: isize;
+    let mut _19: &impl Fn(&T);
+    let mut _20: (&T,);
+    let _21: ();
     scope 1 {
-        debug iter => _15;
-        let _20: &T;
+        debug iter => _14;
+        let _18: &T;
         scope 2 {
-            debug x => _20;
+            debug x => _18;
         }
         scope 17 (inlined <Rev<std::slice::Iter<'_, T>> as Iterator>::next) {
-            debug self => _16;
-            let mut _17: &mut std::slice::Iter<'_, T>;
+            let mut _15: &mut std::slice::Iter<'_, T>;
         }
     }
     scope 3 (inlined core::slice::<impl [T]>::iter) {
-        debug self => _1;
         scope 4 (inlined std::slice::Iter::<'_, T>::new) {
-            debug slice => _1;
             let _3: usize;
-            let mut _5: std::ptr::NonNull<[T]>;
-            let mut _8: bool;
+            let mut _7: bool;
+            let mut _8: *mut T;
             let mut _9: *mut T;
-            let mut _10: *mut T;
-            let mut _12: *const T;
+            let mut _11: *const T;
             scope 5 {
-                debug len => _3;
-                let _7: std::ptr::NonNull<T>;
+                let _6: std::ptr::NonNull<T>;
                 scope 6 {
-                    debug ptr => _7;
-                    let _11: *const T;
+                    let _10: *const T;
                     scope 7 {
-                        debug end_or_len => _11;
                     }
                     scope 11 (inlined without_provenance::<T>) {
-                        debug addr => _3;
                     }
                     scope 12 (inlined NonNull::<T>::as_ptr) {
-                        debug self => _7;
                     }
                     scope 13 (inlined std::ptr::mut_ptr::<impl *mut T>::add) {
-                        debug self => _9;
-                        debug count => _3;
                     }
                 }
                 scope 8 (inlined <NonNull<[T]> as From<&[T]>>::from) {
-                    debug reference => _1;
                     let mut _4: *const [T];
                 }
                 scope 9 (inlined NonNull::<[T]>::cast::<T>) {
-                    debug self => _5;
-                    let mut _6: *const T;
+                    let mut _5: *const T;
                     scope 10 (inlined NonNull::<[T]>::as_ptr) {
-                        debug self => _5;
                     }
                 }
             }
         }
     }
     scope 14 (inlined <std::slice::Iter<'_, T> as Iterator>::rev) {
-        debug self => _13;
         scope 15 (inlined Rev::<std::slice::Iter<'_, T>>::new) {
-            debug iter => _13;
         }
     }
     scope 16 (inlined <Rev<std::slice::Iter<'_, T>> as IntoIterator>::into_iter) {
-        debug self => _14;
     }
 
     bb0: {
-        StorageLive(_13);
+        StorageLive(_12);
         StorageLive(_3);
-        StorageLive(_7);
-        StorageLive(_4);
         StorageLive(_6);
-        _3 = Len((*_1));
+        StorageLive(_4);
         StorageLive(_5);
+        _3 = Len((*_1));
         _4 = &raw const (*_1);
-        _5 = NonNull::<[T]> { pointer: _4 };
-        _6 = _4 as *const T (PtrToPtr);
-        _7 = NonNull::<T> { pointer: _6 };
-        StorageDead(_5);
-        StorageLive(_11);
-        StorageLive(_8);
-        _8 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
-        switchInt(move _8) -> [0: bb1, otherwise: bb2];
+        _5 = _4 as *const T (PtrToPtr);
+        _6 = NonNull::<T> { pointer: _5 };
+        StorageLive(_10);
+        StorageLive(_7);
+        _7 = const <T as std::mem::SizedTypeProperties>::IS_ZST;
+        switchInt(move _7) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
-        StorageLive(_10);
         StorageLive(_9);
-        _9 = _4 as *mut T (PtrToPtr);
-        _10 = Offset(_9, _3);
+        StorageLive(_8);
+        _8 = _4 as *mut T (PtrToPtr);
+        _9 = Offset(_8, _3);
+        StorageDead(_8);
+        _10 = move _9 as *const T (PointerCoercion(MutToConstPointer));
         StorageDead(_9);
-        _11 = move _10 as *const T (PointerCoercion(MutToConstPointer));
-        StorageDead(_10);
         goto -> bb3;
     }
 
     bb2: {
-        _11 = _3 as *const T (Transmute);
+        _10 = _3 as *const T (Transmute);
         goto -> bb3;
     }
 
     bb3: {
-        StorageDead(_8);
-        StorageLive(_12);
-        _12 = _11;
-        _13 = std::slice::Iter::<'_, T> { ptr: _7, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
-        StorageDead(_12);
-        StorageDead(_11);
-        StorageDead(_6);
-        StorageDead(_4);
         StorageDead(_7);
+        StorageLive(_11);
+        _11 = _10;
+        _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> };
+        StorageDead(_11);
+        StorageDead(_10);
+        StorageDead(_5);
+        StorageDead(_4);
+        StorageDead(_6);
         StorageDead(_3);
-        _14 = Rev::<std::slice::Iter<'_, T>> { iter: _13 };
-        StorageDead(_13);
-        StorageLive(_15);
-        _15 = _14;
+        _13 = Rev::<std::slice::Iter<'_, T>> { iter: _12 };
+        StorageDead(_12);
+        StorageLive(_14);
+        _14 = _13;
         goto -> bb4;
     }
 
     bb4: {
-        StorageLive(_18);
-        _16 = &mut _15;
-        StorageLive(_17);
-        _17 = &mut (_15.0: std::slice::Iter<'_, T>);
-        _18 = <std::slice::Iter<'_, T> as DoubleEndedIterator>::next_back(move _17) -> [return: bb5, unwind: bb11];
+        StorageLive(_16);
+        StorageLive(_15);
+        _15 = &mut (_14.0: std::slice::Iter<'_, T>);
+        _16 = <std::slice::Iter<'_, T> as DoubleEndedIterator>::next_back(move _15) -> [return: bb5, unwind: bb11];
     }
 
     bb5: {
-        StorageDead(_17);
-        _19 = discriminant(_18);
-        switchInt(move _19) -> [0: bb6, 1: bb8, otherwise: bb10];
+        StorageDead(_15);
+        _17 = discriminant(_16);
+        switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10];
     }
 
     bb6: {
-        StorageDead(_18);
-        StorageDead(_15);
+        StorageDead(_16);
+        StorageDead(_14);
         drop(_2) -> [return: bb7, unwind continue];
     }
 
@@ -156,18 +134,18 @@
     }
 
     bb8: {
-        _20 = ((_18 as Some).0: &T);
-        StorageLive(_21);
-        _21 = &_2;
-        StorageLive(_22);
-        _22 = (_20,);
-        _23 = <impl Fn(&T) as Fn<(&T,)>>::call(move _21, move _22) -> [return: bb9, unwind: bb11];
+        _18 = ((_16 as Some).0: &T);
+        StorageLive(_19);
+        _19 = &_2;
+        StorageLive(_20);
+        _20 = (_18,);
+        _21 = <impl Fn(&T) as Fn<(&T,)>>::call(move _19, move _20) -> [return: bb9, unwind: bb11];
     }
 
     bb9: {
-        StorageDead(_22);
-        StorageDead(_21);
-        StorageDead(_18);
+        StorageDead(_20);
+        StorageDead(_19);
+        StorageDead(_16);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/spans.rs b/tests/mir-opt/pre-codegen/spans.rs
index 4d3dc7e..940089d 100644
--- a/tests/mir-opt/pre-codegen/spans.rs
+++ b/tests/mir-opt/pre-codegen/spans.rs
@@ -2,7 +2,7 @@
 // Test that the comments we emit in MIR opts are accurate.
 //
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ compile-flags: -Zmir-include-spans
+//@ compile-flags: -Zmir-include-spans -C debuginfo=full
 
 #![crate_type = "lib"]
 
diff --git a/tests/mir-opt/pre-codegen/vec_deref.rs b/tests/mir-opt/pre-codegen/vec_deref.rs
new file mode 100644
index 0000000..3476e17
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/vec_deref.rs
@@ -0,0 +1,13 @@
+// skip-filecheck
+//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=2
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+
+#![crate_type = "lib"]
+
+// Added after it stopped inlining in a nightly; see
+// <https://github.com/rust-lang/rust/issues/123174>
+
+// EMIT_MIR vec_deref.vec_deref_to_slice.PreCodegen.after.mir
+pub fn vec_deref_to_slice(v: &Vec<u8>) -> &[u8] {
+    v
+}
diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir
new file mode 100644
index 0000000..18728d5
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,72 @@
+// MIR for `vec_deref_to_slice` after PreCodegen
+
+fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
+    debug v => _1;
+    let mut _0: &[u8];
+    scope 1 (inlined <Vec<u8> as Deref>::deref) {
+        debug self => _1;
+        let mut _4: *const u8;
+        let mut _5: usize;
+        scope 2 (inlined Vec::<u8>::as_ptr) {
+            debug self => _1;
+            let mut _2: &alloc::raw_vec::RawVec<u8>;
+            scope 3 (inlined alloc::raw_vec::RawVec::<u8>::ptr) {
+                debug self => _2;
+                let mut _3: std::ptr::NonNull<u8>;
+                scope 4 (inlined Unique::<u8>::as_ptr) {
+                    debug ((self: Unique<u8>).0: std::ptr::NonNull<u8>) => _3;
+                    debug ((self: Unique<u8>).1: std::marker::PhantomData<u8>) => const PhantomData::<u8>;
+                    scope 5 (inlined NonNull::<u8>::as_ptr) {
+                        debug self => _3;
+                    }
+                }
+            }
+        }
+        scope 6 (inlined std::slice::from_raw_parts::<'_, u8>) {
+            debug data => _4;
+            debug len => _5;
+            let _7: *const [u8];
+            scope 7 (inlined core::ub_checks::check_language_ub) {
+                scope 8 (inlined core::ub_checks::check_language_ub::runtime) {
+                }
+            }
+            scope 9 (inlined std::mem::size_of::<u8>) {
+            }
+            scope 10 (inlined align_of::<u8>) {
+            }
+            scope 11 (inlined slice_from_raw_parts::<u8>) {
+                debug data => _4;
+                debug len => _5;
+                let mut _6: *const ();
+                scope 12 (inlined std::ptr::const_ptr::<impl *const u8>::cast::<()>) {
+                    debug self => _4;
+                }
+                scope 13 (inlined std::ptr::from_raw_parts::<[u8]>) {
+                    debug data_pointer => _6;
+                    debug metadata => _5;
+                }
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_4);
+        StorageLive(_2);
+        _2 = &((*_1).0: alloc::raw_vec::RawVec<u8>);
+        StorageLive(_3);
+        _3 = ((((*_1).0: alloc::raw_vec::RawVec<u8>).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
+        _4 = (_3.0: *const u8);
+        StorageDead(_3);
+        StorageDead(_2);
+        StorageLive(_5);
+        _5 = ((*_1).1: usize);
+        StorageLive(_6);
+        _6 = _4 as *const () (PtrToPtr);
+        _7 = *const [u8] from (_6, _5);
+        StorageDead(_6);
+        StorageDead(_5);
+        StorageDead(_4);
+        _0 = &(*_7);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 0000000..18728d5
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,72 @@
+// MIR for `vec_deref_to_slice` after PreCodegen
+
+fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
+    debug v => _1;
+    let mut _0: &[u8];
+    scope 1 (inlined <Vec<u8> as Deref>::deref) {
+        debug self => _1;
+        let mut _4: *const u8;
+        let mut _5: usize;
+        scope 2 (inlined Vec::<u8>::as_ptr) {
+            debug self => _1;
+            let mut _2: &alloc::raw_vec::RawVec<u8>;
+            scope 3 (inlined alloc::raw_vec::RawVec::<u8>::ptr) {
+                debug self => _2;
+                let mut _3: std::ptr::NonNull<u8>;
+                scope 4 (inlined Unique::<u8>::as_ptr) {
+                    debug ((self: Unique<u8>).0: std::ptr::NonNull<u8>) => _3;
+                    debug ((self: Unique<u8>).1: std::marker::PhantomData<u8>) => const PhantomData::<u8>;
+                    scope 5 (inlined NonNull::<u8>::as_ptr) {
+                        debug self => _3;
+                    }
+                }
+            }
+        }
+        scope 6 (inlined std::slice::from_raw_parts::<'_, u8>) {
+            debug data => _4;
+            debug len => _5;
+            let _7: *const [u8];
+            scope 7 (inlined core::ub_checks::check_language_ub) {
+                scope 8 (inlined core::ub_checks::check_language_ub::runtime) {
+                }
+            }
+            scope 9 (inlined std::mem::size_of::<u8>) {
+            }
+            scope 10 (inlined align_of::<u8>) {
+            }
+            scope 11 (inlined slice_from_raw_parts::<u8>) {
+                debug data => _4;
+                debug len => _5;
+                let mut _6: *const ();
+                scope 12 (inlined std::ptr::const_ptr::<impl *const u8>::cast::<()>) {
+                    debug self => _4;
+                }
+                scope 13 (inlined std::ptr::from_raw_parts::<[u8]>) {
+                    debug data_pointer => _6;
+                    debug metadata => _5;
+                }
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_4);
+        StorageLive(_2);
+        _2 = &((*_1).0: alloc::raw_vec::RawVec<u8>);
+        StorageLive(_3);
+        _3 = ((((*_1).0: alloc::raw_vec::RawVec<u8>).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
+        _4 = (_3.0: *const u8);
+        StorageDead(_3);
+        StorageDead(_2);
+        StorageLive(_5);
+        _5 = ((*_1).1: usize);
+        StorageLive(_6);
+        _6 = _4 as *const () (PtrToPtr);
+        _7 = *const [u8] from (_6, _5);
+        StorageDead(_6);
+        StorageDead(_5);
+        StorageDead(_4);
+        _0 = &(*_7);
+        return;
+    }
+}
diff --git a/tests/mir-opt/reference_prop.rs b/tests/mir-opt/reference_prop.rs
index 70587df..2dda771 100644
--- a/tests/mir-opt/reference_prop.rs
+++ b/tests/mir-opt/reference_prop.rs
@@ -1,5 +1,5 @@
 //@ compile-flags: -Zlint-mir=no
-//@ unit-test: ReferencePropagation
+//@ test-mir-pass: ReferencePropagation
 //@ needs-unwind
 
 #![feature(raw_ref_op)]
diff --git a/tests/mir-opt/remove_storage_markers.rs b/tests/mir-opt/remove_storage_markers.rs
index c53c387..4a928b7 100644
--- a/tests/mir-opt/remove_storage_markers.rs
+++ b/tests/mir-opt/remove_storage_markers.rs
@@ -1,5 +1,5 @@
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: RemoveStorageMarkers
+//@ test-mir-pass: RemoveStorageMarkers
 
 // Checks that storage markers are removed at opt-level=0.
 //
diff --git a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-abort.diff b/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-abort.diff
index 22a1a88..0c73602 100644
--- a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-abort.diff
+++ b/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-abort.diff
@@ -7,7 +7,6 @@
       let _2: ();
       let mut _3: T;
       scope 1 (inlined std::mem::drop::<T>) {
-          debug _x => _3;
       }
   
       bb0: {
diff --git a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-unwind.diff b/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-unwind.diff
index 56070e6..59cce9f 100644
--- a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-unwind.diff
+++ b/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-unwind.diff
@@ -7,7 +7,6 @@
       let _2: ();
       let mut _3: T;
       scope 1 (inlined std::mem::drop::<T>) {
-          debug _x => _3;
       }
   
       bb0: {
diff --git a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-abort.diff b/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-abort.diff
index 105bedd..428b366 100644
--- a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-abort.diff
+++ b/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-abort.diff
@@ -7,7 +7,6 @@
       let _2: ();
       let mut _3: std::vec::Vec<bool>;
       scope 1 (inlined std::mem::drop::<Vec<bool>>) {
-          debug _x => _3;
       }
   
       bb0: {
diff --git a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-unwind.diff b/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-unwind.diff
index c9790dc..445c1f8 100644
--- a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-unwind.diff
+++ b/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-unwind.diff
@@ -7,7 +7,6 @@
       let _2: ();
       let mut _3: std::vec::Vec<bool>;
       scope 1 (inlined std::mem::drop::<Vec<bool>>) {
-          debug _x => _3;
       }
   
       bb0: {
diff --git a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-abort.diff b/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-abort.diff
index a48ac82..5afeb86 100644
--- a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-abort.diff
+++ b/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-abort.diff
@@ -7,7 +7,6 @@
       let _2: ();
       let mut _3: bool;
       scope 1 (inlined std::mem::drop::<bool>) {
-          debug _x => _3;
       }
   
       bb0: {
diff --git a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-unwind.diff b/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-unwind.diff
index a335e88..b9919dd 100644
--- a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-unwind.diff
+++ b/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-unwind.diff
@@ -7,7 +7,6 @@
       let _2: ();
       let mut _3: bool;
       scope 1 (inlined std::mem::drop::<bool>) {
-          debug _x => _3;
       }
   
       bb0: {
diff --git a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-abort.diff b/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-abort.diff
index 049b32f..b89432d 100644
--- a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-abort.diff
+++ b/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-abort.diff
@@ -7,7 +7,6 @@
       let _2: ();
       let mut _3: T;
       scope 1 (inlined std::mem::drop::<T>) {
-          debug _x => _3;
       }
   
       bb0: {
diff --git a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-unwind.diff b/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-unwind.diff
index 1f7a6f9..48d0260 100644
--- a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-unwind.diff
+++ b/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-unwind.diff
@@ -7,7 +7,6 @@
       let _2: ();
       let mut _3: T;
       scope 1 (inlined std::mem::drop::<T>) {
-          debug _x => _3;
       }
   
       bb0: {
diff --git a/tests/mir-opt/retag.rs b/tests/mir-opt/retag.rs
index 17b3c10..43d74aa 100644
--- a/tests/mir-opt/retag.rs
+++ b/tests/mir-opt/retag.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: AddRetag
+//@ test-mir-pass: AddRetag
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // ignore-tidy-linelength
 //@ compile-flags: -Z mir-emit-retag -Z mir-opt-level=0 -Z span_free_formats
diff --git a/tests/mir-opt/separate_const_switch.identity.JumpThreading.diff b/tests/mir-opt/separate_const_switch.identity.JumpThreading.diff
index d0abebf..8dd904c 100644
--- a/tests/mir-opt/separate_const_switch.identity.JumpThreading.diff
+++ b/tests/mir-opt/separate_const_switch.identity.JumpThreading.diff
@@ -5,54 +5,44 @@
       debug x => _1;
       let mut _0: std::result::Result<i32, i32>;
       let mut _2: std::ops::ControlFlow<std::result::Result<std::convert::Infallible, i32>, i32>;
-      let mut _3: std::result::Result<i32, i32>;
-      let mut _4: isize;
-      let _5: std::result::Result<std::convert::Infallible, i32>;
-      let mut _6: std::result::Result<std::convert::Infallible, i32>;
-      let _7: i32;
+      let mut _3: isize;
+      let _4: std::result::Result<std::convert::Infallible, i32>;
+      let _5: i32;
       scope 1 {
-          debug residual => _5;
+          debug residual => _4;
           scope 2 {
               scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) {
-                  debug residual => _6;
-                  let _12: i32;
+                  let _10: i32;
                   scope 9 {
-                      debug e => _12;
                       scope 10 (inlined <i32 as From<i32>>::from) {
-                          debug t => _12;
                       }
                   }
               }
           }
       }
       scope 3 {
-          debug val => _7;
+          debug val => _5;
           scope 4 {
           }
       }
       scope 5 (inlined <Result<i32, i32> as Try>::branch) {
-          debug self => _3;
-          let mut _8: isize;
-          let _9: i32;
-          let _10: i32;
-          let mut _11: std::result::Result<std::convert::Infallible, i32>;
+          let mut _6: isize;
+          let _7: i32;
+          let _8: i32;
+          let mut _9: std::result::Result<std::convert::Infallible, i32>;
           scope 6 {
-              debug v => _9;
           }
           scope 7 {
-              debug e => _10;
           }
       }
   
       bb0: {
           StorageLive(_2);
-          StorageLive(_3);
-          _3 = _1;
+          StorageLive(_6);
+          StorageLive(_7);
           StorageLive(_8);
-          StorageLive(_9);
-          StorageLive(_10);
-          _8 = discriminant(_1);
-          switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb1];
+          _6 = discriminant(_1);
+          switchInt(move _6) -> [0: bb6, 1: bb5, otherwise: bb1];
       }
   
       bb1: {
@@ -60,55 +50,50 @@
       }
   
       bb2: {
-          _7 = ((_2 as Continue).0: i32);
-          _0 = Result::<i32, i32>::Ok(_7);
+          _5 = ((_2 as Continue).0: i32);
+          _0 = Result::<i32, i32>::Ok(_5);
           StorageDead(_2);
           return;
       }
   
       bb3: {
-          _5 = ((_2 as Break).0: std::result::Result<std::convert::Infallible, i32>);
-          StorageLive(_6);
-          _6 = _5;
-          _12 = ((_5 as Err).0: i32);
-          _0 = Result::<i32, i32>::Err(_12);
-          StorageDead(_6);
+          _4 = ((_2 as Break).0: std::result::Result<std::convert::Infallible, i32>);
+          _10 = ((_4 as Err).0: i32);
+          _0 = Result::<i32, i32>::Err(_10);
           StorageDead(_2);
           return;
       }
   
       bb4: {
-          StorageDead(_10);
-          StorageDead(_9);
           StorageDead(_8);
-          StorageDead(_3);
-          _4 = discriminant(_2);
--         switchInt(move _4) -> [0: bb2, 1: bb3, otherwise: bb1];
+          StorageDead(_7);
+          StorageDead(_6);
+          _3 = discriminant(_2);
+-         switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
 +         goto -> bb2;
       }
   
       bb5: {
-          _10 = ((_1 as Err).0: i32);
-          StorageLive(_11);
-          _11 = Result::<Infallible, i32>::Err(_10);
-          _2 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _11);
-          StorageDead(_11);
+          _8 = ((_1 as Err).0: i32);
+          StorageLive(_9);
+          _9 = Result::<Infallible, i32>::Err(_8);
+          _2 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _9);
+          StorageDead(_9);
 -         goto -> bb4;
 +         goto -> bb7;
       }
   
       bb6: {
-          _9 = ((_1 as Ok).0: i32);
-          _2 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(_9);
+          _7 = ((_1 as Ok).0: i32);
+          _2 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(_7);
           goto -> bb4;
 +     }
 + 
 +     bb7: {
-+         StorageDead(_10);
-+         StorageDead(_9);
 +         StorageDead(_8);
-+         StorageDead(_3);
-+         _4 = discriminant(_2);
++         StorageDead(_7);
++         StorageDead(_6);
++         _3 = discriminant(_2);
 +         goto -> bb3;
       }
   }
diff --git a/tests/mir-opt/set_no_discriminant.rs b/tests/mir-opt/set_no_discriminant.rs
index 995bd11..0c29d1f 100644
--- a/tests/mir-opt/set_no_discriminant.rs
+++ b/tests/mir-opt/set_no_discriminant.rs
@@ -1,6 +1,6 @@
 // `SetDiscriminant` does not actually write anything if the chosen variant is the untagged variant
 // of a niche encoding. Verify that we do not thread over this case.
-//@ unit-test: JumpThreading
+//@ test-mir-pass: JumpThreading
 
 #![feature(custom_mir)]
 #![feature(core_intrinsics)]
diff --git a/tests/mir-opt/simplify_dead_blocks.rs b/tests/mir-opt/simplify_dead_blocks.rs
index d4de856..686eac5 100644
--- a/tests/mir-opt/simplify_dead_blocks.rs
+++ b/tests/mir-opt/simplify_dead_blocks.rs
@@ -1,4 +1,4 @@
-//@ unit-test: SimplifyCfg-after-unreachable-enum-branching
+//@ test-mir-pass: SimplifyCfg-after-unreachable-enum-branching
 #![feature(custom_mir, core_intrinsics)]
 #![crate_type = "lib"]
 
diff --git a/tests/mir-opt/simplify_locals.rs b/tests/mir-opt/simplify_locals.rs
index 756679e..f576111 100644
--- a/tests/mir-opt/simplify_locals.rs
+++ b/tests/mir-opt/simplify_locals.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: SimplifyLocals-before-const-prop
+//@ test-mir-pass: SimplifyLocals-before-const-prop
 
 
 #![feature(thread_local)]
diff --git a/tests/mir-opt/simplify_locals_removes_unused_consts.rs b/tests/mir-opt/simplify_locals_removes_unused_consts.rs
index 3a46164..70d1555 100644
--- a/tests/mir-opt/simplify_locals_removes_unused_consts.rs
+++ b/tests/mir-opt/simplify_locals_removes_unused_consts.rs
@@ -1,6 +1,6 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-//@ unit-test: SimplifyLocals-before-const-prop
+//@ test-mir-pass: SimplifyLocals-before-const-prop
 //@ compile-flags: -C overflow-checks=no
 
 fn use_zst(_: ((), ())) {}
diff --git a/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs b/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs
index 52afb4f..6257f5e 100644
--- a/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs
+++ b/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs
@@ -1,5 +1,5 @@
 // skip-filecheck
-//@ unit-test: SimplifyLocals-before-const-prop
+//@ test-mir-pass: SimplifyLocals-before-const-prop
 
 fn map(x: Option<Box<()>>) -> Option<Box<()>> {
     match x {
diff --git a/tests/mir-opt/sroa/lifetimes.rs b/tests/mir-opt/sroa/lifetimes.rs
index 3f5c994..6c18dba 100644
--- a/tests/mir-opt/sroa/lifetimes.rs
+++ b/tests/mir-opt/sroa/lifetimes.rs
@@ -1,4 +1,4 @@
-//@ unit-test: ScalarReplacementOfAggregates
+//@ test-mir-pass: ScalarReplacementOfAggregates
 //@ compile-flags: -Cpanic=abort
 //@ no-prefer-dynamic
 
diff --git a/tests/mir-opt/sroa/structs.rs b/tests/mir-opt/sroa/structs.rs
index cbe4b98..a177dbf 100644
--- a/tests/mir-opt/sroa/structs.rs
+++ b/tests/mir-opt/sroa/structs.rs
@@ -1,4 +1,4 @@
-//@ unit-test: ScalarReplacementOfAggregates
+//@ test-mir-pass: ScalarReplacementOfAggregates
 //@ compile-flags: -Cpanic=abort
 //@ no-prefer-dynamic
 
diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs
index b07b823..5838b35 100644
--- a/tests/mir-opt/unreachable.rs
+++ b/tests/mir-opt/unreachable.rs
@@ -1,4 +1,4 @@
-//@ unit-test: UnreachablePropagation
+//@ test-mir-pass: UnreachablePropagation
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 enum Empty {}
diff --git a/tests/mir-opt/unreachable_diverging.rs b/tests/mir-opt/unreachable_diverging.rs
index b7e0f6e..695e3c4 100644
--- a/tests/mir-opt/unreachable_diverging.rs
+++ b/tests/mir-opt/unreachable_diverging.rs
@@ -1,4 +1,4 @@
-//@ unit-test: UnreachablePropagation
+//@ test-mir-pass: UnreachablePropagation
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 pub enum Empty {}
diff --git a/tests/mir-opt/unreachable_enum_branching.rs b/tests/mir-opt/unreachable_enum_branching.rs
index 156b236..6005dc5 100644
--- a/tests/mir-opt/unreachable_enum_branching.rs
+++ b/tests/mir-opt/unreachable_enum_branching.rs
@@ -1,4 +1,4 @@
-//@ unit-test: UnreachableEnumBranching
+//@ test-mir-pass: UnreachableEnumBranching
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 enum Empty {}
diff --git a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs
index 2f13cf1..b534a99 100644
--- a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs
+++ b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs
@@ -1,10 +1,10 @@
 //! Make sure that cross-language LTO works on riscv targets,
-//! which requires extra abi metadata to be emitted.
+//! which requires extra `target-abi` metadata to be emitted.
 //@ needs-matching-clang
 //@ needs-llvm-components riscv
 extern crate run_make_support;
 
-use run_make_support::{bin_name, rustc, tmp_dir};
+use run_make_support::{bin_name, clang, llvm_readobj, rustc, tmp_dir};
 use std::{
     env,
     path::PathBuf,
@@ -12,13 +12,6 @@
     str,
 };
 
-fn handle_failed_output(output: Output) {
-    eprintln!("output status: `{}`", output.status);
-    eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap());
-    eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap());
-    std::process::exit(1)
-}
-
 fn check_target(target: &str, clang_target: &str, carch: &str, is_double_float: bool) {
     eprintln!("Checking target {target}");
     // Rust part
@@ -30,40 +23,24 @@
         .linker_plugin_lto("on")
         .run();
     // C part
-    let clang = env::var("CLANG").unwrap();
-    let mut cmd = Command::new(clang);
-    let executable = tmp_dir().join("riscv-xlto");
-    cmd.arg("-target")
-        .arg(clang_target)
-        .arg(format!("-march={carch}"))
-        .arg(format!("-flto=thin"))
-        .arg(format!("-fuse-ld=lld"))
-        .arg("-nostdlib")
-        .arg("-o")
-        .arg(&executable)
-        .arg("cstart.c")
-        .arg(tmp_dir().join("libriscv_xlto.rlib"));
-    eprintln!("{cmd:?}");
-    let output = cmd.output().unwrap();
-    if !output.status.success() {
-        handle_failed_output(output);
-    }
+    clang()
+        .target(clang_target)
+        .arch(carch)
+        .lto("thin")
+        .use_ld("lld")
+        .no_stdlib()
+        .out_exe("riscv-xlto")
+        .input("cstart.c")
+        .input(tmp_dir().join("libriscv_xlto.rlib"))
+        .run();
+
     // Check that the built binary has correct float abi
-    let llvm_readobj =
-        PathBuf::from(env::var("LLVM_BIN_DIR").unwrap()).join(bin_name("llvm-readobj"));
-    let mut cmd = Command::new(llvm_readobj);
-    cmd.arg("--file-header").arg(executable);
-    eprintln!("{cmd:?}");
-    let output = cmd.output().unwrap();
-    if output.status.success() {
-        assert!(
-            !(is_double_float
-                ^ dbg!(str::from_utf8(&output.stdout).unwrap())
-                    .contains("EF_RISCV_FLOAT_ABI_DOUBLE"))
-        )
-    } else {
-        handle_failed_output(output);
-    }
+    let executable = tmp_dir().join(bin_name("riscv-xlto"));
+    let output = llvm_readobj().input(&executable).file_header().run();
+    let stdout = String::from_utf8_lossy(&output.stdout);
+    eprintln!("obj:\n{}", stdout);
+
+    assert!(!(is_double_float ^ stdout.contains("EF_RISCV_FLOAT_ABI_DOUBLE")));
 }
 
 fn main() {
diff --git a/tests/run-make/non-unicode-in-incremental-dir/foo.rs b/tests/run-make/non-unicode-in-incremental-dir/foo.rs
new file mode 100644
index 0000000..f328e4d
--- /dev/null
+++ b/tests/run-make/non-unicode-in-incremental-dir/foo.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs
new file mode 100644
index 0000000..129e424
--- /dev/null
+++ b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs
@@ -0,0 +1,26 @@
+extern crate run_make_support;
+
+use run_make_support::{rustc, tmp_dir};
+
+fn main() {
+    #[cfg(unix)]
+    let non_unicode: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(&[0xFF]);
+    #[cfg(windows)]
+    let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]);
+    match std::fs::create_dir(tmp_dir().join(&non_unicode)) {
+        // If an error occurs, check if creating a directory with a valid Unicode name would
+        // succeed.
+        Err(e) if std::fs::create_dir(tmp_dir().join("valid_unicode")).is_ok() => {
+            // Filesystem doesn't appear support non-Unicode paths.
+            return;
+        }
+        Err(e) => panic!("error creating non-Unicode directory: {e}"),
+        _ => {}
+    }
+    let incr_dir = tmp_dir().join("incr-dir");
+    rustc().input("foo.rs").incremental(&incr_dir).run();
+    for crate_dir in std::fs::read_dir(&incr_dir).unwrap() {
+        std::fs::create_dir(crate_dir.unwrap().path().join(&non_unicode)).unwrap();
+    }
+    rustc().input("foo.rs").incremental(&incr_dir).run();
+}
diff --git a/tests/rustdoc-gui/copy-path.goml b/tests/rustdoc-gui/copy-path.goml
new file mode 100644
index 0000000..dc05b96f
--- /dev/null
+++ b/tests/rustdoc-gui/copy-path.goml
@@ -0,0 +1,15 @@
+// Checks that the "copy path" button is not triggering JS error and its display
+// isn't broken.
+go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+
+// First we store the size of the button before we click on it.
+store-size: ("#copy-path", {"width": width, "height": height})
+click: "#copy-path"
+// We wait for the new text to appear.
+wait-for: "#copy-path.clicked"
+// We check that the size didn't change.
+assert-size: ("#copy-path.clicked", {"width": |width|, "height": |height|})
+// We wait for the button to turn back to its original state.
+wait-for: "#copy-path:not(.clicked)"
+// We check that the size is still the same.
+assert-size: ("#copy-path:not(.clicked)", {"width": |width|, "height": |height|})
diff --git a/tests/rustdoc-js-std/alias-1.js b/tests/rustdoc-js-std/alias-1.js
index b27b3da..c31d1a3 100644
--- a/tests/rustdoc-js-std/alias-1.js
+++ b/tests/rustdoc-js-std/alias-1.js
@@ -1,6 +1,10 @@
 const EXPECTED = {
     'query': '&',
     'others': [
-        { 'path': 'std', 'name': 'reference' },
+        {
+            'path': 'std',
+            'name': 'reference',
+            'desc': "References, <code>&amp;T</code> and <code>&amp;mut T</code>.",
+        },
     ],
 };
diff --git a/tests/rustdoc-js-std/vec-new.js b/tests/rustdoc-js-std/vec-new.js
index 9823a41..bb122ff 100644
--- a/tests/rustdoc-js-std/vec-new.js
+++ b/tests/rustdoc-js-std/vec-new.js
@@ -3,17 +3,47 @@
         'query': 'Vec::new',
         'others': [
             { 'path': 'std::vec::Vec', 'name': 'new' },
-            { 'path': 'alloc::vec::Vec', 'name': 'new' },
             { 'path': 'std::vec::Vec', 'name': 'new_in' },
-            { 'path': 'alloc::vec::Vec', 'name': 'new_in' },
+        ],
+    },
+    {
+        'query': 'prelude::vec',
+        'others': [
+            { 'path': 'std::prelude::rust_2024', 'name': 'Vec' },
         ],
     },
     {
         'query': 'Vec new',
         'others': [
             { 'path': 'std::vec::Vec', 'name': 'new' },
-            { 'path': 'alloc::vec::Vec', 'name': 'new' },
             { 'path': 'std::vec::Vec', 'name': 'new_in' },
+        ],
+    },
+    {
+        'query': 'std::Vec::new',
+        'others': [
+            { 'path': 'std::vec::Vec', 'name': 'new' },
+            { 'path': 'std::vec::Vec', 'name': 'new_in' },
+        ],
+    },
+    {
+        'query': 'std Vec new',
+        'others': [
+            { 'path': 'std::vec::Vec', 'name': 'new' },
+            { 'path': 'std::vec::Vec', 'name': 'new_in' },
+        ],
+    },
+    {
+        'query': 'alloc::Vec::new',
+        'others': [
+            { 'path': 'alloc::vec::Vec', 'name': 'new' },
+            { 'path': 'alloc::vec::Vec', 'name': 'new_in' },
+        ],
+    },
+    {
+        'query': 'alloc Vec new',
+        'others': [
+            { 'path': 'alloc::vec::Vec', 'name': 'new' },
             { 'path': 'alloc::vec::Vec', 'name': 'new_in' },
         ],
     },
diff --git a/tests/rustdoc-js/auxiliary/macro-in-module.rs b/tests/rustdoc-js/auxiliary/macro-in-module.rs
new file mode 100644
index 0000000..b3fbcca
--- /dev/null
+++ b/tests/rustdoc-js/auxiliary/macro-in-module.rs
@@ -0,0 +1,7 @@
+#[macro_use]
+mod hidden_macro_module {
+    #[macro_export]
+    macro_rules! vec {
+        () => {};
+    }
+}
diff --git a/tests/rustdoc-js/reexport-dedup-macro.js b/tests/rustdoc-js/reexport-dedup-macro.js
new file mode 100644
index 0000000..bc42fc3
--- /dev/null
+++ b/tests/rustdoc-js/reexport-dedup-macro.js
@@ -0,0 +1,11 @@
+// exact-check
+
+const EXPECTED = [
+    {
+        'query': 'vec',
+        'others': [
+            { 'path': 'foo', 'name': 'vec', 'exactPath': 'macro_in_module' },
+            { 'path': 'foo', 'name': 'myspecialvec', 'exactPath': 'foo' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/reexport-dedup-macro.rs b/tests/rustdoc-js/reexport-dedup-macro.rs
new file mode 100644
index 0000000..3d18da3
--- /dev/null
+++ b/tests/rustdoc-js/reexport-dedup-macro.rs
@@ -0,0 +1,15 @@
+//@ aux-crate: macro_in_module=macro-in-module.rs
+#![crate_name="foo"]
+extern crate macro_in_module;
+
+// Test case based on the relationship between alloc and std.
+#[doc(inline)]
+pub use macro_in_module::vec;
+
+#[macro_use]
+mod hidden_macro_module {
+    #[macro_export]
+    macro_rules! myspecialvec {
+        () => {};
+    }
+}
diff --git a/tests/rustdoc-js/reexport-dedup-method.js b/tests/rustdoc-js/reexport-dedup-method.js
new file mode 100644
index 0000000..5d1497d
--- /dev/null
+++ b/tests/rustdoc-js/reexport-dedup-method.js
@@ -0,0 +1,16 @@
+// exact-check
+
+const EXPECTED = [
+    {
+        'query': 'Subscriber dostuff',
+        'others': [
+            { 'path': 'foo::fmt::Subscriber', 'name': 'dostuff' },
+        ],
+    },
+    {
+        'query': 'AnotherOne dostuff',
+        'others': [
+            { 'path': 'foo::AnotherOne', 'name': 'dostuff' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/reexport-dedup-method.rs b/tests/rustdoc-js/reexport-dedup-method.rs
new file mode 100644
index 0000000..5dbd66e
--- /dev/null
+++ b/tests/rustdoc-js/reexport-dedup-method.rs
@@ -0,0 +1,18 @@
+// This test enforces that the (renamed) reexports are present in the search results.
+#![crate_name="foo"]
+
+pub mod fmt {
+    pub struct Subscriber;
+    impl Subscriber {
+        pub fn dostuff(&self) {}
+    }
+}
+mod foo {
+    pub struct AnotherOne;
+    impl AnotherOne {
+        pub fn dostuff(&self) {}
+    }
+}
+
+pub use foo::AnotherOne;
+pub use fmt::Subscriber;
diff --git a/tests/rustdoc-js/reexport-dedup.js b/tests/rustdoc-js/reexport-dedup.js
new file mode 100644
index 0000000..979619c
--- /dev/null
+++ b/tests/rustdoc-js/reexport-dedup.js
@@ -0,0 +1,22 @@
+// exact-check
+
+const EXPECTED = [
+    {
+        'query': 'Subscriber',
+        'others': [
+            { 'path': 'foo', 'name': 'Subscriber' },
+        ],
+    },
+    {
+        'query': 'fmt Subscriber',
+        'others': [
+            { 'path': 'foo::fmt', 'name': 'Subscriber' },
+        ],
+    },
+    {
+        'query': 'AnotherOne',
+        'others': [
+            { 'path': 'foo', 'name': 'AnotherOne' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/reexport-dedup.rs b/tests/rustdoc-js/reexport-dedup.rs
new file mode 100644
index 0000000..40aea96
--- /dev/null
+++ b/tests/rustdoc-js/reexport-dedup.rs
@@ -0,0 +1,12 @@
+// This test enforces that the (renamed) reexports are present in the search results.
+#![crate_name="foo"]
+
+pub mod fmt {
+    pub struct Subscriber;
+}
+mod foo {
+    pub struct AnotherOne;
+}
+
+pub use foo::AnotherOne;
+pub use fmt::Subscriber;
diff --git a/tests/rustdoc-js/reexport.rs b/tests/rustdoc-js/reexport.rs
index d69b290..d51b7fb 100644
--- a/tests/rustdoc-js/reexport.rs
+++ b/tests/rustdoc-js/reexport.rs
@@ -1,4 +1,6 @@
 // This test enforces that the (renamed) reexports are present in the search results.
+// This is a DWIM case, since renaming the export probably means the intent is also different.
+// For the de-duplication case of exactly the same name, see reexport-dedup
 
 pub mod fmt {
     pub struct Subscriber;
diff --git a/tests/rustdoc/const-display.rs b/tests/rustdoc/const-display.rs
index 594501b..c8967f4 100644
--- a/tests/rustdoc/const-display.rs
+++ b/tests/rustdoc/const-display.rs
@@ -72,8 +72,8 @@
 pub struct Bar;
 
 impl Bar {
-    // Do not show non-const stabilities that are the same as the enclosing item.
-    // @matches 'foo/struct.Bar.html' '//span[@class="since"]' '^const: 1.2.0$'
+    // Show non-const stabilities that are the same as the enclosing item.
+    // @has 'foo/struct.Bar.html' '//span[@class="since"]' '1.0.0 (const: 1.2.0)'
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "const2", since = "1.2.0")]
     pub const fn stable_impl() -> u32 { 42 }
diff --git a/tests/rustdoc/deref/deref-const-fn.rs b/tests/rustdoc/deref/deref-const-fn.rs
index 8ecca6d..85c2f29 100644
--- a/tests/rustdoc/deref/deref-const-fn.rs
+++ b/tests/rustdoc/deref/deref-const-fn.rs
@@ -26,7 +26,7 @@
 
 // @has 'foo/struct.Foo.html'
 // @has - '//*[@id="method.len"]' 'pub fn len(&self) -> usize'
-// @!has - '//*[@id="method.len"]//span[@class="since"]' '1.0.0'
+// @has - '//*[@id="method.len"]//span[@class="since"]' '1.0.0'
 // @!has - '//*[@id="method.len"]//span[@class="since"]' '(const: 1.0.0)'
 #[stable(feature = "rust1", since = "1.0.0")]
 impl std::ops::Deref for Foo {
diff --git a/tests/rustdoc/ensure-src-link.rs b/tests/rustdoc/ensure-src-link.rs
index f99c4c4..f95b5f2 100644
--- a/tests/rustdoc/ensure-src-link.rs
+++ b/tests/rustdoc/ensure-src-link.rs
@@ -2,5 +2,5 @@
 
 // This test ensures that the [src] link is present on traits items.
 
-// @has foo/trait.Iterator.html '//*[@id="method.zip"]//a[@class="src rightside"]' "source"
+// @has foo/trait.Iterator.html '//*[@id="method.zip"]//a[@class="src"]' "source"
 pub use std::iter::Iterator;
diff --git a/tests/rustdoc/implementor-stable-version.rs b/tests/rustdoc/implementor-stable-version.rs
index 9c5b9b7..3674b9f 100644
--- a/tests/rustdoc/implementor-stable-version.rs
+++ b/tests/rustdoc/implementor-stable-version.rs
@@ -16,6 +16,6 @@
 #[stable(feature = "foobar", since = "4.4.4")]
 impl Bar for Foo {}
 
-// @!has foo/trait.Baz.html '//div[@id="implementors-list"]//span[@class="since"]' '3.3.3'
+// @has foo/trait.Baz.html '//div[@id="implementors-list"]//span[@class="since"]' '3.3.3'
 #[stable(feature = "foobaz", since = "3.3.3")]
 impl Baz for Foo {}
diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs
index 3ee4542..373d1cc 100644
--- a/tests/ui/abi/compatibility.rs
+++ b/tests/ui/abi/compatibility.rs
@@ -64,7 +64,6 @@
   [csky] needs-llvm-components: csky
 */
 #![feature(rustc_attrs, unsized_fn_params, transparent_unions)]
-#![cfg_attr(host, feature(generic_nonzero))]
 #![cfg_attr(not(host), feature(no_core, lang_items), no_std, no_core)]
 #![allow(unused, improper_ctypes_definitions, internal_features)]
 
diff --git a/tests/ui/abi/riscv-discoverability-guidance.rs b/tests/ui/abi/riscv-discoverability-guidance.rs
index a09a0ed..1b189d9 100644
--- a/tests/ui/abi/riscv-discoverability-guidance.rs
+++ b/tests/ui/abi/riscv-discoverability-guidance.rs
@@ -2,9 +2,9 @@
 //@ revisions: riscv32 riscv64
 //
 //@ [riscv32] needs-llvm-components: riscv
-//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf -C target-feature=-fast-unaligned-access --crate-type=rlib
+//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf -C target-feature=-unaligned-scalar-mem --crate-type=rlib
 //@ [riscv64] needs-llvm-components: riscv
-//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf -C target-feature=-fast-unaligned-access --crate-type=rlib
+//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf -C target-feature=-unaligned-scalar-mem --crate-type=rlib
 #![no_core]
 #![feature(
     no_core,
diff --git a/tests/ui/abi/segfault-no-out-of-stack.rs b/tests/ui/abi/segfault-no-out-of-stack.rs
index fb1f830..113c82c 100644
--- a/tests/ui/abi/segfault-no-out-of-stack.rs
+++ b/tests/ui/abi/segfault-no-out-of-stack.rs
@@ -1,31 +1,29 @@
 //@ run-pass
-
-#![allow(unused_imports)]
 //@ ignore-wasm32 can't run commands
 //@ ignore-sgx no processes
 //@ ignore-fuchsia must translate zircon signal to SIGSEGV/SIGBUS, FIXME (#58590)
+
 #![feature(rustc_private)]
 
-extern crate libc;
-
 use std::env;
+use std::ffi::c_char;
 use std::process::{Command, ExitStatus};
 
 #[link(name = "rust_test_helpers", kind = "static")]
 extern "C" {
-    fn rust_get_null_ptr() -> *mut ::libc::c_char;
+    fn rust_get_null_ptr() -> *mut c_char;
 }
 
 #[cfg(unix)]
-fn check_status(status: std::process::ExitStatus) {
-    use libc;
+fn check_status(status: ExitStatus) {
+    extern crate libc;
     use std::os::unix::process::ExitStatusExt;
 
     assert!(status.signal() == Some(libc::SIGSEGV) || status.signal() == Some(libc::SIGBUS));
 }
 
 #[cfg(not(unix))]
-fn check_status(status: std::process::ExitStatus) {
+fn check_status(status: ExitStatus) {
     assert!(!status.success());
 }
 
diff --git a/tests/ui/associated-inherent-types/constrain_opaque_types_during_projection.rs b/tests/ui/associated-inherent-types/constrain_opaque_types_during_projection.rs
new file mode 100644
index 0000000..292733c
--- /dev/null
+++ b/tests/ui/associated-inherent-types/constrain_opaque_types_during_projection.rs
@@ -0,0 +1,18 @@
+//@ check-pass
+
+#![feature(inherent_associated_types, type_alias_impl_trait)]
+#![allow(incomplete_features)]
+
+struct Foo<T>(T);
+
+impl Foo<i32> {
+    type Assoc = u32;
+}
+
+type Tait = impl Sized;
+
+fn bar(_: Tait) {
+    let x: Foo<Tait>::Assoc = 42;
+}
+
+fn main() {}
diff --git a/tests/ui/issues/issue-19129-1.rs b/tests/ui/associated-types/issue-19129-1.rs
similarity index 100%
rename from tests/ui/issues/issue-19129-1.rs
rename to tests/ui/associated-types/issue-19129-1.rs
diff --git a/tests/ui/issues/issue-19129-2.rs b/tests/ui/associated-types/issue-19129-2.rs
similarity index 100%
rename from tests/ui/issues/issue-19129-2.rs
rename to tests/ui/associated-types/issue-19129-2.rs
diff --git a/tests/ui/issues/issue-20763-1.rs b/tests/ui/associated-types/issue-20763-1.rs
similarity index 100%
rename from tests/ui/issues/issue-20763-1.rs
rename to tests/ui/associated-types/issue-20763-1.rs
diff --git a/tests/ui/issues/issue-20763-2.rs b/tests/ui/associated-types/issue-20763-2.rs
similarity index 100%
rename from tests/ui/issues/issue-20763-2.rs
rename to tests/ui/associated-types/issue-20763-2.rs
diff --git a/tests/ui/async-await/async-drop.rs b/tests/ui/async-await/async-drop.rs
new file mode 100644
index 0000000..6d02dce
--- /dev/null
+++ b/tests/ui/async-await/async-drop.rs
@@ -0,0 +1,197 @@
+//@ run-pass
+//@ check-run-results
+
+#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)]
+#![allow(incomplete_features, dead_code)]
+
+//@ edition: 2021
+
+// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
+use core::future::{async_drop_in_place, AsyncDrop, Future};
+use core::hint::black_box;
+use core::mem::{self, ManuallyDrop};
+use core::pin::{pin, Pin};
+use core::task::{Context, Poll, Waker};
+
+async fn test_async_drop<T>(x: T) {
+    let mut x = mem::MaybeUninit::new(x);
+    let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) });
+    test_idempotency(dtor).await;
+}
+
+fn test_idempotency<T>(mut x: Pin<&mut T>) -> impl Future<Output = ()> + '_
+where
+    T: Future<Output = ()>,
+{
+    core::future::poll_fn(move |cx| {
+        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
+        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
+        Poll::Ready(())
+    })
+}
+
+fn main() {
+    let waker = Waker::noop();
+    let mut cx = Context::from_waker(&waker);
+
+    let i = 13;
+    let fut = pin!(async {
+        test_async_drop(Int(0)).await;
+        test_async_drop(AsyncInt(0)).await;
+        test_async_drop([AsyncInt(1), AsyncInt(2)]).await;
+        test_async_drop((AsyncInt(3), AsyncInt(4))).await;
+        test_async_drop(5).await;
+        let j = 42;
+        test_async_drop(&i).await;
+        test_async_drop(&j).await;
+        test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await;
+        test_async_drop(ManuallyDrop::new(AsyncInt(9))).await;
+
+        let foo = AsyncInt(10);
+        test_async_drop(AsyncReference { foo: &foo }).await;
+
+        let foo = AsyncInt(11);
+        test_async_drop(|| {
+            black_box(foo);
+            let foo = AsyncInt(10);
+            foo
+        }).await;
+
+        test_async_drop(AsyncEnum::A(AsyncInt(12))).await;
+        test_async_drop(AsyncEnum::B(SyncInt(13))).await;
+
+        test_async_drop(SyncInt(14)).await;
+        test_async_drop(SyncThenAsync {
+            i: 15,
+            a: AsyncInt(16),
+            b: SyncInt(17),
+            c: AsyncInt(18),
+        }).await;
+
+        let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19)));
+        test_idempotency(async_drop_fut).await;
+
+        let foo = AsyncInt(20);
+        test_async_drop(async || {
+            black_box(foo);
+            let foo = AsyncInt(19);
+            // Await point there, but this is async closure so it's fine
+            black_box(core::future::ready(())).await;
+            foo
+        }).await;
+
+        test_async_drop(AsyncUnion { signed: 21 }).await;
+    });
+    let res = fut.poll(&mut cx);
+    assert_eq!(res, Poll::Ready(()));
+}
+
+struct AsyncInt(i32);
+
+impl AsyncDrop for AsyncInt {
+    type Dropper<'a> = impl Future<Output = ()>;
+
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            println!("AsyncInt::Dropper::poll: {}", self.0);
+        }
+    }
+}
+
+struct SyncInt(i32);
+
+impl Drop for SyncInt {
+    fn drop(&mut self) {
+        println!("SyncInt::drop: {}", self.0);
+    }
+}
+
+struct SyncThenAsync {
+    i: i32,
+    a: AsyncInt,
+    b: SyncInt,
+    c: AsyncInt,
+}
+
+impl Drop for SyncThenAsync {
+    fn drop(&mut self) {
+        println!("SyncThenAsync::drop: {}", self.i);
+    }
+}
+
+struct AsyncReference<'a> {
+    foo: &'a AsyncInt,
+}
+
+impl AsyncDrop for AsyncReference<'_> {
+    type Dropper<'a> = impl Future<Output = ()> where Self: 'a;
+
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            println!("AsyncReference::Dropper::poll: {}", self.foo.0);
+        }
+    }
+}
+
+struct Int(i32);
+
+struct AsyncStruct {
+    i: i32,
+    a: AsyncInt,
+    b: AsyncInt,
+}
+
+impl AsyncDrop for AsyncStruct {
+    type Dropper<'a> = impl Future<Output = ()>;
+
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            println!("AsyncStruct::Dropper::poll: {}", self.i);
+        }
+    }
+}
+
+enum AsyncEnum {
+    A(AsyncInt),
+    B(SyncInt),
+}
+
+impl AsyncDrop for AsyncEnum {
+    type Dropper<'a> = impl Future<Output = ()>;
+
+    fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            let new_self = match &*self {
+                AsyncEnum::A(foo) => {
+                    println!("AsyncEnum(A)::Dropper::poll: {}", foo.0);
+                    AsyncEnum::B(SyncInt(foo.0))
+                }
+                AsyncEnum::B(foo) => {
+                    println!("AsyncEnum(B)::Dropper::poll: {}", foo.0);
+                    AsyncEnum::A(AsyncInt(foo.0))
+                }
+            };
+            mem::forget(mem::replace(&mut *self, new_self));
+        }
+    }
+}
+
+// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions
+union AsyncUnion {
+    signed: i32,
+    unsigned: u32,
+}
+
+impl AsyncDrop for AsyncUnion {
+    type Dropper<'a> = impl Future<Output = ()>;
+
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
+        async move {
+            println!(
+                "AsyncUnion::Dropper::poll: {}, {}",
+                unsafe { self.signed },
+                unsafe { self.unsigned },
+            );
+        }
+    }
+}
diff --git a/tests/ui/async-await/async-drop.run.stdout b/tests/ui/async-await/async-drop.run.stdout
new file mode 100644
index 0000000..9cae433
--- /dev/null
+++ b/tests/ui/async-await/async-drop.run.stdout
@@ -0,0 +1,22 @@
+AsyncInt::Dropper::poll: 0
+AsyncInt::Dropper::poll: 1
+AsyncInt::Dropper::poll: 2
+AsyncInt::Dropper::poll: 3
+AsyncInt::Dropper::poll: 4
+AsyncStruct::Dropper::poll: 6
+AsyncInt::Dropper::poll: 7
+AsyncInt::Dropper::poll: 8
+AsyncReference::Dropper::poll: 10
+AsyncInt::Dropper::poll: 11
+AsyncEnum(A)::Dropper::poll: 12
+SyncInt::drop: 12
+AsyncEnum(B)::Dropper::poll: 13
+AsyncInt::Dropper::poll: 13
+SyncInt::drop: 14
+SyncThenAsync::drop: 15
+AsyncInt::Dropper::poll: 16
+SyncInt::drop: 17
+AsyncInt::Dropper::poll: 18
+AsyncInt::Dropper::poll: 19
+AsyncInt::Dropper::poll: 20
+AsyncUnion::Dropper::poll: 21, 21
diff --git a/tests/ui/attributes/unix_sigpipe/unix_sigpipe-and-child-processes.rs b/tests/ui/attributes/unix_sigpipe/unix_sigpipe-and-child-processes.rs
index f96bd63..9d1bd9f 100644
--- a/tests/ui/attributes/unix_sigpipe/unix_sigpipe-and-child-processes.rs
+++ b/tests/ui/attributes/unix_sigpipe/unix_sigpipe-and-child-processes.rs
@@ -11,10 +11,8 @@
 // processes with and without the attribute. Search for
 // `unix_sigpipe_attr_specified()` in the code base to learn more.
 
-#![feature(rustc_private)]
 #![cfg_attr(any(sig_dfl, sig_ign, inherit), feature(unix_sigpipe))]
 
-extern crate libc;
 extern crate sigpipe_utils;
 
 use sigpipe_utils::*;
diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.rs b/tests/ui/binding/issue-40402-1.rs
similarity index 100%
rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-1.rs
rename to tests/ui/binding/issue-40402-1.rs
diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr b/tests/ui/binding/issue-40402-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr
rename to tests/ui/binding/issue-40402-1.stderr
diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-2.rs b/tests/ui/binding/issue-40402-2.rs
similarity index 100%
rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-2.rs
rename to tests/ui/binding/issue-40402-2.rs
diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr b/tests/ui/binding/issue-40402-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr
rename to tests/ui/binding/issue-40402-2.stderr
diff --git a/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs
new file mode 100644
index 0000000..cf927e3
--- /dev/null
+++ b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs
@@ -0,0 +1,12 @@
+// issue rust-lang/rust#121463
+// ICE non-ADT in struct pattern
+#![feature(box_patterns)]
+
+fn main() {
+    let mut a = E::StructVar { boxed: Box::new(5_i32) };
+    //~^ ERROR failed to resolve: use of undeclared type `E`
+    match a {
+        E::StructVar { box boxed } => { }
+        //~^ ERROR failed to resolve: use of undeclared type `E`
+    }
+}
diff --git a/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr
new file mode 100644
index 0000000..3495466
--- /dev/null
+++ b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr
@@ -0,0 +1,21 @@
+error[E0433]: failed to resolve: use of undeclared type `E`
+  --> $DIR/non-ADT-struct-pattern-box-pattern-ice-121463.rs:6:17
+   |
+LL |     let mut a = E::StructVar { boxed: Box::new(5_i32) };
+   |                 ^
+   |                 |
+   |                 use of undeclared type `E`
+   |                 help: a trait with a similar name exists: `Eq`
+
+error[E0433]: failed to resolve: use of undeclared type `E`
+  --> $DIR/non-ADT-struct-pattern-box-pattern-ice-121463.rs:9:9
+   |
+LL |         E::StructVar { box boxed } => { }
+   |         ^
+   |         |
+   |         use of undeclared type `E`
+   |         help: a trait with a similar name exists: `Eq`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr
index feb4f21..557fdcb 100644
--- a/tests/ui/check-cfg/mix.stderr
+++ b/tests/ui/check-cfg/mix.stderr
@@ -251,7 +251,7 @@
 LL |     cfg!(target_feature = "zebra");
    |          ^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2` and 187 more
+   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2` and 188 more
    = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
 
 warning: 27 warnings emitted
diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr
index af3ef92..1863032 100644
--- a/tests/ui/check-cfg/well-known-values.stderr
+++ b/tests/ui/check-cfg/well-known-values.stderr
@@ -154,7 +154,7 @@
 LL |     target_feature = "_UNEXPECTED_VALUE",
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `f`, `f16c`, `f32mm`, `f64mm`, `fast-unaligned-access`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt`
+   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `extended-const`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt`
    = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
 
 warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
diff --git a/tests/ui/issues/issue-22864-1.rs b/tests/ui/closures/issue-22864-1.rs
similarity index 100%
rename from tests/ui/issues/issue-22864-1.rs
rename to tests/ui/closures/issue-22864-1.rs
diff --git a/tests/ui/issues/issue-22864-2.rs b/tests/ui/closures/issue-22864-2.rs
similarity index 100%
rename from tests/ui/issues/issue-22864-2.rs
rename to tests/ui/closures/issue-22864-2.rs
diff --git a/tests/ui/issues/issue-5239-1.rs b/tests/ui/closures/issue-5239-1.rs
similarity index 100%
rename from tests/ui/issues/issue-5239-1.rs
rename to tests/ui/closures/issue-5239-1.rs
diff --git a/tests/ui/issues/issue-5239-1.stderr b/tests/ui/closures/issue-5239-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-5239-1.stderr
rename to tests/ui/closures/issue-5239-1.stderr
diff --git a/tests/ui/issues/issue-5239-2.rs b/tests/ui/closures/issue-5239-2.rs
similarity index 100%
rename from tests/ui/issues/issue-5239-2.rs
rename to tests/ui/closures/issue-5239-2.rs
diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.fixed b/tests/ui/coercion/issue-32122-1.fixed
similarity index 100%
rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.fixed
rename to tests/ui/coercion/issue-32122-1.fixed
diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.rs b/tests/ui/coercion/issue-32122-1.rs
similarity index 100%
rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.rs
rename to tests/ui/coercion/issue-32122-1.rs
diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.stderr b/tests/ui/coercion/issue-32122-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.stderr
rename to tests/ui/coercion/issue-32122-1.stderr
diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.fixed b/tests/ui/coercion/issue-32122-2.fixed
similarity index 100%
rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.fixed
rename to tests/ui/coercion/issue-32122-2.fixed
diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.rs b/tests/ui/coercion/issue-32122-2.rs
similarity index 100%
rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.rs
rename to tests/ui/coercion/issue-32122-2.rs
diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.stderr b/tests/ui/coercion/issue-32122-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.stderr
rename to tests/ui/coercion/issue-32122-2.stderr
diff --git a/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.rs b/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.rs
new file mode 100644
index 0000000..0d1f023
--- /dev/null
+++ b/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.rs
@@ -0,0 +1,18 @@
+// issue: rust-lang/rust/#83993
+
+#![feature(adt_const_params)]
+//~^ WARN the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
+fn bug<'a>()
+where
+    for<'b> [(); {
+        let x: &'b ();
+        //~^ ERROR generic parameters may not be used in const operations
+        0
+    }]:
+{}
+
+fn bad() where for<'b> [();{let _:&'b (); 0}]: Sized { }
+//~^ ERROR generic parameters may not be used in const operations
+fn good() where for<'b> [();{0}]: Sized { }
+
+pub fn main() {}
diff --git a/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.stderr b/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.stderr
new file mode 100644
index 0000000..a49dfc3
--- /dev/null
+++ b/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.stderr
@@ -0,0 +1,29 @@
+error: generic parameters may not be used in const operations
+  --> $DIR/index-oob-ice-83993.rs:8:17
+   |
+LL |         let x: &'b ();
+   |                 ^^ cannot perform const operation using `'b`
+   |
+   = note: lifetime parameters may not be used in const expressions
+   = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+error: generic parameters may not be used in const operations
+  --> $DIR/index-oob-ice-83993.rs:14:36
+   |
+LL | fn bad() where for<'b> [();{let _:&'b (); 0}]: Sized { }
+   |                                    ^^ cannot perform const operation using `'b`
+   |
+   = note: lifetime parameters may not be used in const expressions
+   = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/index-oob-ice-83993.rs:3:12
+   |
+LL | #![feature(adt_const_params)]
+   |            ^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
diff --git a/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs
new file mode 100644
index 0000000..2ce998e
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs
@@ -0,0 +1,10 @@
+// issue: rust-lang/rust#114463
+// ICE cannot convert `ReFree ..` to a region vid
+#![feature(generic_const_exprs)]
+//~^ WARN the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+fn bug<'a>() {
+    [(); (|_: &'a u8| (), 0).1];
+    //~^ ERROR cannot capture late-bound lifetime in constant
+}
+
+pub fn main() {}
diff --git a/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr
new file mode 100644
index 0000000..e484540
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr
@@ -0,0 +1,19 @@
+warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/cannot-convert-refree-ice-114463.rs:3:12
+   |
+LL | #![feature(generic_const_exprs)]
+   |            ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: cannot capture late-bound lifetime in constant
+  --> $DIR/cannot-convert-refree-ice-114463.rs:6:16
+   |
+LL | fn bug<'a>() {
+   |        -- lifetime defined here
+LL |     [(); (|_: &'a u8| (), 0).1];
+   |                ^^
+
+error: aborting due to 1 previous error; 1 warning emitted
+
diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs
index 6ede6f5..0f70862 100644
--- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs
+++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs
@@ -2,10 +2,15 @@
 #![feature(const_heap)]
 #![feature(const_mut_refs)]
 
+// Strip out raw byte dumps to make comparison platform-independent:
+//@ normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
+//@ normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> "HEX_DUMP"
+//@ normalize-stderr-test "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP"
+
 use std::intrinsics;
 
 const _X: &'static u8 = unsafe {
-    //~^ error: dangling pointer in final value of constant
+    //~^ error: it is undefined behavior to use this value
     let ptr = intrinsics::const_allocate(4, 4);
     intrinsics::const_deallocate(ptr, 4, 4);
     &*ptr
diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr
index 59f5f79..a42c26c 100644
--- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr
+++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr
@@ -1,14 +1,19 @@
-error: encountered dangling pointer in final value of constant
-  --> $DIR/dealloc_intrinsic_dangling.rs:7:1
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/dealloc_intrinsic_dangling.rs:12:1
    |
 LL | const _X: &'static u8 = unsafe {
-   | ^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free)
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/dealloc_intrinsic_dangling.rs:18:5
+  --> $DIR/dealloc_intrinsic_dangling.rs:23:5
    |
 LL |     *reference
-   |     ^^^^^^^^^^ memory access failed: ALLOC0 has been freed, so this pointer is dangling
+   |     ^^^^^^^^^^ memory access failed: ALLOC1 has been freed, so this pointer is dangling
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/const-eval/raw-bytes.rs b/tests/ui/consts/const-eval/raw-bytes.rs
index e5dfd5c..2fbf135 100644
--- a/tests/ui/consts/const-eval/raw-bytes.rs
+++ b/tests/ui/consts/const-eval/raw-bytes.rs
@@ -3,7 +3,7 @@
 // ignore-tidy-linelength
 //@ normalize-stderr-test "╾─*ALLOC[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼" -> "╾ALLOC_ID$1╼"
 #![allow(invalid_value)]
-#![feature(generic_nonzero, never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)]
+#![feature(never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)]
 
 use std::mem;
 use std::alloc::Layout;
diff --git a/tests/ui/consts/const-eval/ub-nonnull.rs b/tests/ui/consts/const-eval/ub-nonnull.rs
index 76bd524..10d3044 100644
--- a/tests/ui/consts/const-eval/ub-nonnull.rs
+++ b/tests/ui/consts/const-eval/ub-nonnull.rs
@@ -2,7 +2,7 @@
 //@ normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
 //@ normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP"
 #![allow(invalid_value)] // make sure we cannot allow away the errors tested here
-#![feature(generic_nonzero, rustc_attrs, ptr_metadata)]
+#![feature(rustc_attrs, ptr_metadata)]
 
 use std::mem;
 use std::ptr::NonNull;
diff --git a/tests/ui/consts/const-eval/valid-const.rs b/tests/ui/consts/const-eval/valid-const.rs
index 15d3e88..777484c 100644
--- a/tests/ui/consts/const-eval/valid-const.rs
+++ b/tests/ui/consts/const-eval/valid-const.rs
@@ -1,7 +1,6 @@
 //@ check-pass
 //
 // Some constants that *are* valid
-#![feature(generic_nonzero)]
 
 use std::mem;
 use std::ptr::NonNull;
diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs
index f3f0e14..b86846a 100644
--- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs
+++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs
@@ -33,8 +33,8 @@
     // Undefined behaviour (dangling pointer), who doesn't love tests like this.
     Some(&mut *(&mut 42 as *mut i32))
 } }
-const DANGLING: Option<&mut i32> = helper_dangling(); //~ ERROR encountered dangling pointer
-static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); //~ ERROR encountered dangling pointer
+const DANGLING: Option<&mut i32> = helper_dangling(); //~ ERROR it is undefined behavior to use this value
+static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); //~ ERROR it is undefined behavior to use this value
 
 // These are fine! Just statics pointing to mutable statics, nothing fundamentally wrong with this.
 static MUT_STATIC: Option<&mut i32> = helper();
diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr
index 5923683..ea9dccf 100644
--- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr
+++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr
@@ -31,17 +31,27 @@
                HEX_DUMP
            }
 
-error: encountered dangling pointer in final value of constant
+error[E0080]: it is undefined behavior to use this value
   --> $DIR/mut_ref_in_final_dynamic_check.rs:36:1
    |
 LL | const DANGLING: Option<&mut i32> = helper_dangling();
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
 
-error: encountered dangling pointer in final value of static
+error[E0080]: it is undefined behavior to use this value
   --> $DIR/mut_ref_in_final_dynamic_check.rs:37:1
    |
 LL | static DANGLING_STATIC: Option<&mut i32> = helper_dangling();
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.rs b/tests/ui/consts/const_refs_to_static-ice-121413.rs
new file mode 100644
index 0000000..8a24fb7
--- /dev/null
+++ b/tests/ui/consts/const_refs_to_static-ice-121413.rs
@@ -0,0 +1,16 @@
+// ICE: ImmTy { imm: Scalar(alloc1), ty: *const dyn Sync } input to a fat-to-thin cast (*const dyn Sync -> *const usize
+// or with -Zextra-const-ub-checks: expected wide pointer extra data (e.g. slice length or trait object vtable)
+// issue: rust-lang/rust#121413
+//@ compile-flags: -Zextra-const-ub-checks
+// ignore-tidy-linelength
+#![feature(const_refs_to_static)]
+const REF_INTERIOR_MUT: &usize = {
+    static FOO: Sync = AtomicUsize::new(0);
+    //~^ ERROR failed to resolve: use of undeclared type `AtomicUsize`
+    //~| WARN trait objects without an explicit `dyn` are deprecated
+    //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time
+    //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time
+    //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
+    unsafe { &*(&FOO as *const _ as *const usize) }
+};
+pub fn main() {}
diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.stderr b/tests/ui/consts/const_refs_to_static-ice-121413.stderr
new file mode 100644
index 0000000..c977c69
--- /dev/null
+++ b/tests/ui/consts/const_refs_to_static-ice-121413.stderr
@@ -0,0 +1,46 @@
+error[E0433]: failed to resolve: use of undeclared type `AtomicUsize`
+  --> $DIR/const_refs_to_static-ice-121413.rs:8:24
+   |
+LL |     static FOO: Sync = AtomicUsize::new(0);
+   |                        ^^^^^^^^^^^ use of undeclared type `AtomicUsize`
+   |
+help: consider importing this struct
+   |
+LL + use std::sync::atomic::AtomicUsize;
+   |
+
+warning: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/const_refs_to_static-ice-121413.rs:8:17
+   |
+LL |     static FOO: Sync = AtomicUsize::new(0);
+   |                 ^^^^
+   |
+   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
+   = note: `#[warn(bare_trait_objects)]` on by default
+help: if this is an object-safe trait, use `dyn`
+   |
+LL |     static FOO: dyn Sync = AtomicUsize::new(0);
+   |                 +++
+
+error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time
+  --> $DIR/const_refs_to_static-ice-121413.rs:8:17
+   |
+LL |     static FOO: Sync = AtomicUsize::new(0);
+   |                 ^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)`
+
+error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time
+  --> $DIR/const_refs_to_static-ice-121413.rs:8:24
+   |
+LL |     static FOO: Sync = AtomicUsize::new(0);
+   |                        ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)`
+   = note: constant expressions must have a statically known size
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0277, E0433.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/consts/dangling-alloc-id-ice.rs b/tests/ui/consts/dangling-alloc-id-ice.rs
index d9f458f..76d6f33 100644
--- a/tests/ui/consts/dangling-alloc-id-ice.rs
+++ b/tests/ui/consts/dangling-alloc-id-ice.rs
@@ -1,4 +1,8 @@
 // https://github.com/rust-lang/rust/issues/55223
+// Strip out raw byte dumps to make comparison platform-independent:
+//@ normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
+//@ normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> "HEX_DUMP"
+//@ normalize-stderr-test "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP"
 
 union Foo<'a> {
     y: &'a (),
@@ -6,7 +10,7 @@
 }
 
 const FOO: &() = {
-//~^ ERROR encountered dangling pointer in final value of constant
+    //~^ ERROR it is undefined behavior to use this value
     let y = ();
     unsafe { Foo { y: &y }.long_live_the_unit }
 };
diff --git a/tests/ui/consts/dangling-alloc-id-ice.stderr b/tests/ui/consts/dangling-alloc-id-ice.stderr
index 8322b18..881c0b1 100644
--- a/tests/ui/consts/dangling-alloc-id-ice.stderr
+++ b/tests/ui/consts/dangling-alloc-id-ice.stderr
@@ -1,8 +1,14 @@
-error: encountered dangling pointer in final value of constant
-  --> $DIR/dangling-alloc-id-ice.rs:8:1
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/dangling-alloc-id-ice.rs:12:1
    |
 LL | const FOO: &() = {
-   | ^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free)
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/dangling_raw_ptr.rs b/tests/ui/consts/dangling_raw_ptr.rs
index 0ab3643..e68a7c9 100644
--- a/tests/ui/consts/dangling_raw_ptr.rs
+++ b/tests/ui/consts/dangling_raw_ptr.rs
@@ -1,8 +1,29 @@
-const FOO: *const u32 = { //~ ERROR encountered dangling pointer in final value of constant
+const FOO: *const u32 = {
+    //~^ ERROR encountered dangling pointer in final value of constant
     let x = 42;
     &x
 };
 
-fn main() {
-    let x = FOO;
+union Union {
+    ptr: *const u32,
 }
+
+const BAR: Union = {
+    //~^ ERROR encountered dangling pointer in final value of constant
+    let x = 42;
+    Union { ptr: &x }
+};
+
+const BAZ: Union = {
+    //~^ ERROR encountered dangling pointer in final value of constant
+    let x = 42_u32;
+    Union { ptr: &(&x as *const u32) as *const *const u32 as _ }
+};
+
+const FOOMP: *const u32 = {
+    //~^ ERROR encountered dangling pointer in final value of constant
+    let x = 42_u32;
+    &(&x as *const u32) as *const *const u32 as _
+};
+
+fn main() {}
diff --git a/tests/ui/consts/dangling_raw_ptr.stderr b/tests/ui/consts/dangling_raw_ptr.stderr
index 28a58ae..5cabbbd 100644
--- a/tests/ui/consts/dangling_raw_ptr.stderr
+++ b/tests/ui/consts/dangling_raw_ptr.stderr
@@ -4,5 +4,23 @@
 LL | const FOO: *const u32 = {
    | ^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 1 previous error
+error: encountered dangling pointer in final value of constant
+  --> $DIR/dangling_raw_ptr.rs:11:1
+   |
+LL | const BAR: Union = {
+   | ^^^^^^^^^^^^^^^^
+
+error: encountered dangling pointer in final value of constant
+  --> $DIR/dangling_raw_ptr.rs:17:1
+   |
+LL | const BAZ: Union = {
+   | ^^^^^^^^^^^^^^^^
+
+error: encountered dangling pointer in final value of constant
+  --> $DIR/dangling_raw_ptr.rs:23:1
+   |
+LL | const FOOMP: *const u32 = {
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/issues/issue-19244-1.rs b/tests/ui/consts/issue-19244-1.rs
similarity index 100%
rename from tests/ui/issues/issue-19244-1.rs
rename to tests/ui/consts/issue-19244-1.rs
diff --git a/tests/ui/issues/issue-19244-1.stderr b/tests/ui/consts/issue-19244-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-19244-1.stderr
rename to tests/ui/consts/issue-19244-1.stderr
diff --git a/tests/ui/issues/issue-19244-2.rs b/tests/ui/consts/issue-19244-2.rs
similarity index 100%
rename from tests/ui/issues/issue-19244-2.rs
rename to tests/ui/consts/issue-19244-2.rs
diff --git a/tests/ui/issues/issue-19244-2.stderr b/tests/ui/consts/issue-19244-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-19244-2.stderr
rename to tests/ui/consts/issue-19244-2.stderr
diff --git a/tests/ui/consts/miri_unleashed/mutable_references.rs b/tests/ui/consts/miri_unleashed/mutable_references.rs
index 8878e8e..07f1d70 100644
--- a/tests/ui/consts/miri_unleashed/mutable_references.rs
+++ b/tests/ui/consts/miri_unleashed/mutable_references.rs
@@ -5,11 +5,9 @@
 #![deny(const_eval_mutable_ptr_in_final_value)]
 use std::cell::UnsafeCell;
 
-// a test demonstrating what things we could allow with a smarter const qualification
-
+// This requires walking nested statics.
 static FOO: &&mut u32 = &&mut 42;
-//~^ ERROR encountered mutable pointer in final value of static
-//~| WARNING this was previously accepted by the compiler
+//~^ ERROR it is undefined behavior to use this value
 
 static BAR: &mut () = &mut ();
 //~^ ERROR encountered mutable pointer in final value of static
@@ -26,13 +24,10 @@
 }
 unsafe impl Sync for Meh {}
 static MEH: Meh = Meh { x: &UnsafeCell::new(42) };
-//~^ ERROR encountered mutable pointer in final value of static
-//~| WARNING this was previously accepted by the compiler
+//~^ ERROR it is undefined behavior to use this value
 
 static OH_YES: &mut i32 = &mut 42;
-//~^ ERROR encountered mutable pointer in final value of static
-//~| WARNING this was previously accepted by the compiler
-//~| ERROR it is undefined behavior to use this value
+//~^ ERROR it is undefined behavior to use this value
 
 fn main() {
     unsafe {
diff --git a/tests/ui/consts/miri_unleashed/mutable_references.stderr b/tests/ui/consts/miri_unleashed/mutable_references.stderr
index 7122eb6..b207e38 100644
--- a/tests/ui/consts/miri_unleashed/mutable_references.stderr
+++ b/tests/ui/consts/miri_unleashed/mutable_references.stderr
@@ -1,8 +1,19 @@
-error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:10:1
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/mutable_references.rs:9:1
    |
 LL | static FOO: &&mut u32 = &&mut 42;
-   | ^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>: encountered mutable reference or box pointing to read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
+
+error: encountered mutable pointer in final value of static
+  --> $DIR/mutable_references.rs:12:1
+   |
+LL | static BAR: &mut () = &mut ();
+   | ^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
@@ -13,16 +24,7 @@
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:14:1
-   |
-LL | static BAR: &mut () = &mut ();
-   | ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:20:1
+  --> $DIR/mutable_references.rs:18:1
    |
 LL | static BOO: &mut Foo<()> = &mut Foo(());
    | ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -30,26 +32,19 @@
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
 
-error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:28:1
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/mutable_references.rs:26:1
    |
 LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) };
-   | ^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^ constructing invalid value at .x.<deref>: encountered `UnsafeCell` in read-only memory
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:32:1
-   |
-LL | static OH_YES: &mut i32 = &mut 42;
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/mutable_references.rs:32:1
+  --> $DIR/mutable_references.rs:29:1
    |
 LL | static OH_YES: &mut i32 = &mut 42;
    | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
@@ -60,7 +55,7 @@
            }
 
 error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item
-  --> $DIR/mutable_references.rs:41:5
+  --> $DIR/mutable_references.rs:36:5
    |
 LL |     *OH_YES = 99;
    |     ^^^^^^^^^^^^ cannot assign
@@ -68,53 +63,38 @@
 warning: skipping const checks
    |
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:10:26
+  --> $DIR/mutable_references.rs:9:26
    |
 LL | static FOO: &&mut u32 = &&mut 42;
    |                          ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:14:23
+  --> $DIR/mutable_references.rs:12:23
    |
 LL | static BAR: &mut () = &mut ();
    |                       ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:20:28
+  --> $DIR/mutable_references.rs:18:28
    |
 LL | static BOO: &mut Foo<()> = &mut Foo(());
    |                            ^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:28:28
+  --> $DIR/mutable_references.rs:26:28
    |
 LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) };
    |                            ^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:32:27
+  --> $DIR/mutable_references.rs:29:27
    |
 LL | static OH_YES: &mut i32 = &mut 42;
    |                           ^^^^^^^
 
-error: aborting due to 7 previous errors; 1 warning emitted
+error: aborting due to 6 previous errors; 1 warning emitted
 
 Some errors have detailed explanations: E0080, E0594.
 For more information about an error, try `rustc --explain E0080`.
 Future incompatibility report: Future breakage diagnostic:
 error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:10:1
-   |
-LL | static FOO: &&mut u32 = &&mut 42;
-   | ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/mutable_references.rs:5:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:14:1
+  --> $DIR/mutable_references.rs:12:1
    |
 LL | static BAR: &mut () = &mut ();
    | ^^^^^^^^^^^^^^^^^^^
@@ -129,7 +109,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:20:1
+  --> $DIR/mutable_references.rs:18:1
    |
 LL | static BOO: &mut Foo<()> = &mut Foo(());
    | ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -142,33 +122,3 @@
 LL | #![deny(const_eval_mutable_ptr_in_final_value)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:28:1
-   |
-LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) };
-   | ^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/mutable_references.rs:5:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/mutable_references.rs:32:1
-   |
-LL | static OH_YES: &mut i32 = &mut 42;
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/mutable_references.rs:5:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
diff --git a/tests/ui/consts/miri_unleashed/mutable_references_err.rs b/tests/ui/consts/miri_unleashed/mutable_references_err.rs
index 97b8a71..a3da545 100644
--- a/tests/ui/consts/miri_unleashed/mutable_references_err.rs
+++ b/tests/ui/consts/miri_unleashed/mutable_references_err.rs
@@ -16,9 +16,7 @@
 // the following will never be ok! no interior mut behind consts, because
 // all allocs interned here will be marked immutable.
 const MUH: Meh = Meh {
-    //~^ ERROR encountered mutable pointer in final value of constant
-    //~| WARNING this was previously accepted by the compiler
-    //~| ERROR: it is undefined behavior to use this value
+    //~^ ERROR it is undefined behavior to use this value
     x: &UnsafeCell::new(42),
 };
 
@@ -29,9 +27,7 @@
 
 // Make sure we also catch this behind a type-erased `dyn Trait` reference.
 const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
-//~^ ERROR: mutable pointer in final value
-//~| WARNING this was previously accepted by the compiler
-//~| ERROR it is undefined behavior to use this value
+//~^ ERROR: it is undefined behavior to use this value
 
 // Make sure we also catch mutable references in values that shouldn't have them.
 static mut FOO: i32 = 0;
@@ -40,9 +36,7 @@
 //~| static
 
 const BLUNT: &mut i32 = &mut 42;
-//~^ ERROR: mutable pointer in final value
-//~| WARNING this was previously accepted by the compiler
-//~| ERROR it is undefined behavior to use this value
+//~^ ERROR: it is undefined behavior to use this value
 
 // Check for mutable references to read-only memory.
 static READONLY: i32 = 0;
diff --git a/tests/ui/consts/miri_unleashed/mutable_references_err.stderr b/tests/ui/consts/miri_unleashed/mutable_references_err.stderr
index 45615f5..d385b45 100644
--- a/tests/ui/consts/miri_unleashed/mutable_references_err.stderr
+++ b/tests/ui/consts/miri_unleashed/mutable_references_err.stderr
@@ -1,17 +1,3 @@
-error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:18:1
-   |
-LL | const MUH: Meh = Meh {
-   | ^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/mutable_references_err.rs:5:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/mutable_references_err.rs:18:1
    |
@@ -23,17 +9,8 @@
                HEX_DUMP
            }
 
-error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:31:1
-   |
-LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/mutable_references_err.rs:31:1
+  --> $DIR/mutable_references_err.rs:29:1
    |
 LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
    | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.<dyn-downcast>.x: encountered `UnsafeCell` in read-only memory
@@ -44,7 +21,7 @@
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/mutable_references_err.rs:38:1
+  --> $DIR/mutable_references_err.rs:34:1
    |
 LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
    | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const`
@@ -54,17 +31,8 @@
                HEX_DUMP
            }
 
-error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:42:1
-   |
-LL | const BLUNT: &mut i32 = &mut 42;
-   | ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/mutable_references_err.rs:42:1
+  --> $DIR/mutable_references_err.rs:38:1
    |
 LL | const BLUNT: &mut i32 = &mut 42;
    | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
@@ -75,7 +43,7 @@
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/mutable_references_err.rs:49:1
+  --> $DIR/mutable_references_err.rs:43:1
    |
 LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
@@ -86,7 +54,7 @@
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/mutable_references_err.rs:56:1
+  --> $DIR/mutable_references_err.rs:50:1
    |
 LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const`
@@ -97,28 +65,33 @@
            }
 
 note: erroneous constant encountered
-  --> $DIR/mutable_references_err.rs:58:34
+  --> $DIR/mutable_references_err.rs:52:34
    |
 LL | const READS_FROM_MUTABLE: i32 = *POINTS_TO_MUTABLE1;
    |                                  ^^^^^^^^^^^^^^^^^^
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/mutable_references_err.rs:60:43
+  --> $DIR/mutable_references_err.rs:54:43
    |
 LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
    |                                           ^^^^^^^^^^^^^ constant accesses mutable global memory
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:64:1
+  --> $DIR/mutable_references_err.rs:58:1
    |
 LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
+note: the lint level is defined here
+  --> $DIR/mutable_references_err.rs:5:9
+   |
+LL | #![deny(const_eval_mutable_ptr_in_final_value)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:68:1
+  --> $DIR/mutable_references_err.rs:62:1
    |
 LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -127,7 +100,7 @@
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:72:1
+  --> $DIR/mutable_references_err.rs:66:1
    |
 LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -136,7 +109,7 @@
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:85:1
+  --> $DIR/mutable_references_err.rs:79:1
    |
 LL | const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -145,7 +118,7 @@
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:89:1
+  --> $DIR/mutable_references_err.rs:83:1
    |
 LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x: &mut 42 as *mut _ as *const _ };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +127,7 @@
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:93:1
+  --> $DIR/mutable_references_err.rs:87:1
    |
 LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -165,132 +138,87 @@
 warning: skipping const checks
    |
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:22:8
+  --> $DIR/mutable_references_err.rs:20:8
    |
 LL |     x: &UnsafeCell::new(42),
    |        ^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:31:27
+  --> $DIR/mutable_references_err.rs:29:27
    |
 LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check for `const_refs_to_static` feature
-  --> $DIR/mutable_references_err.rs:38:40
+  --> $DIR/mutable_references_err.rs:34:40
    |
 LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
    |                                        ^^^
 help: skipping check for `const_mut_refs` feature
-  --> $DIR/mutable_references_err.rs:38:35
+  --> $DIR/mutable_references_err.rs:34:35
    |
 LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
    |                                   ^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:42:25
+  --> $DIR/mutable_references_err.rs:38:25
    |
 LL | const BLUNT: &mut i32 = &mut 42;
    |                         ^^^^^^^
 help: skipping check for `const_mut_refs` feature
-  --> $DIR/mutable_references_err.rs:49:49
+  --> $DIR/mutable_references_err.rs:43:49
    |
 LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check for `const_mut_refs` feature
-  --> $DIR/mutable_references_err.rs:49:49
+  --> $DIR/mutable_references_err.rs:43:49
    |
 LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check for `const_refs_to_static` feature
-  --> $DIR/mutable_references_err.rs:56:44
+  --> $DIR/mutable_references_err.rs:50:44
    |
 LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
    |                                            ^^^^^^^
 help: skipping check for `const_refs_to_static` feature
-  --> $DIR/mutable_references_err.rs:60:45
+  --> $DIR/mutable_references_err.rs:54:45
    |
 LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
    |                                             ^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:64:45
+  --> $DIR/mutable_references_err.rs:58:45
    |
 LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
    |                                             ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:68:46
+  --> $DIR/mutable_references_err.rs:62:46
    |
 LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _;
    |                                              ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:72:47
+  --> $DIR/mutable_references_err.rs:66:47
    |
 LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
    |                                               ^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:85:51
+  --> $DIR/mutable_references_err.rs:79:51
    |
 LL | const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
    |                                                   ^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:89:49
+  --> $DIR/mutable_references_err.rs:83:49
    |
 LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x: &mut 42 as *mut _ as *const _ };
    |                                                 ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references_err.rs:93:51
+  --> $DIR/mutable_references_err.rs:87:51
    |
 LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    |                                                   ^^^^^^
 
-error: aborting due to 16 previous errors; 1 warning emitted
+error: aborting due to 13 previous errors; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0080`.
 Future incompatibility report: Future breakage diagnostic:
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:18:1
-   |
-LL | const MUH: Meh = Meh {
-   | ^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/mutable_references_err.rs:5:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:31:1
-   |
-LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/mutable_references_err.rs:5:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:42:1
-   |
-LL | const BLUNT: &mut i32 = &mut 42;
-   | ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/mutable_references_err.rs:5:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:64:1
+  --> $DIR/mutable_references_err.rs:58:1
    |
 LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -305,7 +233,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:68:1
+  --> $DIR/mutable_references_err.rs:62:1
    |
 LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -320,7 +248,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:72:1
+  --> $DIR/mutable_references_err.rs:66:1
    |
 LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -335,7 +263,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:85:1
+  --> $DIR/mutable_references_err.rs:79:1
    |
 LL | const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -350,7 +278,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:89:1
+  --> $DIR/mutable_references_err.rs:83:1
    |
 LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x: &mut 42 as *mut _ as *const _ };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -365,7 +293,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references_err.rs:93:1
+  --> $DIR/mutable_references_err.rs:87:1
    |
 LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr b/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr
index 85ed6cb..1e554f6 100644
--- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr
+++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr
@@ -1,8 +1,52 @@
-error: encountered mutable pointer in final value of static
+error[E0080]: it is undefined behavior to use this value
   --> $DIR/static-no-inner-mut.rs:9:1
    |
 LL | static REF: &AtomicI32 = &AtomicI32::new(42);
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.v: encountered `UnsafeCell` in read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: 4, align: 4) {
+               ╾ALLOC0╼                                     │ ╾──╼
+           }
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/static-no-inner-mut.rs:12:1
+   |
+LL | static REFMUT: &mut i32 = &mut 0;
+   | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: 4, align: 4) {
+               ╾ALLOC1╼                                     │ ╾──╼
+           }
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/static-no-inner-mut.rs:16:1
+   |
+LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
+   | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.v: encountered `UnsafeCell` in read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: 4, align: 4) {
+               ╾ALLOC2╼                                     │ ╾──╼
+           }
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/static-no-inner-mut.rs:18:1
+   |
+LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: 4, align: 4) {
+               ╾ALLOC3╼                                     │ ╾──╼
+           }
+
+error: encountered mutable pointer in final value of static
+  --> $DIR/static-no-inner-mut.rs:34:1
+   |
+LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
@@ -13,65 +57,7 @@
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:13:1
-   |
-LL | static REFMUT: &mut i32 = &mut 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/static-no-inner-mut.rs:13:1
-   |
-LL | static REFMUT: &mut i32 = &mut 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 4, align: 4) {
-               ╾ALLOC0╼                                     │ ╾──╼
-           }
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:19:1
-   |
-LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:23:1
-   |
-LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/static-no-inner-mut.rs:23:1
-   |
-LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 4, align: 4) {
-               ╾ALLOC1╼                                     │ ╾──╼
-           }
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:41:1
-   |
-LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:45:1
+  --> $DIR/static-no-inner-mut.rs:38:1
    |
 LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -80,7 +66,7 @@
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
 
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:49:1
+  --> $DIR/static-no-inner-mut.rs:42:1
    |
 LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -96,102 +82,42 @@
 LL | static REF: &AtomicI32 = &AtomicI32::new(42);
    |                          ^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:13:27
+  --> $DIR/static-no-inner-mut.rs:12:27
    |
 LL | static REFMUT: &mut i32 = &mut 0;
    |                           ^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:19:56
+  --> $DIR/static-no-inner-mut.rs:16:56
    |
 LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
    |                                                        ^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:23:44
+  --> $DIR/static-no-inner-mut.rs:18:44
    |
 LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
    |                                            ^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:41:52
+  --> $DIR/static-no-inner-mut.rs:34:52
    |
 LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
    |                                                    ^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:45:51
+  --> $DIR/static-no-inner-mut.rs:38:51
    |
 LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
    |                                                   ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:49:52
+  --> $DIR/static-no-inner-mut.rs:42:52
    |
 LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    |                                                    ^^^^^^
 
-error: aborting due to 9 previous errors; 1 warning emitted
+error: aborting due to 7 previous errors; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0080`.
 Future incompatibility report: Future breakage diagnostic:
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:9:1
-   |
-LL | static REF: &AtomicI32 = &AtomicI32::new(42);
-   | ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/static-no-inner-mut.rs:6:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:13:1
-   |
-LL | static REFMUT: &mut i32 = &mut 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/static-no-inner-mut.rs:6:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:19:1
-   |
-LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/static-no-inner-mut.rs:6:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:23:1
-   |
-LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/static-no-inner-mut.rs:6:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:41:1
+  --> $DIR/static-no-inner-mut.rs:34:1
    |
 LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -206,7 +132,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:45:1
+  --> $DIR/static-no-inner-mut.rs:38:1
    |
 LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -221,7 +147,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:49:1
+  --> $DIR/static-no-inner-mut.rs:42:1
    |
 LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr b/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr
index 5aa1cd0..84ed631 100644
--- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr
+++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr
@@ -1,8 +1,52 @@
-error: encountered mutable pointer in final value of static
+error[E0080]: it is undefined behavior to use this value
   --> $DIR/static-no-inner-mut.rs:9:1
    |
 LL | static REF: &AtomicI32 = &AtomicI32::new(42);
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.v: encountered `UnsafeCell` in read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: 8, align: 8) {
+               ╾ALLOC0╼                         │ ╾──────╼
+           }
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/static-no-inner-mut.rs:12:1
+   |
+LL | static REFMUT: &mut i32 = &mut 0;
+   | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: 8, align: 8) {
+               ╾ALLOC1╼                         │ ╾──────╼
+           }
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/static-no-inner-mut.rs:16:1
+   |
+LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
+   | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.v: encountered `UnsafeCell` in read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: 8, align: 8) {
+               ╾ALLOC2╼                         │ ╾──────╼
+           }
+
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/static-no-inner-mut.rs:18:1
+   |
+LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: 8, align: 8) {
+               ╾ALLOC3╼                         │ ╾──────╼
+           }
+
+error: encountered mutable pointer in final value of static
+  --> $DIR/static-no-inner-mut.rs:34:1
+   |
+LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
@@ -13,65 +57,7 @@
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:13:1
-   |
-LL | static REFMUT: &mut i32 = &mut 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/static-no-inner-mut.rs:13:1
-   |
-LL | static REFMUT: &mut i32 = &mut 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 8, align: 8) {
-               ╾ALLOC0╼                         │ ╾──────╼
-           }
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:19:1
-   |
-LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:23:1
-   |
-LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error[E0080]: it is undefined behavior to use this value
-  --> $DIR/static-no-inner-mut.rs:23:1
-   |
-LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: 8, align: 8) {
-               ╾ALLOC1╼                         │ ╾──────╼
-           }
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:41:1
-   |
-LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:45:1
+  --> $DIR/static-no-inner-mut.rs:38:1
    |
 LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -80,7 +66,7 @@
    = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
 
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:49:1
+  --> $DIR/static-no-inner-mut.rs:42:1
    |
 LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -96,102 +82,42 @@
 LL | static REF: &AtomicI32 = &AtomicI32::new(42);
    |                          ^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:13:27
+  --> $DIR/static-no-inner-mut.rs:12:27
    |
 LL | static REFMUT: &mut i32 = &mut 0;
    |                           ^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:19:56
+  --> $DIR/static-no-inner-mut.rs:16:56
    |
 LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
    |                                                        ^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:23:44
+  --> $DIR/static-no-inner-mut.rs:18:44
    |
 LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
    |                                            ^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:41:52
+  --> $DIR/static-no-inner-mut.rs:34:52
    |
 LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
    |                                                    ^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:45:51
+  --> $DIR/static-no-inner-mut.rs:38:51
    |
 LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
    |                                                   ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/static-no-inner-mut.rs:49:52
+  --> $DIR/static-no-inner-mut.rs:42:52
    |
 LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    |                                                    ^^^^^^
 
-error: aborting due to 9 previous errors; 1 warning emitted
+error: aborting due to 7 previous errors; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0080`.
 Future incompatibility report: Future breakage diagnostic:
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:9:1
-   |
-LL | static REF: &AtomicI32 = &AtomicI32::new(42);
-   | ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/static-no-inner-mut.rs:6:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:13:1
-   |
-LL | static REFMUT: &mut i32 = &mut 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/static-no-inner-mut.rs:6:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:19:1
-   |
-LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/static-no-inner-mut.rs:6:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:23:1
-   |
-LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #122153 <https://github.com/rust-lang/rust/issues/122153>
-note: the lint level is defined here
-  --> $DIR/static-no-inner-mut.rs:6:9
-   |
-LL | #![deny(const_eval_mutable_ptr_in_final_value)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:41:1
+  --> $DIR/static-no-inner-mut.rs:34:1
    |
 LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -206,7 +132,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:45:1
+  --> $DIR/static-no-inner-mut.rs:38:1
    |
 LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -221,7 +147,7 @@
 
 Future breakage diagnostic:
 error: encountered mutable pointer in final value of static
-  --> $DIR/static-no-inner-mut.rs:49:1
+  --> $DIR/static-no-inner-mut.rs:42:1
    |
 LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs b/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs
index e82ca50..8107603 100644
--- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs
+++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs
@@ -7,23 +7,16 @@
 use std::sync::atomic::*;
 
 static REF: &AtomicI32 = &AtomicI32::new(42);
-//~^ ERROR mutable pointer in final value
-//~| WARNING this was previously accepted by the compiler
+//~^ ERROR it is undefined behavior to use this value
 
 static REFMUT: &mut i32 = &mut 0;
-//~^ ERROR mutable pointer in final value
-//~| WARNING this was previously accepted by the compiler
-//~| ERROR it is undefined behavior to use this value
+//~^ ERROR it is undefined behavior to use this value
 
 // Different way of writing this that avoids promotion.
 static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
-//~^ ERROR mutable pointer in final value
-//~| WARNING this was previously accepted by the compiler
-
+//~^ ERROR it is undefined behavior to use this value
 static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
-//~^ ERROR mutable pointer in final value
-//~| WARNING this was previously accepted by the compiler
-//~| ERROR it is undefined behavior to use this value
+//~^ ERROR it is undefined behavior to use this value
 
 // This one is obvious, since it is non-Sync. (It also suppresses the other errors, so it is
 // commented out.)
diff --git a/tests/ui/consts/tuple-struct-constructors.rs b/tests/ui/consts/tuple-struct-constructors.rs
index d2f25ae..e645b57 100644
--- a/tests/ui/consts/tuple-struct-constructors.rs
+++ b/tests/ui/consts/tuple-struct-constructors.rs
@@ -1,7 +1,6 @@
 //@ run-pass
 //
 // https://github.com/rust-lang/rust/issues/41898
-#![feature(generic_nonzero)]
 
 use std::num::NonZero;
 
diff --git a/tests/ui/debuginfo/msvc-strip-debuginfo.rs b/tests/ui/debuginfo/msvc-strip-debuginfo.rs
new file mode 100644
index 0000000..d5f516e
--- /dev/null
+++ b/tests/ui/debuginfo/msvc-strip-debuginfo.rs
@@ -0,0 +1,26 @@
+//@ compile-flags: -C strip=debuginfo
+//@ only-msvc
+//@ run-pass
+
+use std::path::Path;
+
+pub fn is_related_pdb<P: AsRef<Path>>(path: &P, exe: &P) -> bool {
+    let (exe, path) = (exe.as_ref(), path.as_ref());
+
+    path.extension()
+        .map(|x| x.to_ascii_lowercase())
+        .is_some_and(|x| x == "pdb")
+        && path.file_stem() == exe.file_stem()
+}
+
+pub fn main() {
+    let curr_exe = std::env::current_exe().unwrap();
+    let curr_dir = curr_exe.parent().unwrap();
+
+    let entries = std::fs::read_dir(curr_dir).unwrap();
+
+    assert!(entries
+        .map_while(|x| x.ok())
+        .find(|x| is_related_pdb(&x.path(), &curr_exe))
+        .is_some());
+}
diff --git a/tests/ui/debuginfo/msvc-strip-symbols.rs b/tests/ui/debuginfo/msvc-strip-symbols.rs
new file mode 100644
index 0000000..198c949
--- /dev/null
+++ b/tests/ui/debuginfo/msvc-strip-symbols.rs
@@ -0,0 +1,26 @@
+//@ compile-flags: -C strip=symbols
+//@ only-msvc
+//@ run-pass
+
+use std::path::Path;
+
+pub fn is_related_pdb<P: AsRef<Path>>(path: &P, exe: &P) -> bool {
+    let (exe, path) = (exe.as_ref(), path.as_ref());
+
+    path.extension()
+        .map(|x| x.to_ascii_lowercase())
+        .is_some_and(|x| x == "pdb")
+        && path.file_stem() == exe.file_stem()
+}
+
+pub fn main() {
+    let curr_exe = std::env::current_exe().unwrap();
+    let curr_dir = curr_exe.parent().unwrap();
+
+    let entries = std::fs::read_dir(curr_dir).unwrap();
+
+    assert!(entries
+        .map_while(|x| x.ok())
+        .find(|x| is_related_pdb(&x.path(), &curr_exe))
+        .is_some());
+}
diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.fixed b/tests/ui/deref-patterns/issue-71676-1.fixed
similarity index 100%
rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.fixed
rename to tests/ui/deref-patterns/issue-71676-1.fixed
diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.rs b/tests/ui/deref-patterns/issue-71676-1.rs
similarity index 100%
rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.rs
rename to tests/ui/deref-patterns/issue-71676-1.rs
diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.stderr b/tests/ui/deref-patterns/issue-71676-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.stderr
rename to tests/ui/deref-patterns/issue-71676-1.stderr
diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.rs b/tests/ui/deref-patterns/issue-71676-2.rs
similarity index 100%
rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.rs
rename to tests/ui/deref-patterns/issue-71676-2.rs
diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.stderr b/tests/ui/deref-patterns/issue-71676-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.stderr
rename to tests/ui/deref-patterns/issue-71676-2.stderr
diff --git a/tests/ui/issues/auxiliary/issue-19340-1.rs b/tests/ui/enum/auxiliary/issue-19340-1.rs
similarity index 100%
rename from tests/ui/issues/auxiliary/issue-19340-1.rs
rename to tests/ui/enum/auxiliary/issue-19340-1.rs
diff --git a/tests/ui/issues/issue-19340-1.rs b/tests/ui/enum/issue-19340-1.rs
similarity index 100%
rename from tests/ui/issues/issue-19340-1.rs
rename to tests/ui/enum/issue-19340-1.rs
diff --git a/tests/ui/issues/issue-19340-2.rs b/tests/ui/enum/issue-19340-2.rs
similarity index 100%
rename from tests/ui/issues/issue-19340-2.rs
rename to tests/ui/enum/issue-19340-2.rs
diff --git a/tests/ui/issues/issue-23304-1.rs b/tests/ui/enum/issue-23304-1.rs
similarity index 100%
rename from tests/ui/issues/issue-23304-1.rs
rename to tests/ui/enum/issue-23304-1.rs
diff --git a/tests/ui/issues/issue-23304-2.rs b/tests/ui/enum/issue-23304-2.rs
similarity index 100%
rename from tests/ui/issues/issue-23304-2.rs
rename to tests/ui/enum/issue-23304-2.rs
diff --git a/tests/ui/issues/issue-22933-1.rs b/tests/ui/expr/issue-22933-1.rs
similarity index 100%
rename from tests/ui/issues/issue-22933-1.rs
rename to tests/ui/expr/issue-22933-1.rs
diff --git a/tests/ui/issues/issue-22933-2.rs b/tests/ui/expr/issue-22933-2.rs
similarity index 100%
rename from tests/ui/issues/issue-22933-2.rs
rename to tests/ui/expr/issue-22933-2.rs
diff --git a/tests/ui/issues/issue-22933-2.stderr b/tests/ui/expr/issue-22933-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-22933-2.stderr
rename to tests/ui/expr/issue-22933-2.stderr
diff --git a/tests/ui/extern-flag/auxiliary/panic_handler.rs b/tests/ui/extern-flag/auxiliary/panic_handler.rs
index a625761..5ca32fa 100644
--- a/tests/ui/extern-flag/auxiliary/panic_handler.rs
+++ b/tests/ui/extern-flag/auxiliary/panic_handler.rs
@@ -5,7 +5,9 @@
 // Rust programs link necessary system libraries via `#[link()]`
 // attributes in the `libc` crate. `libc` is a dependency of `std`,
 // but as we are `#![no_std]`, we need to include it manually.
+// Except on windows-msvc.
 #![feature(rustc_private)]
+#[cfg(not(all(windows, target_env = "msvc")))]
 extern crate libc;
 
 #[panic_handler]
diff --git a/tests/ui/fmt/no-inline-literals-out-of-range.rs b/tests/ui/fmt/no-inline-literals-out-of-range.rs
new file mode 100644
index 0000000..d2532cd
--- /dev/null
+++ b/tests/ui/fmt/no-inline-literals-out-of-range.rs
@@ -0,0 +1,14 @@
+//@ only-64bit
+
+fn main() {
+    format_args!("{}", 0x8f_i8); // issue #115423
+    //~^ ERROR literal out of range for `i8`
+    format_args!("{}", 0xffff_ffff_u8); // issue #116633
+    //~^ ERROR literal out of range for `u8`
+    format_args!("{}", 0xffff_ffff_ffff_ffff_ffff_usize);
+    //~^ ERROR literal out of range for `usize`
+    format_args!("{}", 0x8000_0000_0000_0000_isize);
+    //~^ ERROR literal out of range for `isize`
+    format_args!("{}", 0xffff_ffff); // treat unsuffixed literals as i32
+    //~^ ERROR literal out of range for `i32`
+}
diff --git a/tests/ui/fmt/no-inline-literals-out-of-range.stderr b/tests/ui/fmt/no-inline-literals-out-of-range.stderr
new file mode 100644
index 0000000..78ec488
--- /dev/null
+++ b/tests/ui/fmt/no-inline-literals-out-of-range.stderr
@@ -0,0 +1,56 @@
+error: literal out of range for `i8`
+  --> $DIR/no-inline-literals-out-of-range.rs:4:24
+   |
+LL |     format_args!("{}", 0x8f_i8); // issue #115423
+   |                        ^^^^^^^
+   |
+   = note: the literal `0x8f_i8` (decimal `143`) does not fit into the type `i8` and will become `-113i8`
+   = note: `#[deny(overflowing_literals)]` on by default
+help: consider using the type `u8` instead
+   |
+LL |     format_args!("{}", 0x8f_u8); // issue #115423
+   |                        ~~~~~~~
+help: to use as a negative number (decimal `-113`), consider using the type `u8` for the literal and cast it to `i8`
+   |
+LL |     format_args!("{}", 0x8f_u8 as i8); // issue #115423
+   |                        ~~~~~~~~~~~~~
+
+error: literal out of range for `u8`
+  --> $DIR/no-inline-literals-out-of-range.rs:6:24
+   |
+LL |     format_args!("{}", 0xffff_ffff_u8); // issue #116633
+   |                        ^^^^^^^^^^^^^^ help: consider using the type `u32` instead: `0xffff_ffff_u32`
+   |
+   = note: the literal `0xffff_ffff_u8` (decimal `4294967295`) does not fit into the type `u8` and will become `255u8`
+
+error: literal out of range for `usize`
+  --> $DIR/no-inline-literals-out-of-range.rs:8:24
+   |
+LL |     format_args!("{}", 0xffff_ffff_ffff_ffff_ffff_usize);
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the literal `0xffff_ffff_ffff_ffff_ffff_usize` (decimal `1208925819614629174706175`) does not fit into the type `usize` and will become `18446744073709551615usize`
+
+error: literal out of range for `isize`
+  --> $DIR/no-inline-literals-out-of-range.rs:10:24
+   |
+LL |     format_args!("{}", 0x8000_0000_0000_0000_isize);
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the literal `0x8000_0000_0000_0000_isize` (decimal `9223372036854775808`) does not fit into the type `isize` and will become `-9223372036854775808isize`
+
+error: literal out of range for `i32`
+  --> $DIR/no-inline-literals-out-of-range.rs:12:24
+   |
+LL |     format_args!("{}", 0xffff_ffff); // treat unsuffixed literals as i32
+   |                        ^^^^^^^^^^^
+   |
+   = note: the literal `0xffff_ffff` (decimal `4294967295`) does not fit into the type `i32` and will become `-1i32`
+   = help: consider using the type `u32` instead
+help: to use as a negative number (decimal `-1`), consider using the type `u32` for the literal and cast it to `i32`
+   |
+LL |     format_args!("{}", 0xffff_ffffu32 as i32); // treat unsuffixed literals as i32
+   |                        ~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 5 previous errors
+
diff --git a/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs b/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs
index 3a173ef..10c2a81 100644
--- a/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs
+++ b/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs
@@ -6,6 +6,10 @@
     fn foo() -> impl Deref<Target = impl Deref<Target = impl Sized>> {
         &&()
     }
+
+    fn bar() -> impl Deref<Target = Option<impl Sized>> {
+        &Some(())
+    }
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs
new file mode 100644
index 0000000..35b28d0
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs
@@ -0,0 +1,38 @@
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+trait Tr {
+    type Assoc;
+}
+
+struct W<'a>(&'a ());
+
+impl Tr for W<'_> {
+    type Assoc = ();
+}
+
+// The normal way of capturing `'a`...
+impl<'a> W<'a> {
+    fn good1() -> impl use<'a> Into<<W<'a> as Tr>::Assoc> {}
+}
+
+// This ensures that we don't error when we capture the *parent* copy of `'a`,
+// since the opaque captures that rather than the duplicated `'a` lifetime
+// synthesized from mentioning `'a` directly in the bounds.
+impl<'a> W<'a> {
+    fn good2() -> impl use<'a> Into<<Self as Tr>::Assoc> {}
+}
+
+// The normal way of capturing `'a`... but not mentioned in the bounds.
+impl<'a> W<'a> {
+    fn bad1() -> impl use<> Into<<W<'a> as Tr>::Assoc> {}
+    //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+}
+
+// But also make sure that we error here...
+impl<'a> W<'a> {
+    fn bad2() -> impl use<> Into<<Self as Tr>::Assoc> {}
+    //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr
new file mode 100644
index 0000000..13aaa99
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr
@@ -0,0 +1,27 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/capture-parent-arg.rs:1:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+  --> $DIR/capture-parent-arg.rs:28:37
+   |
+LL | impl<'a> W<'a> {
+   |      -- this lifetime parameter is captured
+LL |     fn bad1() -> impl use<> Into<<W<'a> as Tr>::Assoc> {}
+   |                  -------------------^^---------------- lifetime captured due to being mentioned in the bounds of the `impl Trait`
+
+error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+  --> $DIR/capture-parent-arg.rs:34:18
+   |
+LL | impl<'a> W<'a> {
+   |      -- this lifetime parameter is captured
+LL |     fn bad2() -> impl use<> Into<<Self as Tr>::Assoc> {}
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime captured due to being mentioned in the bounds of the `impl Trait`
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
index 9c99f2b..3f78e7c 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
@@ -7,11 +7,13 @@
    = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
-error: `impl Trait` must mention all const parameters in scope
-  --> $DIR/forgot-to-capture-const.rs:4:13
+error: `impl Trait` must mention all const parameters in scope in `use<...>`
+  --> $DIR/forgot-to-capture-const.rs:4:34
    |
 LL | fn constant<const C: usize>() -> impl use<> Sized {}
-   |             ^^^^^^^^^^^^^^       ---------------- const parameter is implicitly captured by this `impl Trait`
+   |             --------------       ^^^^^^^^^^^^^^^^
+   |             |
+   |             const parameter is implicitly captured by this `impl Trait`
    |
    = note: currently, all const parameters are required to be mentioned in the precise captures list
 
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
index 6eaff01..d359ea5 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
@@ -5,8 +5,8 @@
 //~^ ERROR `impl Trait` must mention all type parameters in scope
 
 trait Foo {
-//~^ ERROR `impl Trait` must mention all type parameters in scope
     fn bar() -> impl use<> Sized;
+    //~^ ERROR `impl Trait` must mention the `Self` type of the trait
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
index a8eb454..26994d0 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
@@ -7,22 +7,23 @@
    = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
-error: `impl Trait` must mention all type parameters in scope
-  --> $DIR/forgot-to-capture-type.rs:4:15
+error: `impl Trait` must mention all type parameters in scope in `use<...>`
+  --> $DIR/forgot-to-capture-type.rs:4:23
    |
 LL | fn type_param<T>() -> impl use<> Sized {}
-   |               ^       ---------------- type parameter is implicitly captured by this `impl Trait`
+   |               -       ^^^^^^^^^^^^^^^^
+   |               |
+   |               type parameter is implicitly captured by this `impl Trait`
    |
    = note: currently, all type parameters are required to be mentioned in the precise captures list
 
-error: `impl Trait` must mention all type parameters in scope
-  --> $DIR/forgot-to-capture-type.rs:7:1
+error: `impl Trait` must mention the `Self` type of the trait in `use<...>`
+  --> $DIR/forgot-to-capture-type.rs:8:17
    |
 LL | trait Foo {
-   | ^^^^^^^^^
-LL |
+   | --------- `Self` type parameter is implicitly captured by this `impl Trait`
 LL |     fn bar() -> impl use<> Sized;
-   |                 ---------------- type parameter is implicitly captured by this `impl Trait`
+   |                 ^^^^^^^^^^^^^^^^
    |
    = note: currently, all type parameters are required to be mentioned in the precise captures list
 
diff --git a/tests/ui/inference/hint-closure-signature-119266.rs b/tests/ui/inference/hint-closure-signature-119266.rs
new file mode 100644
index 0000000..35be600
--- /dev/null
+++ b/tests/ui/inference/hint-closure-signature-119266.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let x = |a: u8, b: (usize, u32), c: fn() -> char| -> String { "I love beans.".to_string() };
+    //~^ NOTE: the found closure
+
+    let x: fn(i32) = x;
+    //~^ ERROR: 5:22: 5:23: mismatched types [E0308]
+    //~| NOTE: incorrect number of function parameters
+    //~| NOTE: expected due to this
+    //~| NOTE: expected fn pointer `fn(i32)`
+    //~| NOTE: closure has signature: `fn(u8, (usize, u32), fn() -> char) -> String`
+}
diff --git a/tests/ui/inference/hint-closure-signature-119266.stderr b/tests/ui/inference/hint-closure-signature-119266.stderr
new file mode 100644
index 0000000..f0b9579
--- /dev/null
+++ b/tests/ui/inference/hint-closure-signature-119266.stderr
@@ -0,0 +1,18 @@
+error[E0308]: mismatched types
+  --> $DIR/hint-closure-signature-119266.rs:5:22
+   |
+LL |     let x = |a: u8, b: (usize, u32), c: fn() -> char| -> String { "I love beans.".to_string() };
+   |             --------------------------------------------------- the found closure
+...
+LL |     let x: fn(i32) = x;
+   |            -------   ^ incorrect number of function parameters
+   |            |
+   |            expected due to this
+   |
+   = note: expected fn pointer `fn(i32)`
+                 found closure `{closure@$DIR/hint-closure-signature-119266.rs:2:13: 2:64}`
+   = note: closure has signature: `fn(u8, (usize, u32), fn() -> char) -> String`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/instrument-coverage/coverage-options.bad.stderr b/tests/ui/instrument-coverage/coverage-options.bad.stderr
index ca82dc5..f6e5421 100644
--- a/tests/ui/instrument-coverage/coverage-options.bad.stderr
+++ b/tests/ui/instrument-coverage/coverage-options.bad.stderr
@@ -1,2 +1,2 @@
-error: incorrect value `bad` for unstable option `coverage-options` - `branch` or `no-branch` was expected
+error: incorrect value `bad` for unstable option `coverage-options` - either  `no-branch`, `branch` or `mcdc` was expected
 
diff --git a/tests/ui/instrument-coverage/coverage-options.rs b/tests/ui/instrument-coverage/coverage-options.rs
index a62e055..50c01ed 100644
--- a/tests/ui/instrument-coverage/coverage-options.rs
+++ b/tests/ui/instrument-coverage/coverage-options.rs
@@ -8,7 +8,13 @@
 //@ [no-branch] check-pass
 //@ [no-branch] compile-flags: -Zcoverage-options=no-branch
 
+//@ [mcdc] check-pass
+//@ [mcdc] compile-flags: -Zcoverage-options=mcdc
+
 //@ [bad] check-fail
 //@ [bad] compile-flags: -Zcoverage-options=bad
 
+//@ [conflict] check-fail
+//@ [conflict] compile-flags: -Zcoverage-options=no-branch,mcdc
+
 fn main() {}
diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs
index b1ac752..67b9832 100644
--- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs
+++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs
@@ -7,7 +7,6 @@
 //
 // This test checks panic emitted from `mem::{uninitialized,zeroed}`.
 #![allow(deprecated, invalid_value)]
-#![feature(generic_nonzero)]
 #![feature(never_type)]
 
 use std::{
diff --git a/tests/ui/issues/issue-64593.rs b/tests/ui/issues/issue-64593.rs
index 091c3a2..e28b957 100644
--- a/tests/ui/issues/issue-64593.rs
+++ b/tests/ui/issues/issue-64593.rs
@@ -1,6 +1,5 @@
 //@ check-pass
 #![deny(improper_ctypes)]
-#![feature(generic_nonzero)]
 
 pub struct Error(std::num::NonZero<u32>);
 
diff --git a/tests/ui/layout/unsafe-cell-hides-niche.rs b/tests/ui/layout/unsafe-cell-hides-niche.rs
index 568eb81..fe51c99 100644
--- a/tests/ui/layout/unsafe-cell-hides-niche.rs
+++ b/tests/ui/layout/unsafe-cell-hides-niche.rs
@@ -6,7 +6,6 @@
 //@ check-pass
 //@ compile-flags: --crate-type=lib
 //@ only-x86
-#![feature(generic_nonzero)]
 #![feature(repr_simd)]
 
 use std::cell::{UnsafeCell, RefCell, Cell};
diff --git a/tests/ui/layout/zero-sized-array-enum-niche.rs b/tests/ui/layout/zero-sized-array-enum-niche.rs
index 058f592..0c37c0f 100644
--- a/tests/ui/layout/zero-sized-array-enum-niche.rs
+++ b/tests/ui/layout/zero-sized-array-enum-niche.rs
@@ -1,6 +1,5 @@
 //@ normalize-stderr-test "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN"
 #![crate_type = "lib"]
-#![feature(generic_nonzero)]
 #![feature(rustc_attrs)]
 
 // Various tests around the behavior of zero-sized arrays and
diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr
index af04912..ee34cfd 100644
--- a/tests/ui/layout/zero-sized-array-enum-niche.stderr
+++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr
@@ -98,7 +98,7 @@
            max_repr_align: None,
            unadjusted_abi_align: Align(4 bytes),
        }
-  --> $DIR/zero-sized-array-enum-niche.rs:14:1
+  --> $DIR/zero-sized-array-enum-niche.rs:13:1
    |
 LL | type AlignedResult = Result<[u32; 0], bool>;
    | ^^^^^^^^^^^^^^^^^^
@@ -227,7 +227,7 @@
            max_repr_align: None,
            unadjusted_abi_align: Align(4 bytes),
        }
-  --> $DIR/zero-sized-array-enum-niche.rs:22:1
+  --> $DIR/zero-sized-array-enum-niche.rs:21:1
    |
 LL | enum MultipleAlignments {
    | ^^^^^^^^^^^^^^^^^^^^^^^
@@ -332,7 +332,7 @@
            max_repr_align: None,
            unadjusted_abi_align: Align(4 bytes),
        }
-  --> $DIR/zero-sized-array-enum-niche.rs:38:1
+  --> $DIR/zero-sized-array-enum-niche.rs:37:1
    |
 LL | type NicheLosesToTagged = Result<[u32; 0], Packed<std::num::NonZero<u16>>>;
    | ^^^^^^^^^^^^^^^^^^^^^^^
@@ -441,7 +441,7 @@
            max_repr_align: None,
            unadjusted_abi_align: Align(4 bytes),
        }
-  --> $DIR/zero-sized-array-enum-niche.rs:45:1
+  --> $DIR/zero-sized-array-enum-niche.rs:44:1
    |
 LL | type NicheWinsOverTagged = Result<[u32; 0], Packed<U16IsZero>>;
    | ^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/lexer/emoji-literal-prefix.rs b/tests/ui/lexer/emoji-literal-prefix.rs
new file mode 100644
index 0000000..ccc8d48
--- /dev/null
+++ b/tests/ui/lexer/emoji-literal-prefix.rs
@@ -0,0 +1,8 @@
+macro_rules! lexes {($($_:tt)*) => {}}
+
+lexes!(🐛#); //~ ERROR identifiers cannot contain emoji
+lexes!(🐛"foo");
+lexes!(🐛'q');
+lexes!(🐛'q);
+
+fn main() {}
diff --git a/tests/ui/lexer/emoji-literal-prefix.stderr b/tests/ui/lexer/emoji-literal-prefix.stderr
new file mode 100644
index 0000000..25aafed
--- /dev/null
+++ b/tests/ui/lexer/emoji-literal-prefix.stderr
@@ -0,0 +1,14 @@
+error: identifiers cannot contain emoji: `🐛`
+  --> $DIR/emoji-literal-prefix.rs:3:8
+   |
+LL | lexes!(🐛#);
+   |        ^^
+LL | lexes!(🐛"foo");
+   |        ^^
+LL | lexes!(🐛'q');
+   |        ^^
+LL | lexes!(🐛'q);
+   |        ^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs
index cb63af0..728dfab 100644
--- a/tests/ui/lint/clashing-extern-fn.rs
+++ b/tests/ui/lint/clashing-extern-fn.rs
@@ -2,7 +2,6 @@
 //@ aux-build:external_extern_fn.rs
 #![crate_type = "lib"]
 #![warn(clashing_extern_declarations)]
-#![feature(generic_nonzero)]
 
 mod redeclared_different_signature {
     mod a {
diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr
index 86ee789..43c8cde 100644
--- a/tests/ui/lint/clashing-extern-fn.stderr
+++ b/tests/ui/lint/clashing-extern-fn.stderr
@@ -1,5 +1,5 @@
 warning: `extern` block uses type `Option<TransparentNoNiche>`, which is not FFI-safe
-  --> $DIR/clashing-extern-fn.rs:430:55
+  --> $DIR/clashing-extern-fn.rs:429:55
    |
 LL |             fn hidden_niche_transparent_no_niche() -> Option<TransparentNoNiche>;
    |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -9,7 +9,7 @@
    = note: `#[warn(improper_ctypes)]` on by default
 
 warning: `extern` block uses type `Option<UnsafeCell<NonZero<usize>>>`, which is not FFI-safe
-  --> $DIR/clashing-extern-fn.rs:434:46
+  --> $DIR/clashing-extern-fn.rs:433:46
    |
 LL |             fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZero<usize>>>;
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -18,7 +18,7 @@
    = note: enum has no representation hint
 
 warning: `clash` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:15:13
+  --> $DIR/clashing-extern-fn.rs:14:13
    |
 LL |             fn clash(x: u8);
    |             --------------- `clash` previously declared here
@@ -35,7 +35,7 @@
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: `extern_link_name` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:53:9
+  --> $DIR/clashing-extern-fn.rs:52:9
    |
 LL |     #[link_name = "extern_link_name"]
    |     --------------------------------- `extern_link_name` previously declared here
@@ -47,7 +47,7 @@
               found `unsafe extern "C" fn(u32)`
 
 warning: `some_other_extern_link_name` redeclares `some_other_new_name` with a different signature
-  --> $DIR/clashing-extern-fn.rs:56:9
+  --> $DIR/clashing-extern-fn.rs:55:9
    |
 LL |     fn some_other_new_name(x: i16);
    |     ------------------------------ `some_other_new_name` previously declared here
@@ -59,7 +59,7 @@
               found `unsafe extern "C" fn(u32)`
 
 warning: `other_both_names_different` redeclares `link_name_same` with a different signature
-  --> $DIR/clashing-extern-fn.rs:60:9
+  --> $DIR/clashing-extern-fn.rs:59:9
    |
 LL |     #[link_name = "link_name_same"]
    |     ------------------------------- `link_name_same` previously declared here
@@ -71,7 +71,7 @@
               found `unsafe extern "C" fn(u32)`
 
 warning: `different_mod` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:73:9
+  --> $DIR/clashing-extern-fn.rs:72:9
    |
 LL |         fn different_mod(x: u8);
    |         ----------------------- `different_mod` previously declared here
@@ -83,7 +83,7 @@
               found `unsafe extern "C" fn(u64)`
 
 warning: `variadic_decl` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:83:9
+  --> $DIR/clashing-extern-fn.rs:82:9
    |
 LL |     fn variadic_decl(x: u8, ...);
    |     ---------------------------- `variadic_decl` previously declared here
@@ -95,7 +95,7 @@
               found `unsafe extern "C" fn(u8)`
 
 warning: `weigh_banana` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:143:13
+  --> $DIR/clashing-extern-fn.rs:142:13
    |
 LL |             fn weigh_banana(count: *const Banana) -> u64;
    |             -------------------------------------------- `weigh_banana` previously declared here
@@ -107,7 +107,7 @@
               found `unsafe extern "C" fn(*const three::Banana) -> u64`
 
 warning: `draw_point` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:172:13
+  --> $DIR/clashing-extern-fn.rs:171:13
    |
 LL |             fn draw_point(p: Point);
    |             ----------------------- `draw_point` previously declared here
@@ -119,7 +119,7 @@
               found `unsafe extern "C" fn(sameish_members::b::Point)`
 
 warning: `origin` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:198:13
+  --> $DIR/clashing-extern-fn.rs:197:13
    |
 LL |             fn origin() -> Point3;
    |             --------------------- `origin` previously declared here
@@ -131,7 +131,7 @@
               found `unsafe extern "C" fn() -> same_sized_members_clash::b::Point3`
 
 warning: `transparent_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:221:13
+  --> $DIR/clashing-extern-fn.rs:220:13
    |
 LL |             fn transparent_incorrect() -> T;
    |             ------------------------------- `transparent_incorrect` previously declared here
@@ -143,7 +143,7 @@
               found `unsafe extern "C" fn() -> isize`
 
 warning: `missing_return_type` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:260:13
+  --> $DIR/clashing-extern-fn.rs:259:13
    |
 LL |             fn missing_return_type() -> usize;
    |             --------------------------------- `missing_return_type` previously declared here
@@ -155,7 +155,7 @@
               found `unsafe extern "C" fn()`
 
 warning: `non_zero_usize` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:278:13
+  --> $DIR/clashing-extern-fn.rs:277:13
    |
 LL |             fn non_zero_usize() -> core::num::NonZero<usize>;
    |             ------------------------------------------------ `non_zero_usize` previously declared here
@@ -167,7 +167,7 @@
               found `unsafe extern "C" fn() -> usize`
 
 warning: `non_null_ptr` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:280:13
+  --> $DIR/clashing-extern-fn.rs:279:13
    |
 LL |             fn non_null_ptr() -> core::ptr::NonNull<usize>;
    |             ---------------------------------------------- `non_null_ptr` previously declared here
@@ -179,7 +179,7 @@
               found `unsafe extern "C" fn() -> *const usize`
 
 warning: `option_non_zero_usize_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:374:13
+  --> $DIR/clashing-extern-fn.rs:373:13
    |
 LL |             fn option_non_zero_usize_incorrect() -> usize;
    |             --------------------------------------------- `option_non_zero_usize_incorrect` previously declared here
@@ -191,7 +191,7 @@
               found `unsafe extern "C" fn() -> isize`
 
 warning: `option_non_null_ptr_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:376:13
+  --> $DIR/clashing-extern-fn.rs:375:13
    |
 LL |             fn option_non_null_ptr_incorrect() -> *const usize;
    |             -------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here
@@ -203,7 +203,7 @@
               found `unsafe extern "C" fn() -> *const isize`
 
 warning: `hidden_niche_transparent_no_niche` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:430:13
+  --> $DIR/clashing-extern-fn.rs:429:13
    |
 LL |             fn hidden_niche_transparent_no_niche() -> usize;
    |             ----------------------------------------------- `hidden_niche_transparent_no_niche` previously declared here
@@ -215,7 +215,7 @@
               found `unsafe extern "C" fn() -> Option<TransparentNoNiche>`
 
 warning: `hidden_niche_unsafe_cell` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:434:13
+  --> $DIR/clashing-extern-fn.rs:433:13
    |
 LL |             fn hidden_niche_unsafe_cell() -> usize;
    |             -------------------------------------- `hidden_niche_unsafe_cell` previously declared here
diff --git a/tests/ui/lint/invalid_value.rs b/tests/ui/lint/invalid_value.rs
index 1d2f23a..29e8e6c 100644
--- a/tests/ui/lint/invalid_value.rs
+++ b/tests/ui/lint/invalid_value.rs
@@ -2,7 +2,7 @@
 // in a lint.
 #![allow(deprecated)]
 #![deny(invalid_value)]
-#![feature(generic_nonzero, never_type, rustc_attrs)]
+#![feature(never_type, rustc_attrs)]
 
 use std::mem::{self, MaybeUninit};
 use std::ptr::NonNull;
diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs
index 3157b6e..c60290f 100644
--- a/tests/ui/lint/lint-ctypes-enum.rs
+++ b/tests/ui/lint/lint-ctypes-enum.rs
@@ -1,6 +1,5 @@
 #![allow(dead_code)]
 #![deny(improper_ctypes)]
-#![feature(generic_nonzero)]
 #![feature(ptr_internals)]
 #![feature(transparent_unions)]
 
diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr
index 48be3eb..103fda8 100644
--- a/tests/ui/lint/lint-ctypes-enum.stderr
+++ b/tests/ui/lint/lint-ctypes-enum.stderr
@@ -1,5 +1,5 @@
 error: `extern` block uses type `U`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:61:13
+  --> $DIR/lint-ctypes-enum.rs:60:13
    |
 LL |    fn uf(x: U);
    |             ^ not FFI-safe
@@ -7,7 +7,7 @@
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 note: the type is defined here
-  --> $DIR/lint-ctypes-enum.rs:10:1
+  --> $DIR/lint-ctypes-enum.rs:9:1
    |
 LL | enum U {
    | ^^^^^^
@@ -18,7 +18,7 @@
    |         ^^^^^^^^^^^^^^^
 
 error: `extern` block uses type `B`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:62:13
+  --> $DIR/lint-ctypes-enum.rs:61:13
    |
 LL |    fn bf(x: B);
    |             ^ not FFI-safe
@@ -26,13 +26,13 @@
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 note: the type is defined here
-  --> $DIR/lint-ctypes-enum.rs:13:1
+  --> $DIR/lint-ctypes-enum.rs:12:1
    |
 LL | enum B {
    | ^^^^^^
 
 error: `extern` block uses type `T`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:63:13
+  --> $DIR/lint-ctypes-enum.rs:62:13
    |
 LL |    fn tf(x: T);
    |             ^ not FFI-safe
@@ -40,13 +40,13 @@
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 note: the type is defined here
-  --> $DIR/lint-ctypes-enum.rs:17:1
+  --> $DIR/lint-ctypes-enum.rs:16:1
    |
 LL | enum T {
    | ^^^^^^
 
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:75:23
+  --> $DIR/lint-ctypes-enum.rs:74:23
    |
 LL |    fn nonzero_u128(x: Option<num::NonZero<u128>>);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -54,7 +54,7 @@
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `i128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:82:23
+  --> $DIR/lint-ctypes-enum.rs:81:23
    |
 LL |    fn nonzero_i128(x: Option<num::NonZero<i128>>);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -62,7 +62,7 @@
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `Option<TransparentUnion<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:87:28
+  --> $DIR/lint-ctypes-enum.rs:86:28
    |
 LL |    fn transparent_union(x: Option<TransparentUnion<num::NonZero<u8>>>);
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -71,7 +71,7 @@
    = note: enum has no representation hint
 
 error: `extern` block uses type `Option<Rust<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:89:20
+  --> $DIR/lint-ctypes-enum.rs:88:20
    |
 LL |    fn repr_rust(x: Option<Rust<num::NonZero<u8>>>);
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -80,7 +80,7 @@
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<(), NonZero<i32>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:90:20
+  --> $DIR/lint-ctypes-enum.rs:89:20
    |
 LL |    fn no_result(x: Result<(), num::NonZero<i32>>);
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
diff --git a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs
new file mode 100644
index 0000000..4291426
--- /dev/null
+++ b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs
@@ -0,0 +1,14 @@
+//@ check-pass
+//@ edition:2021
+
+// https://github.com/rust-lang/rust/issues/123573#issue-2229428739
+
+pub trait Test {}
+
+impl<'a, T: 'a> Test for &[T] where &'a T: Test {}
+
+fn main() {
+    struct Local {}
+    impl Test for &Local {}
+    //~^ WARN non-local `impl` definition
+}
diff --git a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr
new file mode 100644
index 0000000..9a8ab81
--- /dev/null
+++ b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr
@@ -0,0 +1,14 @@
+warning: non-local `impl` definition, they should be avoided as they go against expectation
+  --> $DIR/trait-solver-overflow-123573.rs:12:5
+   |
+LL |     impl Test for &Local {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: move this `impl` block outside the of the current function `main`
+   = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl`
+   = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type
+   = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>
+   = note: `#[warn(non_local_definitions)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/issues/issue-11692-1.rs b/tests/ui/macros/issue-11692-1.rs
similarity index 100%
rename from tests/ui/issues/issue-11692-1.rs
rename to tests/ui/macros/issue-11692-1.rs
diff --git a/tests/ui/issues/issue-11692-1.stderr b/tests/ui/macros/issue-11692-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-11692-1.stderr
rename to tests/ui/macros/issue-11692-1.stderr
diff --git a/tests/ui/issues/issue-11692-2.rs b/tests/ui/macros/issue-11692-2.rs
similarity index 100%
rename from tests/ui/issues/issue-11692-2.rs
rename to tests/ui/macros/issue-11692-2.rs
diff --git a/tests/ui/issues/issue-11692-2.stderr b/tests/ui/macros/issue-11692-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-11692-2.stderr
rename to tests/ui/macros/issue-11692-2.stderr
diff --git a/tests/ui/never_type/eq-never-types.rs b/tests/ui/never_type/eq-never-types.rs
new file mode 100644
index 0000000..19717fc
--- /dev/null
+++ b/tests/ui/never_type/eq-never-types.rs
@@ -0,0 +1,13 @@
+//@ check-pass
+//
+// issue: rust-lang/rust#120600
+
+#![allow(internal_features)]
+#![feature(never_type, rustc_attrs)]
+#![rustc_never_type_options(fallback = "never")]
+
+fn ice(a: !) {
+    a == a;
+}
+
+fn main() {}
diff --git a/tests/ui/issues/issue-57362-1.rs b/tests/ui/nll/issue-57362-1.rs
similarity index 100%
rename from tests/ui/issues/issue-57362-1.rs
rename to tests/ui/nll/issue-57362-1.rs
diff --git a/tests/ui/issues/issue-57362-1.stderr b/tests/ui/nll/issue-57362-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-57362-1.stderr
rename to tests/ui/nll/issue-57362-1.stderr
diff --git a/tests/ui/issues/issue-57362-2.rs b/tests/ui/nll/issue-57362-2.rs
similarity index 100%
rename from tests/ui/issues/issue-57362-2.rs
rename to tests/ui/nll/issue-57362-2.rs
diff --git a/tests/ui/issues/issue-57362-2.stderr b/tests/ui/nll/issue-57362-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-57362-2.stderr
rename to tests/ui/nll/issue-57362-2.stderr
diff --git a/tests/ui/numbers-arithmetic/f16-f128-lit.rs b/tests/ui/numbers-arithmetic/f16-f128-lit.rs
new file mode 100644
index 0000000..762436e
--- /dev/null
+++ b/tests/ui/numbers-arithmetic/f16-f128-lit.rs
@@ -0,0 +1,16 @@
+//@ run-pass
+
+#![feature(f16)]
+#![feature(f128)]
+
+fn main() {
+    assert_eq!(0.0_f16.to_bits(), 0x0000);
+    assert_eq!((-0.0_f16).to_bits(), 0x8000);
+    assert_eq!(10.0_f16.to_bits(), 0x4900);
+    assert_eq!((-10.0_f16).to_bits(), 0xC900);
+
+    assert_eq!(0.0_f128.to_bits(), 0x0000_0000_0000_0000_0000_0000_0000_0000);
+    assert_eq!((-0.0_f128).to_bits(), 0x8000_0000_0000_0000_0000_0000_0000_0000);
+    assert_eq!(10.0_f128.to_bits(), 0x4002_4000_0000_0000_0000_0000_0000_0000);
+    assert_eq!((-10.0_f128).to_bits(), 0xC002_4000_0000_0000_0000_0000_0000_0000);
+}
diff --git a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs
index bda5cef..8aa0d04 100644
--- a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs
+++ b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs
@@ -3,7 +3,6 @@
 //@ ignore-emscripten no processes
 //@ compile-flags: -C debug-assertions
 #![allow(arithmetic_overflow)]
-#![feature(generic_nonzero)]
 
 use std::num::NonZero;
 
diff --git a/tests/ui/numbers-arithmetic/saturating-float-casts-wasm.stderr b/tests/ui/numbers-arithmetic/saturating-float-casts-wasm.stderr
deleted file mode 100644
index 475b413..0000000
--- a/tests/ui/numbers-arithmetic/saturating-float-casts-wasm.stderr
+++ /dev/null
@@ -1,6 +0,0 @@
-warning: unstable feature specified for `-Ctarget-feature`: `nontrapping-fptoint`
-   |
-   = note: this feature is not stably supported; its behavior can change in the future
-
-warning: 1 warning emitted
-
diff --git a/tests/ui/issues/issue-12187-1.rs b/tests/ui/parser/issue-12187-1.rs
similarity index 100%
rename from tests/ui/issues/issue-12187-1.rs
rename to tests/ui/parser/issue-12187-1.rs
diff --git a/tests/ui/issues/issue-12187-1.stderr b/tests/ui/parser/issue-12187-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-12187-1.stderr
rename to tests/ui/parser/issue-12187-1.stderr
diff --git a/tests/ui/issues/issue-12187-2.rs b/tests/ui/parser/issue-12187-2.rs
similarity index 100%
rename from tests/ui/issues/issue-12187-2.rs
rename to tests/ui/parser/issue-12187-2.rs
diff --git a/tests/ui/issues/issue-12187-2.stderr b/tests/ui/parser/issue-12187-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-12187-2.stderr
rename to tests/ui/parser/issue-12187-2.stderr
diff --git a/tests/ui/print_type_sizes/niche-filling.rs b/tests/ui/print_type_sizes/niche-filling.rs
index 07da1cf..5dda0da 100644
--- a/tests/ui/print_type_sizes/niche-filling.rs
+++ b/tests/ui/print_type_sizes/niche-filling.rs
@@ -15,7 +15,6 @@
 //  ^-- needed because `--pass check` does not emit the output needed.
 //      FIXME: consider using an attribute instead of side-effects.
 #![allow(dead_code)]
-#![feature(generic_nonzero)]
 #![feature(rustc_attrs)]
 
 use std::num::NonZero;
diff --git a/tests/ui/issues/issue-23122-1.rs b/tests/ui/recursion/issue-23122-1.rs
similarity index 100%
rename from tests/ui/issues/issue-23122-1.rs
rename to tests/ui/recursion/issue-23122-1.rs
diff --git a/tests/ui/issues/issue-23122-1.stderr b/tests/ui/recursion/issue-23122-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-23122-1.stderr
rename to tests/ui/recursion/issue-23122-1.stderr
diff --git a/tests/ui/issues/issue-23122-2.rs b/tests/ui/recursion/issue-23122-2.rs
similarity index 100%
rename from tests/ui/issues/issue-23122-2.rs
rename to tests/ui/recursion/issue-23122-2.rs
diff --git a/tests/ui/issues/issue-23122-2.stderr b/tests/ui/recursion/issue-23122-2.stderr
similarity index 100%
rename from tests/ui/issues/issue-23122-2.stderr
rename to tests/ui/recursion/issue-23122-2.stderr
diff --git a/tests/ui/issues/issue-3214.rs b/tests/ui/resolve/issue-3214.rs
similarity index 100%
rename from tests/ui/issues/issue-3214.rs
rename to tests/ui/resolve/issue-3214.rs
diff --git a/tests/ui/issues/issue-3214.stderr b/tests/ui/resolve/issue-3214.stderr
similarity index 100%
rename from tests/ui/issues/issue-3214.stderr
rename to tests/ui/resolve/issue-3214.stderr
diff --git a/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014-2.rs b/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014-2.rs
index 4944de0..eb512bb 100644
--- a/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014-2.rs
+++ b/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014-2.rs
@@ -1,31 +1,28 @@
+// Test that println! to a closed stdout does not panic.
+// On Windows, close via SetStdHandle to 0.
 //@ run-pass
-#![allow(dead_code)]
 
 #![feature(rustc_private)]
 
-extern crate libc;
-
-type DWORD = u32;
-type HANDLE = *mut u8;
-type BOOL = i32;
-
-#[cfg(windows)]
-extern "system" {
-    fn SetStdHandle(nStdHandle: DWORD, nHandle: HANDLE) -> BOOL;
-}
-
 #[cfg(windows)]
 fn close_stdout() {
+    type DWORD = u32;
+    type HANDLE = *mut u8;
+    type BOOL = i32;
+
+    extern "system" {
+        fn SetStdHandle(nStdHandle: DWORD, nHandle: HANDLE) -> BOOL;
+    }
+
     const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
     unsafe { SetStdHandle(STD_OUTPUT_HANDLE, 0 as HANDLE); }
 }
 
-#[cfg(windows)]
+#[cfg(not(windows))]
+fn close_stdout() {}
+
 fn main() {
     close_stdout();
     println!("hello");
     println!("world");
 }
-
-#[cfg(not(windows))]
-fn main() {}
diff --git a/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014.rs b/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014.rs
index cc2d9ba..950c4de 100644
--- a/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014.rs
+++ b/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014.rs
@@ -1,28 +1,27 @@
+// Test that println! to a closed stdout does not panic.
+// On Windows, close via CloseHandle.
 //@ run-pass
-#![allow(dead_code)]
 //@ ignore-sgx no libc
 
 #![feature(rustc_private)]
 
-extern crate libc;
-
-type DWORD = u32;
-type HANDLE = *mut u8;
-
-#[cfg(windows)]
-extern "system" {
-    fn GetStdHandle(which: DWORD) -> HANDLE;
-    fn CloseHandle(handle: HANDLE) -> i32;
-}
-
 #[cfg(windows)]
 fn close_stdout() {
+    type DWORD = u32;
+    type HANDLE = *mut u8;
+
+    extern "system" {
+        fn GetStdHandle(which: DWORD) -> HANDLE;
+        fn CloseHandle(handle: HANDLE) -> i32;
+    }
+
     const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
     unsafe { CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); }
 }
 
 #[cfg(not(windows))]
 fn close_stdout() {
+    extern crate libc;
     unsafe { libc::close(1); }
 }
 
diff --git a/tests/ui/statics/mutable_memory_validation.rs b/tests/ui/statics/mutable_memory_validation.rs
new file mode 100644
index 0000000..fcf6ad1
--- /dev/null
+++ b/tests/ui/statics/mutable_memory_validation.rs
@@ -0,0 +1,21 @@
+//issue: rust-lang/rust#122548
+
+// Strip out raw byte dumps to make comparison platform-independent:
+//@ normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
+//@ normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> "HEX_DUMP"
+
+#![feature(const_mut_refs)]
+#![feature(const_refs_to_static)]
+
+use std::cell::UnsafeCell;
+
+struct Meh {
+    x: &'static UnsafeCell<i32>,
+}
+
+const MUH: Meh = Meh { x: unsafe { &mut *(&READONLY as *const _ as *mut _) } };
+//~^ ERROR: it is undefined behavior to use this value
+
+static READONLY: i32 = 0;
+
+pub fn main() {}
diff --git a/tests/ui/statics/mutable_memory_validation.stderr b/tests/ui/statics/mutable_memory_validation.stderr
new file mode 100644
index 0000000..f212692
--- /dev/null
+++ b/tests/ui/statics/mutable_memory_validation.stderr
@@ -0,0 +1,14 @@
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/mutable_memory_validation.rs:16:1
+   |
+LL | const MUH: Meh = Meh { x: unsafe { &mut *(&READONLY as *const _ as *mut _) } };
+   | ^^^^^^^^^^^^^^ constructing invalid value at .x.<deref>: encountered `UnsafeCell` in read-only memory
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/structs-enums/enum-null-pointer-opt.rs b/tests/ui/structs-enums/enum-null-pointer-opt.rs
index a841894..7a9e1b3 100644
--- a/tests/ui/structs-enums/enum-null-pointer-opt.rs
+++ b/tests/ui/structs-enums/enum-null-pointer-opt.rs
@@ -1,5 +1,4 @@
 //@ run-pass
-#![feature(generic_nonzero)]
 #![feature(transparent_unions)]
 
 use std::mem::size_of;
diff --git a/tests/ui/structs-enums/enum-null-pointer-opt.stderr b/tests/ui/structs-enums/enum-null-pointer-opt.stderr
index fca62bd..64e93ff 100644
--- a/tests/ui/structs-enums/enum-null-pointer-opt.stderr
+++ b/tests/ui/structs-enums/enum-null-pointer-opt.stderr
@@ -1,5 +1,5 @@
 warning: method `dummy` is never used
-  --> $DIR/enum-null-pointer-opt.rs:11:18
+  --> $DIR/enum-null-pointer-opt.rs:10:18
    |
 LL | trait Trait { fn dummy(&self) { } }
    |       -----      ^^^^^
diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs
index 50491d5..9c933a9 100644
--- a/tests/ui/structs-enums/type-sizes.rs
+++ b/tests/ui/structs-enums/type-sizes.rs
@@ -2,7 +2,6 @@
 
 #![allow(non_camel_case_types)]
 #![allow(dead_code)]
-#![feature(generic_nonzero)]
 #![feature(never_type)]
 #![feature(pointer_is_aligned_to)]
 #![feature(strict_provenance)]
diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.rs b/tests/ui/suggestions/core-std-import-order-issue-83564.rs
index 62b9b24..6f2bdd7 100644
--- a/tests/ui/suggestions/core-std-import-order-issue-83564.rs
+++ b/tests/ui/suggestions/core-std-import-order-issue-83564.rs
@@ -2,7 +2,6 @@
 //
 // This is a regression test for #83564.
 // For some reason, Rust 2018 or higher is required to reproduce the bug.
-#![feature(generic_nonzero)]
 
 fn main() {
     //~^ HELP consider importing one of these items
diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr b/tests/ui/suggestions/core-std-import-order-issue-83564.stderr
index 56e10b9..8665cc6 100644
--- a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr
+++ b/tests/ui/suggestions/core-std-import-order-issue-83564.stderr
@@ -1,5 +1,5 @@
 error[E0433]: failed to resolve: use of undeclared type `NonZero`
-  --> $DIR/core-std-import-order-issue-83564.rs:9:14
+  --> $DIR/core-std-import-order-issue-83564.rs:8:14
    |
 LL |     let _x = NonZero::new(5u32).unwrap();
    |              ^^^^^^^ use of undeclared type `NonZero`
diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout
index b248d2a..31c4671 100644
--- a/tests/ui/thir-print/thir-tree-match.stdout
+++ b/tests/ui/thir-print/thir-tree-match.stdout
@@ -12,7 +12,7 @@
                 kind: PatKind {
                     Binding {
                         name: "foo"
-                        mode: BindingAnnotation(No, Not)
+                        mode: BindingMode(No, Not)
                         var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).2))
                         ty: Foo
                         is_primary: true
diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs
index 17c55fb..caa3bfc 100644
--- a/tests/ui/traits/next-solver/specialization-transmute.rs
+++ b/tests/ui/traits/next-solver/specialization-transmute.rs
@@ -1,6 +1,5 @@
 //@ compile-flags: -Znext-solver
 //~^ ERROR cannot normalize `<T as Default>::Id: '_`
-#![feature(generic_nonzero)]
 #![feature(specialization)]
 //~^ WARN the feature `specialization` is incomplete
 
diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr
index 65e3370..76ae08f 100644
--- a/tests/ui/traits/next-solver/specialization-transmute.stderr
+++ b/tests/ui/traits/next-solver/specialization-transmute.stderr
@@ -1,5 +1,5 @@
 warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/specialization-transmute.rs:4:12
+  --> $DIR/specialization-transmute.rs:3:12
    |
 LL | #![feature(specialization)]
    |            ^^^^^^^^^^^^^^
@@ -11,31 +11,31 @@
 error: cannot normalize `<T as Default>::Id: '_`
 
 error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id == _`
-  --> $DIR/specialization-transmute.rs:16:23
+  --> $DIR/specialization-transmute.rs:15:23
    |
 LL |     fn intu(&self) -> &Self::Id {
    |                       ^^^^^^^^^ cannot satisfy `<T as Default>::Id == _`
 
 error[E0284]: type annotations needed: cannot satisfy `T <: <T as Default>::Id`
-  --> $DIR/specialization-transmute.rs:18:9
+  --> $DIR/specialization-transmute.rs:17:9
    |
 LL |         self
    |         ^^^^ cannot satisfy `T <: <T as Default>::Id`
 
 error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id == Option<NonZero<u8>>`
-  --> $DIR/specialization-transmute.rs:29:13
+  --> $DIR/specialization-transmute.rs:28:13
    |
 LL |     let s = transmute::<u8, Option<NonZero<u8>>>(0);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id == Option<NonZero<u8>>`
    |
 note: required by a bound in `transmute`
-  --> $DIR/specialization-transmute.rs:22:25
+  --> $DIR/specialization-transmute.rs:21:25
    |
 LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
    |                         ^^^^^^ required by this bound in `transmute`
 
 error[E0282]: type annotations needed
-  --> $DIR/specialization-transmute.rs:14:23
+  --> $DIR/specialization-transmute.rs:13:23
    |
 LL |     default type Id = T;
    |                       ^ cannot infer type for associated type `<T as Default>::Id`
diff --git a/tests/ui/type-alias-impl-trait/variance.rs b/tests/ui/type-alias-impl-trait/variance.rs
index 4b9fa67..ba52eaa 100644
--- a/tests/ui/type-alias-impl-trait/variance.rs
+++ b/tests/ui/type-alias-impl-trait/variance.rs
@@ -52,4 +52,28 @@
     //~^ ERROR: unconstrained opaque type
 }
 
+trait Nesting<'a> {
+    type Output;
+}
+impl<'a> Nesting<'a> for &'a () {
+    type Output = &'a ();
+}
+type NestedDeeply<'a> =
+    impl Nesting< //~ [*, o]
+        'a,
+        Output = impl Nesting< //~ [*, o]
+            'a,
+            Output = impl Nesting< //~ [*, o]
+                'a,
+                Output = impl Nesting< //~ [*, o]
+                    'a,
+                    Output = impl Nesting<'a> //~ [*, o]
+                >
+            >,
+        >,
+    >;
+fn test<'a>() -> NestedDeeply<'a> {
+    &()
+}
+
 fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/variance.stderr b/tests/ui/type-alias-impl-trait/variance.stderr
index 1aaf362..e5ced7a 100644
--- a/tests/ui/type-alias-impl-trait/variance.stderr
+++ b/tests/ui/type-alias-impl-trait/variance.stderr
@@ -176,6 +176,60 @@
 LL |     type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>;
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 24 previous errors
+error: [*, o]
+  --> $DIR/variance.rs:62:5
+   |
+LL | /     impl Nesting<
+LL | |         'a,
+LL | |         Output = impl Nesting<
+LL | |             'a,
+...  |
+LL | |         >,
+LL | |     >;
+   | |_____^
+
+error: [*, o]
+  --> $DIR/variance.rs:64:18
+   |
+LL |           Output = impl Nesting<
+   |  __________________^
+LL | |             'a,
+LL | |             Output = impl Nesting<
+LL | |                 'a,
+...  |
+LL | |             >,
+LL | |         >,
+   | |_________^
+
+error: [*, o]
+  --> $DIR/variance.rs:66:22
+   |
+LL |               Output = impl Nesting<
+   |  ______________________^
+LL | |                 'a,
+LL | |                 Output = impl Nesting<
+LL | |                     'a,
+LL | |                     Output = impl Nesting<'a>
+LL | |                 >
+LL | |             >,
+   | |_____________^
+
+error: [*, o]
+  --> $DIR/variance.rs:68:26
+   |
+LL |                   Output = impl Nesting<
+   |  __________________________^
+LL | |                     'a,
+LL | |                     Output = impl Nesting<'a>
+LL | |                 >
+   | |_________________^
+
+error: [*, o]
+  --> $DIR/variance.rs:70:30
+   |
+LL |                     Output = impl Nesting<'a>
+   |                              ^^^^^^^^^^^^^^^^
+
+error: aborting due to 29 previous errors
 
 For more information about this error, try `rustc --explain E0657`.
diff --git a/tests/ui/issues/issue-7607-1.rs b/tests/ui/type/issue-7607-1.rs
similarity index 100%
rename from tests/ui/issues/issue-7607-1.rs
rename to tests/ui/type/issue-7607-1.rs
diff --git a/tests/ui/issues/issue-7607-1.stderr b/tests/ui/type/issue-7607-1.stderr
similarity index 100%
rename from tests/ui/issues/issue-7607-1.stderr
rename to tests/ui/type/issue-7607-1.stderr
diff --git a/tests/ui/issues/issue-7607-2.rs b/tests/ui/type/issue-7607-2.rs
similarity index 100%
rename from tests/ui/issues/issue-7607-2.rs
rename to tests/ui/type/issue-7607-2.rs
diff --git a/triagebot.toml b/triagebot.toml
index 66b8f44..903569b 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -851,7 +851,6 @@
     "@nnethercote",
     "@fmease",
     "@fee1-dead",
-    "@BoxyUwU",
     "@jieyouxu",
 ]
 compiler = [