summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs19
-rw-r--r--compiler/rustc_attr_data_structures/src/encode_cross_crate.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs41
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs26
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs49
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs33
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/bounds.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs47
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/hash.rs1
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/jit.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/mod.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/attributes.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/callee.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/mono_item.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/callee.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/mono_item.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs33
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/naked_asm.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/mono_item.rs10
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs46
-rw-r--r--compiler/rustc_expand/src/build.rs10
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/utils.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs51
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs3
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs28
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs15
-rw-r--r--compiler/rustc_infer/src/infer/outlives/env.rs11
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs21
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs20
-rw-r--r--compiler/rustc_infer/src/infer/snapshot/undo_log.rs4
-rw-r--r--compiler/rustc_lint/src/builtin.rs2
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs14
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs22
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs13
-rw-r--r--compiler/rustc_middle/src/query/mod.rs11
-rw-r--r--compiler/rustc_middle/src/ty/context.rs34
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs1
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs1
-rw-r--r--compiler/rustc_mir_transform/src/coverage/query.rs32
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs553
-rw-r--r--compiler/rustc_parse/src/lexer/diagnostics.rs20
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs15
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs1
-rw-r--r--compiler/rustc_passes/src/check_attr.rs8
-rw-r--r--compiler/rustc_public/src/rustc_internal/mod.rs18
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs30
-rw-r--r--compiler/rustc_resolve/src/imports.rs38
-rw-r--r--compiler/rustc_resolve/src/late.rs14
-rw-r--r--compiler/rustc_resolve/src/lib.rs33
-rw-r--r--compiler/rustc_resolve/src/macros.rs2
-rw-r--r--compiler/rustc_session/src/config.rs5
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs4
-rw-r--r--compiler/rustc_trait_selection/src/regions.rs8
-rw-r--r--compiler/rustc_trait_selection/src/solve.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs12
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs27
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs40
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs33
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs25
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs88
-rw-r--r--compiler/rustc_traits/src/coroutine_witnesses.rs58
-rw-r--r--compiler/rustc_type_ir/src/elaborate.rs51
-rw-r--r--compiler/rustc_type_ir/src/flags.rs16
-rw-r--r--compiler/rustc_type_ir/src/interner.rs7
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs1
-rw-r--r--compiler/rustc_type_ir/src/visit.rs4
94 files changed, 1205 insertions, 625 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index 6542cbf4ba2..3157b18b635 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -110,6 +110,22 @@ pub enum DeprecatedSince {
     Err,
 }
 
+#[derive(
+    Copy,
+    Debug,
+    Eq,
+    PartialEq,
+    Encodable,
+    Decodable,
+    Clone,
+    HashStable_Generic,
+    PrintAttribute
+)]
+pub enum CoverageStatus {
+    On,
+    Off,
+}
+
 impl Deprecation {
     /// Whether an item marked with #[deprecated(since = "X")] is currently
     /// deprecated (i.e., whether X is not greater than the current rustc
@@ -274,6 +290,9 @@ pub enum AttributeKind {
     /// Represents `#[const_trait]`.
     ConstTrait(Span),
 
+    /// Represents `#[coverage]`.
+    Coverage(Span, CoverageStatus),
+
     ///Represents `#[rustc_deny_explicit_impl]`.
     DenyExplicitImpl(Span),
 
diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
index 6b54827c473..8a3eb2ac845 100644
--- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
+++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
@@ -28,6 +28,7 @@ impl AttributeKind {
             ConstStability { .. } => Yes,
             ConstStabilityIndirect => No,
             ConstTrait(..) => No,
+            Coverage(..) => No,
             DenyExplicitImpl(..) => No,
             Deprecation { .. } => Yes,
             DoNotImplementViaObject(..) => No,
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 34d9b048348..3e542771d58 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -1,4 +1,4 @@
-use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy};
+use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy};
 use rustc_feature::{AttributeTemplate, template};
 use rustc_session::parse::feature_err;
 use rustc_span::{Span, Symbol, sym};
@@ -52,6 +52,45 @@ impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
     const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
 }
 
+pub(crate) struct CoverageParser;
+
+impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
+    const PATH: &[Symbol] = &[sym::coverage];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
+    const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let Some(args) = args.list() else {
+            cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]);
+            return None;
+        };
+
+        let Some(arg) = args.single() else {
+            cx.expected_single_argument(args.span);
+            return None;
+        };
+
+        let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]);
+
+        let Some(arg) = arg.meta_item() else {
+            fail_incorrect_argument(args.span);
+            return None;
+        };
+
+        let status = match arg.path().word_sym() {
+            Some(sym::off) => CoverageStatus::Off,
+            Some(sym::on) => CoverageStatus::On,
+            None | Some(_) => {
+                fail_incorrect_argument(arg.span());
+                return None;
+            }
+        };
+
+        Some(AttributeKind::Coverage(cx.attr_span, status))
+    }
+}
+
 pub(crate) struct ExportNameParser;
 
 impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 1449680e35b..4d692d9562c 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -17,8 +17,9 @@ use crate::attributes::allow_unstable::{
     AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
 };
 use crate::attributes::codegen_attrs::{
-    ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser,
-    OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
+    ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser,
+    OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser,
+    UsedParser,
 };
 use crate::attributes::confusables::ConfusablesParser;
 use crate::attributes::deprecation::DeprecationParser;
@@ -139,6 +140,7 @@ attribute_parsers!(
         // tidy-alphabetical-end
 
         // tidy-alphabetical-start
+        Single<CoverageParser>,
         Single<DeprecationParser>,
         Single<DummyParser>,
         Single<ExportNameParser>,
@@ -452,6 +454,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             reason: AttributeParseErrorReason::ExpectedSpecificArgument {
                 possibilities,
                 strings: false,
+                list: false,
+            },
+        })
+    }
+
+    pub(crate) fn expected_specific_argument_and_list(
+        &self,
+        span: Span,
+        possibilities: Vec<&'static str>,
+    ) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedSpecificArgument {
+                possibilities,
+                strings: false,
+                list: true,
             },
         })
     }
@@ -469,6 +490,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             reason: AttributeParseErrorReason::ExpectedSpecificArgument {
                 possibilities,
                 strings: true,
+                list: false,
             },
         })
     }
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 5b0bf0e6662..9a400e0fe10 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -533,7 +533,9 @@ pub(crate) struct LinkOrdinalOutOfRange {
 
 pub(crate) enum AttributeParseErrorReason {
     ExpectedNoArgs,
-    ExpectedStringLiteral { byte_string: Option<Span> },
+    ExpectedStringLiteral {
+        byte_string: Option<Span>,
+    },
     ExpectedIntegerLiteral,
     ExpectedAtLeastOneArgument,
     ExpectedSingleArgument,
@@ -541,7 +543,12 @@ pub(crate) enum AttributeParseErrorReason {
     UnexpectedLiteral,
     ExpectedNameValue(Option<Symbol>),
     DuplicateKey(Symbol),
-    ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
+    ExpectedSpecificArgument {
+        possibilities: Vec<&'static str>,
+        strings: bool,
+        /// Should we tell the user to write a list when they didn't?
+        list: bool,
+    },
 }
 
 pub(crate) struct AttributeParseError {
@@ -615,7 +622,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                     format!("expected this to be of the form `{name} = \"...\"`"),
                 );
             }
-            AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
+            AttributeParseErrorReason::ExpectedSpecificArgument {
+                possibilities,
+                strings,
+                list: false,
+            } => {
                 let quote = if strings { '"' } else { '`' };
                 match possibilities.as_slice() {
                     &[] => {}
@@ -641,6 +652,38 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                     }
                 }
             }
+            AttributeParseErrorReason::ExpectedSpecificArgument {
+                possibilities,
+                strings,
+                list: true,
+            } => {
+                let quote = if strings { '"' } else { '`' };
+                match possibilities.as_slice() {
+                    &[] => {}
+                    &[x] => {
+                        diag.span_label(
+                            self.span,
+                            format!(
+                                "this attribute is only valid with {quote}{x}{quote} as an argument"
+                            ),
+                        );
+                    }
+                    [first, second] => {
+                        diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument"));
+                    }
+                    [first @ .., second_to_last, last] => {
+                        let mut res = String::new();
+                        for i in first {
+                            res.push_str(&format!("{quote}{i}{quote}, "));
+                        }
+                        res.push_str(&format!(
+                            "{quote}{second_to_last}{quote} or {quote}{last}{quote}"
+                        ));
+
+                        diag.span_label(self.span, format!("this attribute is only valid with one of the following arguments: {res}"));
+                    }
+                }
+            }
         }
 
         let suggestions = self.template.suggestions(false, &name);
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index ca636a8c999..9bb96b94506 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -1,3 +1,4 @@
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::LocalDefId;
 use rustc_infer::infer::canonical::QueryRegionConstraints;
 use rustc_infer::infer::outlives::env::RegionBoundPairs;
@@ -7,7 +8,7 @@ use rustc_infer::infer::{InferCtxt, SubregionOrigin};
 use rustc_infer::traits::query::type_op::DeeplyNormalize;
 use rustc_middle::bug;
 use rustc_middle::ty::{
-    self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions,
+    self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions,
 };
 use rustc_span::Span;
 use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -70,10 +71,12 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
 
     #[instrument(skip(self), level = "debug")]
     pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
-        let QueryRegionConstraints { outlives } = query_constraints;
+        let QueryRegionConstraints { outlives, assumptions } = query_constraints;
+        let assumptions =
+            elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());
 
         for &(predicate, constraint_category) in outlives {
-            self.convert(predicate, constraint_category);
+            self.convert(predicate, constraint_category, &assumptions);
         }
     }
 
@@ -112,15 +115,20 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
 
             self.category = outlives_requirement.category;
             self.span = outlives_requirement.blame_span;
-            self.convert(ty::OutlivesPredicate(subject, outlived_region), self.category);
+            self.convert(
+                ty::OutlivesPredicate(subject, outlived_region),
+                self.category,
+                &Default::default(),
+            );
         }
         (self.category, self.span, self.from_closure) = backup;
     }
 
     fn convert(
         &mut self,
-        predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
+        predicate: ty::ArgOutlivesPredicate<'tcx>,
         constraint_category: ConstraintCategory<'tcx>,
+        higher_ranked_assumptions: &FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
     ) {
         let tcx = self.infcx.tcx;
         debug!("generate: constraints at: {:#?}", self.locations);
@@ -150,7 +158,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
             }
 
             let mut next_outlives_predicates = vec![];
-            for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates {
+            for (pred, constraint_category) in outlives_predicates {
+                // Constraint is implied by a coroutine's well-formedness.
+                if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
+                    && higher_ranked_assumptions.contains(&pred)
+                {
+                    continue;
+                }
+
+                let ty::OutlivesPredicate(k1, r2) = pred;
                 match k1.kind() {
                     GenericArgKind::Lifetime(r1) => {
                         let r1_vid = self.to_region_vid(r1);
@@ -266,14 +282,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         &self,
         ty: Ty<'tcx>,
         next_outlives_predicates: &mut Vec<(
-            ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
+            ty::ArgOutlivesPredicate<'tcx>,
             ConstraintCategory<'tcx>,
         )>,
     ) -> Ty<'tcx> {
         match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span)
         {
             Ok(TypeOpOutput { output: ty, constraints, .. }) => {
-                if let Some(QueryRegionConstraints { outlives }) = constraints {
+                // FIXME(higher_ranked_auto): What should we do with the assumptions here?
+                if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints {
                     next_outlives_predicates.extend(outlives.iter().copied());
                 }
                 ty
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index f877e5eaadb..d500088c259 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -131,6 +131,11 @@ pub(crate) fn type_check<'tcx>(
         pre_obligations.is_empty(),
         "there should be no incoming region obligations = {pre_obligations:#?}",
     );
+    let pre_assumptions = infcx.take_registered_region_assumptions();
+    assert!(
+        pre_assumptions.is_empty(),
+        "there should be no incoming region assumptions = {pre_assumptions:#?}",
+    );
 
     debug!(?normalized_inputs_and_output);
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
index a98e9c6d1c7..dd8f0e46a0e 100644
--- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
@@ -23,6 +23,7 @@ pub(crate) fn expand_deriving_copy(
         methods: Vec::new(),
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
 
     trait_def.expand(cx, mitem, item, push);
@@ -46,6 +47,7 @@ pub(crate) fn expand_deriving_const_param_ty(
         methods: Vec::new(),
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
 
     trait_def.expand(cx, mitem, item, push);
@@ -60,6 +62,7 @@ pub(crate) fn expand_deriving_const_param_ty(
         methods: Vec::new(),
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
 
     trait_def.expand(cx, mitem, item, push);
@@ -83,6 +86,7 @@ pub(crate) fn expand_deriving_unsized_const_param_ty(
         methods: Vec::new(),
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
 
     trait_def.expand(cx, mitem, item, push);
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index 69f8c273797..3c78f53c5cb 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -87,6 +87,7 @@ pub(crate) fn expand_deriving_clone(
         }],
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
 
     trait_def.expand_ext(cx, mitem, item, push, is_simple)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index eca79e4dc48..29d531219a6 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -43,6 +43,7 @@ pub(crate) fn expand_deriving_eq(
         }],
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
     trait_def.expand_ext(cx, mitem, item, push, true)
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index 1ed44c20bc6..0e1ecf3118a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -34,6 +34,7 @@ pub(crate) fn expand_deriving_ord(
         }],
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
 
     trait_def.expand(cx, mitem, item, push)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index b1d950b8d89..990835fa277 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -30,6 +30,7 @@ pub(crate) fn expand_deriving_partial_eq(
         methods: Vec::new(),
         associated_types: Vec::new(),
         is_const: false,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
     structural_trait_def.expand(cx, mitem, item, push);
 
@@ -58,6 +59,7 @@ pub(crate) fn expand_deriving_partial_eq(
         methods,
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
     trait_def.expand(cx, mitem, item, push)
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 0a076dd670b..f5d262ece36 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -64,6 +64,7 @@ pub(crate) fn expand_deriving_partial_ord(
         methods: vec![partial_cmp_def],
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
     trait_def.expand(cx, mitem, item, push)
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index 8ab21986e68..1d63ce7d5fd 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -41,6 +41,7 @@ pub(crate) fn expand_deriving_debug(
         }],
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
     trait_def.expand(cx, mitem, item, push)
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 1fe567e23f4..b4e2d27fed3 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -51,6 +51,7 @@ pub(crate) fn expand_deriving_default(
         }],
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
     trait_def.expand(cx, mitem, item, push)
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index c55a9e73e38..b24e5563761 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -181,9 +181,11 @@ use std::{iter, vec};
 pub(crate) use StaticFields::*;
 pub(crate) use SubstructureFields::*;
 use rustc_ast::ptr::P;
+use rustc_ast::token::{IdentIsRaw, LitKind, Token, TokenKind};
+use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenTree};
 use rustc_ast::{
-    self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
-    Generics, Mutability, PatKind, VariantData,
+    self as ast, AnonConst, AttrArgs, BindingMode, ByRef, DelimArgs, EnumDef, Expr, GenericArg,
+    GenericParamKind, Generics, Mutability, PatKind, Safety, VariantData,
 };
 use rustc_attr_data_structures::{AttributeKind, ReprPacked};
 use rustc_attr_parsing::AttributeParser;
@@ -222,6 +224,8 @@ pub(crate) struct TraitDef<'a> {
     pub associated_types: Vec<(Ident, Ty)>,
 
     pub is_const: bool,
+
+    pub is_staged_api_crate: bool,
 }
 
 pub(crate) struct MethodDef<'a> {
@@ -784,8 +788,45 @@ impl<'a> TraitDef<'a> {
         // Create the type of `self`.
         let path = cx.path_all(self.span, false, vec![type_ident], self_params);
         let self_type = cx.ty_path(path);
+        let rustc_const_unstable =
+            cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span));
+
+        let mut attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span),];
+
+        // Only add `rustc_const_unstable` attributes if `derive_const` is used within libcore/libstd,
+        // Other crates don't need stability attributes, so adding them is not useful, but libcore needs them
+        // on all const trait impls.
+        if self.is_const && self.is_staged_api_crate {
+            attrs.push(
+                cx.attr_nested(
+                    rustc_ast::AttrItem {
+                        unsafety: Safety::Default,
+                        path: rustc_const_unstable,
+                        args: AttrArgs::Delimited(DelimArgs {
+                            dspan: DelimSpan::from_single(self.span),
+                            delim: rustc_ast::token::Delimiter::Parenthesis,
+                            tokens: [
+                                TokenKind::Ident(sym::feature, IdentIsRaw::No),
+                                TokenKind::Eq,
+                                TokenKind::lit(LitKind::Str, sym::derive_const, None),
+                                TokenKind::Comma,
+                                TokenKind::Ident(sym::issue, IdentIsRaw::No),
+                                TokenKind::Eq,
+                                TokenKind::lit(LitKind::Str, sym::derive_const_issue, None),
+                            ]
+                            .into_iter()
+                            .map(|kind| {
+                                TokenTree::Token(Token { kind, span: self.span }, Spacing::Alone)
+                            })
+                            .collect(),
+                        }),
+                        tokens: None,
+                    },
+                    self.span,
+                ),
+            )
+        }
 
-        let attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span),];
         let opt_trait_ref = Some(trait_ref);
 
         cx.item(
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs
index 6e6dbe19e4d..78534449895 100644
--- a/compiler/rustc_builtin_macros/src/deriving/hash.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs
@@ -41,6 +41,7 @@ pub(crate) fn expand_deriving_hash(
         }],
         associated_types: Vec::new(),
         is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
     };
 
     hash_trait_def.expand(cx, mitem, item, push);
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index 442151fe32d..8ec3599b63d 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -530,8 +530,8 @@ fn codegen_cgu_content(
     for (mono_item, item_data) in mono_items {
         match mono_item {
             MonoItem::Fn(instance) => {
-                if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED)
-                {
+                let flags = tcx.codegen_instance_attrs(instance.def).flags;
+                if flags.contains(CodegenFnAttrFlags::NAKED) {
                     rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm(
                         &mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm },
                         instance,
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index b1f185b551c..b3497503bf0 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -127,7 +127,7 @@ fn codegen_and_compile_fn<'tcx>(
     module: &mut dyn Module,
     instance: Instance<'tcx>,
 ) {
-    if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
+    if tcx.codegen_instance_attrs(instance.def).flags.contains(CodegenFnAttrFlags::NAKED) {
         tcx.dcx()
             .span_fatal(tcx.def_span(instance.def_id()), "Naked asm is not supported in JIT mode");
     }
diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs
index ffd47cace38..8f83c30b598 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs
@@ -35,7 +35,7 @@ fn predefine_mono_items<'tcx>(
                         is_compiler_builtins,
                     );
                     let is_naked = tcx
-                        .codegen_fn_attrs(instance.def_id())
+                        .codegen_instance_attrs(instance.def)
                         .flags
                         .contains(CodegenFnAttrFlags::NAKED);
                     module
diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs
index bf0927dc590..7a1ae6ca9c8 100644
--- a/compiler/rustc_codegen_gcc/src/attributes.rs
+++ b/compiler/rustc_codegen_gcc/src/attributes.rs
@@ -87,7 +87,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
     #[cfg_attr(not(feature = "master"), allow(unused_variables))] func: Function<'gcc>,
     instance: ty::Instance<'tcx>,
 ) {
-    let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
+    let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def);
 
     #[cfg(feature = "master")]
     {
diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs
index 189ac7cd779..e7ca95af594 100644
--- a/compiler/rustc_codegen_gcc/src/callee.rs
+++ b/compiler/rustc_codegen_gcc/src/callee.rs
@@ -105,7 +105,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
             let is_hidden = if is_generic {
                 // This is a monomorphization of a generic function.
                 if !(cx.tcx.sess.opts.share_generics()
-                    || tcx.codegen_fn_attrs(instance_def_id).inline
+                    || tcx.codegen_instance_attrs(instance.def).inline
                         == rustc_attr_data_structures::InlineAttr::Never)
                 {
                     // When not sharing generics, all instances are in the same
diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs
index 539e3ac8507..51f35cbdee4 100644
--- a/compiler/rustc_codegen_gcc/src/mono_item.rs
+++ b/compiler/rustc_codegen_gcc/src/mono_item.rs
@@ -53,7 +53,7 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
         self.linkage.set(base::linkage_to_gcc(linkage));
         let decl = self.declare_fn(symbol_name, fn_abi);
-        //let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
+        //let attrs = self.tcx.codegen_instance_attrs(instance.def);
 
         attributes::from_fn_attrs(self, decl, instance);
 
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 1ea5a062254..c32f11b27f3 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -344,7 +344,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
     llfn: &'ll Value,
     instance: ty::Instance<'tcx>,
 ) {
-    let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
+    let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def);
 
     let mut to_add = SmallVec::<[_; 16]>::new();
 
diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs
index 6d68eca60af..5a3dd90ab24 100644
--- a/compiler/rustc_codegen_llvm/src/callee.rs
+++ b/compiler/rustc_codegen_llvm/src/callee.rs
@@ -102,7 +102,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
         let is_hidden = if is_generic {
             // This is a monomorphization of a generic function.
             if !(cx.tcx.sess.opts.share_generics()
-                || tcx.codegen_fn_attrs(instance_def_id).inline
+                || tcx.codegen_instance_attrs(instance.def).inline
                     == rustc_attr_data_structures::InlineAttr::Never)
             {
                 // When not sharing generics, all instances are in the same
diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs
index 8f70270f203..f9edaded60d 100644
--- a/compiler/rustc_codegen_llvm/src/mono_item.rs
+++ b/compiler/rustc_codegen_llvm/src/mono_item.rs
@@ -55,8 +55,8 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
         let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
         let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
         llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
-        let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
-        base::set_link_section(lldecl, attrs);
+        let attrs = self.tcx.codegen_instance_attrs(instance.def);
+        base::set_link_section(lldecl, &attrs);
         if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
             && self.tcx.sess.target.supports_comdat()
         {
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index b46773396fc..5ce301c0eb9 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -2542,12 +2542,7 @@ fn add_order_independent_options(
         // sections to ensure we have all the data for PGO.
         let keep_metadata =
             crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled();
-        if crate_type != CrateType::Executable || !sess.opts.unstable_opts.export_executable_symbols
-        {
-            cmd.gc_sections(keep_metadata);
-        } else {
-            cmd.no_gc_sections();
-        }
+        cmd.gc_sections(keep_metadata);
     }
 
     cmd.set_output_kind(link_output_kind, crate_type, out_filename);
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index e0a3ad55be0..050797354b4 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -326,7 +326,6 @@ pub(crate) trait Linker {
         link_or_cc_args(self, &[path]);
     }
     fn gc_sections(&mut self, keep_metadata: bool);
-    fn no_gc_sections(&mut self);
     fn full_relro(&mut self);
     fn partial_relro(&mut self);
     fn no_relro(&mut self);
@@ -688,12 +687,6 @@ impl<'a> Linker for GccLinker<'a> {
         }
     }
 
-    fn no_gc_sections(&mut self) {
-        if self.is_gnu || self.sess.target.is_like_wasm {
-            self.link_arg("--no-gc-sections");
-        }
-    }
-
     fn optimize(&mut self) {
         if !self.is_gnu && !self.sess.target.is_like_wasm {
             return;
@@ -1010,10 +1003,6 @@ impl<'a> Linker for MsvcLinker<'a> {
         }
     }
 
-    fn no_gc_sections(&mut self) {
-        self.link_arg("/OPT:NOREF,NOICF");
-    }
-
     fn full_relro(&mut self) {
         // noop
     }
@@ -1243,10 +1232,6 @@ impl<'a> Linker for EmLinker<'a> {
         // noop
     }
 
-    fn no_gc_sections(&mut self) {
-        // noop
-    }
-
     fn optimize(&mut self) {
         // Emscripten performs own optimizations
         self.cc_arg(match self.sess.opts.optimize {
@@ -1418,10 +1403,6 @@ impl<'a> Linker for WasmLd<'a> {
         self.link_arg("--gc-sections");
     }
 
-    fn no_gc_sections(&mut self) {
-        self.link_arg("--no-gc-sections");
-    }
-
     fn optimize(&mut self) {
         // The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and
         // only differentiates -O0 and -O1. It does not apply to LTO.
@@ -1567,10 +1548,6 @@ impl<'a> Linker for L4Bender<'a> {
         }
     }
 
-    fn no_gc_sections(&mut self) {
-        self.link_arg("--no-gc-sections");
-    }
-
     fn optimize(&mut self) {
         // GNU-style linkers support optimization with -O. GNU ld doesn't
         // need a numeric argument, but other linkers do.
@@ -1734,10 +1711,6 @@ impl<'a> Linker for AixLinker<'a> {
         self.link_arg("-bgc");
     }
 
-    fn no_gc_sections(&mut self) {
-        self.link_arg("-bnogc");
-    }
-
     fn optimize(&mut self) {}
 
     fn pgo_gen(&mut self) {
@@ -1982,8 +1955,6 @@ impl<'a> Linker for PtxLinker<'a> {
 
     fn gc_sections(&mut self, _keep_metadata: bool) {}
 
-    fn no_gc_sections(&mut self) {}
-
     fn pgo_gen(&mut self) {}
 
     fn no_crt_objects(&mut self) {}
@@ -2057,8 +2028,6 @@ impl<'a> Linker for LlbcLinker<'a> {
 
     fn gc_sections(&mut self, _keep_metadata: bool) {}
 
-    fn no_gc_sections(&mut self) {}
-
     fn pgo_gen(&mut self) {}
 
     fn no_crt_objects(&mut self) {}
@@ -2139,8 +2108,6 @@ impl<'a> Linker for BpfLinker<'a> {
 
     fn gc_sections(&mut self, _keep_metadata: bool) {}
 
-    fn no_gc_sections(&mut self) {}
-
     fn pgo_gen(&mut self) {}
 
     fn no_crt_objects(&mut self) {}
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 025f5fb54f4..b8f635ab781 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -356,7 +356,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             LocalRef::Operand(operand) => {
                 // Don't spill operands onto the stack in naked functions.
                 // See: https://github.com/rust-lang/rust/issues/42779
-                let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id());
+                let attrs = bx.tcx().codegen_instance_attrs(self.instance.def);
                 if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
                     return;
                 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index fa69820d5d2..50d0f910744 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -390,9 +390,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 
     let mut num_untupled = None;
 
-    let codegen_fn_attrs = bx.tcx().codegen_fn_attrs(fx.instance.def_id());
-    let naked = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED);
-    if naked {
+    let codegen_fn_attrs = bx.tcx().codegen_instance_attrs(fx.instance.def);
+    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
         return vec![];
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
index beaf8950978..42e435cf0a3 100644
--- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
@@ -128,7 +128,7 @@ fn prefix_and_suffix<'tcx>(
     let is_arm = tcx.sess.target.arch == "arm";
     let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode);
 
-    let attrs = tcx.codegen_fn_attrs(instance.def_id());
+    let attrs = tcx.codegen_instance_attrs(instance.def);
     let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
 
     // If no alignment is specified, an alignment of 4 bytes is used.
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index e90463aacc8..2587e89417a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -611,11 +611,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
                     let fn_ty = bx.fn_decl_backend_type(fn_abi);
                     let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() {
-                        Some(bx.tcx().codegen_fn_attrs(instance.def_id()))
+                        Some(bx.tcx().codegen_instance_attrs(instance.def))
                     } else {
                         None
                     };
-                    bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, Some(instance))
+                    bx.call(
+                        fn_ty,
+                        fn_attrs.as_deref(),
+                        Some(fn_abi),
+                        fn_ptr,
+                        &[],
+                        None,
+                        Some(instance),
+                    )
                 } else {
                     bx.get_static(def_id)
                 };
diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs
index 7b4268abe4b..b9040c330fb 100644
--- a/compiler/rustc_codegen_ssa/src/mono_item.rs
+++ b/compiler/rustc_codegen_ssa/src/mono_item.rs
@@ -41,12 +41,8 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
                 base::codegen_global_asm(cx, item_id);
             }
             MonoItem::Fn(instance) => {
-                if cx
-                    .tcx()
-                    .codegen_fn_attrs(instance.def_id())
-                    .flags
-                    .contains(CodegenFnAttrFlags::NAKED)
-                {
+                let flags = cx.tcx().codegen_instance_attrs(instance.def).flags;
+                if flags.contains(CodegenFnAttrFlags::NAKED) {
                     naked_asm::codegen_naked_asm::<Bx::CodegenCx>(cx, instance, item_data);
                 } else {
                     base::codegen_instance::<Bx>(cx, instance);
@@ -75,7 +71,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
                 cx.predefine_static(def_id, linkage, visibility, symbol_name);
             }
             MonoItem::Fn(instance) => {
-                let attrs = cx.tcx().codegen_fn_attrs(instance.def_id());
+                let attrs = cx.tcx().codegen_instance_attrs(instance.def);
 
                 if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
                     // do not define this function; it will become a global assembly block
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index b2cbbc21430..47228de5213 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -936,7 +936,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         if let Some(fn_val) = self.get_fn_alloc(id) {
             let align = match fn_val {
                 FnVal::Instance(instance) => {
-                    self.tcx.codegen_fn_attrs(instance.def_id()).alignment.unwrap_or(Align::ONE)
+                    self.tcx.codegen_instance_attrs(instance.def).alignment.unwrap_or(Align::ONE)
                 }
                 // Machine-specific extra functions currently do not support alignment restrictions.
                 FnVal::Other(_) => Align::ONE,
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 18490385455..f3ed6042105 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -1237,9 +1237,55 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
         return None;
     }
 
+    warn_on_confusing_output_filename_flag(early_dcx, &matches, args);
+
     Some(matches)
 }
 
+/// Warn if `-o` is used without a space between the flag name and the value
+/// and the value is a high-value confusables,
+/// e.g. `-optimize` instead of `-o optimize`, see issue #142812.
+fn warn_on_confusing_output_filename_flag(
+    early_dcx: &EarlyDiagCtxt,
+    matches: &getopts::Matches,
+    args: &[String],
+) {
+    fn eq_ignore_separators(s1: &str, s2: &str) -> bool {
+        let s1 = s1.replace('-', "_");
+        let s2 = s2.replace('-', "_");
+        s1 == s2
+    }
+
+    if let Some(name) = matches.opt_str("o")
+        && let Some(suspect) = args.iter().find(|arg| arg.starts_with("-o") && *arg != "-o")
+    {
+        let filename = suspect.strip_prefix("-").unwrap_or(suspect);
+        let optgroups = config::rustc_optgroups();
+        let fake_args = ["optimize", "o0", "o1", "o2", "o3", "ofast", "og", "os", "oz"];
+
+        // Check if provided filename might be confusing in conjunction with `-o` flag,
+        // i.e. consider `-o{filename}` such as `-optimize` with `filename` being `ptimize`.
+        // There are high-value confusables, for example:
+        // - Long name of flags, e.g. `--out-dir` vs `-out-dir`
+        // - C compiler flag, e.g. `optimize`, `o0`, `o1`, `o2`, `o3`, `ofast`.
+        // - Codegen flags, e.g. `pt-level` of `-opt-level`.
+        if optgroups.iter().any(|option| eq_ignore_separators(option.long_name(), filename))
+            || config::CG_OPTIONS.iter().any(|option| eq_ignore_separators(option.name(), filename))
+            || fake_args.iter().any(|arg| eq_ignore_separators(arg, filename))
+        {
+            early_dcx.early_warn(
+                "option `-o` has no space between flag name and value, which can be confusing",
+            );
+            early_dcx.early_note(format!(
+                "output filename `-o {name}` is applied instead of a flag named `o{name}`"
+            ));
+            early_dcx.early_help(format!(
+                "insert a space between `-o` and `{name}` if this is intentional: `-o {name}`"
+            ));
+        }
+    }
+}
+
 fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
     let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
         Input::File(file) => new_parser_from_file(&sess.psess, file, None),
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 85683c1a03f..51d6e43ab67 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -3,8 +3,8 @@ use rustc_ast::token::Delimiter;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::util::literal;
 use rustc_ast::{
-    self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp,
-    attr, token, tokenstream,
+    self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind,
+    UnOp, attr, token, tokenstream,
 };
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
@@ -766,4 +766,10 @@ impl<'a> ExtCtxt<'a> {
             span,
         )
     }
+
+    // Builds an attribute fully manually.
+    pub fn attr_nested(&self, inner: AttrItem, span: Span) -> ast::Attribute {
+        let g = &self.sess.psess.attr_id_generator;
+        attr::mk_attr_from_item(g, inner, None, ast::AttrStyle::Outer, span)
+    }
 }
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index a20becbe7e8..e38ca9e80ce 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -158,7 +158,7 @@ hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be imp
 hir_analysis_drop_impl_negative = negative `Drop` impls are not supported
 
 hir_analysis_drop_impl_on_wrong_item =
-    the `Drop` trait may only be implemented for local structs, enums, and unions
+    the `{$trait_}` trait may only be implemented for local structs, enums, and unions
     .label = must be a struct, enum, or union in the current crate
 
 hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 8356a0af63c..27948f50a4a 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -37,6 +37,7 @@ pub(super) fn check_trait<'tcx>(
     let lang_items = tcx.lang_items();
     let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header };
     checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?;
+    checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?;
     checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?;
     checker.check(lang_items.const_param_ty_trait(), |checker| {
         visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy)
@@ -83,7 +84,10 @@ fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaran
 
     let impl_ = tcx.hir_expect_item(impl_did).expect_impl();
 
-    Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span }))
+    Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem {
+        span: impl_.self_ty.span,
+        trait_: tcx.item_name(checker.impl_header.trait_ref.skip_binder().def_id),
+    }))
 }
 
 fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index eb65050c17c..fbd21f8b100 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -205,6 +205,7 @@ pub(crate) struct DropImplOnWrongItem {
     #[primary_span]
     #[label]
     pub span: Span,
+    pub trait_: Symbol,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs
index 301f5de9424..99a633e2b7d 100644
--- a/compiler/rustc_hir_analysis/src/outlives/utils.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs
@@ -7,8 +7,7 @@ use smallvec::smallvec;
 
 /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
 /// must be added to the struct header.
-pub(crate) type RequiredPredicates<'tcx> =
-    FxIndexMap<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, Span>;
+pub(crate) type RequiredPredicates<'tcx> = FxIndexMap<ty::ArgOutlivesPredicate<'tcx>, Span>;
 
 /// Given a requirement `T: 'a` or `'b: 'a`, deduce the
 /// outlives_component and add it to `required_predicates`
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 459c0498d50..a413f805873 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -161,16 +161,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Resume type defaults to `()` if the coroutine has no argument.
                 let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit);
 
-                // In the new solver, we can just instantiate this eagerly
-                // with the witness. This will ensure that goals that don't need
-                // to stall on interior types will get processed eagerly.
-                let interior = if self.next_trait_solver() {
-                    Ty::new_coroutine_witness(tcx, expr_def_id.to_def_id(), parent_args)
-                } else {
-                    self.next_ty_var(expr_span)
-                };
-
-                self.deferred_coroutine_interiors.borrow_mut().push((expr_def_id, interior));
+                let interior = Ty::new_coroutine_witness(tcx, expr_def_id.to_def_id(), parent_args);
 
                 // Coroutines that come from coroutine closures have not yet determined
                 // their kind ty, so make a fresh infer var which will be constrained
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index af1fc045ac8..0b3d50ff219 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -625,50 +625,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // trigger query cycle ICEs, as doing so requires MIR.
         self.select_obligations_where_possible(|_| {});
 
-        let coroutines = std::mem::take(&mut *self.deferred_coroutine_interiors.borrow_mut());
-        debug!(?coroutines);
-
-        let mut obligations = vec![];
-
-        if !self.next_trait_solver() {
-            for &(coroutine_def_id, interior) in coroutines.iter() {
-                debug!(?coroutine_def_id);
-
-                // Create the `CoroutineWitness` type that we will unify with `interior`.
-                let args = ty::GenericArgs::identity_for_item(
-                    self.tcx,
-                    self.tcx.typeck_root_def_id(coroutine_def_id.to_def_id()),
-                );
-                let witness =
-                    Ty::new_coroutine_witness(self.tcx, coroutine_def_id.to_def_id(), args);
-
-                // Unify `interior` with `witness` and collect all the resulting obligations.
-                let span = self.tcx.hir_body_owned_by(coroutine_def_id).value.span;
-                let ty::Infer(ty::InferTy::TyVar(_)) = interior.kind() else {
-                    span_bug!(span, "coroutine interior witness not infer: {:?}", interior.kind())
-                };
-                let ok = self
-                    .at(&self.misc(span), self.param_env)
-                    // Will never define opaque types, as all we do is instantiate a type variable.
-                    .eq(DefineOpaqueTypes::Yes, interior, witness)
-                    .expect("Failed to unify coroutine interior type");
-
-                obligations.extend(ok.obligations);
-            }
-        }
+        let ty::TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode()
+        else {
+            bug!();
+        };
 
-        if !coroutines.is_empty() {
-            obligations.extend(
+        if defining_opaque_types_and_generators
+            .iter()
+            .any(|def_id| self.tcx.is_coroutine(def_id.to_def_id()))
+        {
+            self.typeck_results.borrow_mut().coroutine_stalled_predicates.extend(
                 self.fulfillment_cx
                     .borrow_mut()
-                    .drain_stalled_obligations_for_coroutines(&self.infcx),
+                    .drain_stalled_obligations_for_coroutines(&self.infcx)
+                    .into_iter()
+                    .map(|o| (o.predicate, o.cause)),
             );
         }
-
-        self.typeck_results
-            .borrow_mut()
-            .coroutine_stalled_predicates
-            .extend(obligations.into_iter().map(|o| (o.predicate, o.cause)));
     }
 
     #[instrument(skip(self), level = "debug")]
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 3261327a9fd..1f3969bd93c 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1650,7 +1650,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 }
             }
 
-            let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
+            let sources =
+                applicable_candidates.iter().map(|p| self.candidate_source(p.0, self_ty)).collect();
             return Some(Err(MethodError::Ambiguity(sources)));
         }
 
diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
index 26be5fc6d19..9f4ab8ca5d4 100644
--- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
+++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
@@ -63,8 +63,6 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
 
     pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, HirId)>>,
 
-    pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, Ty<'tcx>)>>,
-
     pub(super) deferred_repeat_expr_checks:
         RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>,
 
@@ -103,7 +101,6 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
             deferred_cast_checks: RefCell::new(Vec::new()),
             deferred_transmute_checks: RefCell::new(Vec::new()),
             deferred_asm_checks: RefCell::new(Vec::new()),
-            deferred_coroutine_interiors: RefCell::new(Vec::new()),
             deferred_repeat_expr_checks: RefCell::new(Vec::new()),
             diverging_type_vars: RefCell::new(Default::default()),
             infer_var_info: RefCell::new(Default::default()),
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index d5d49e3188a..6be53c948c8 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -116,9 +116,15 @@ impl<'tcx> InferCtxt<'tcx> {
         }
 
         let region_obligations = self.take_registered_region_obligations();
+        let region_assumptions = self.take_registered_region_assumptions();
         debug!(?region_obligations);
         let region_constraints = self.with_region_constraints(|region_constraints| {
-            make_query_region_constraints(tcx, region_obligations, region_constraints)
+            make_query_region_constraints(
+                tcx,
+                region_obligations,
+                region_constraints,
+                region_assumptions,
+            )
         });
         debug!(?region_constraints);
 
@@ -169,6 +175,11 @@ impl<'tcx> InferCtxt<'tcx> {
             self.register_outlives_constraint(predicate, cause);
         }
 
+        for assumption in &query_response.value.region_constraints.assumptions {
+            let assumption = instantiate_value(self.tcx, &result_args, *assumption);
+            self.register_region_assumption(assumption);
+        }
+
         let user_result: R =
             query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
 
@@ -292,6 +303,18 @@ impl<'tcx> InferCtxt<'tcx> {
             }),
         );
 
+        // FIXME(higher_ranked_auto): Optimize this to instantiate all assumptions
+        // at once, rather than calling `instantiate_value` repeatedly which may
+        // create more universes.
+        output_query_region_constraints.assumptions.extend(
+            query_response
+                .value
+                .region_constraints
+                .assumptions
+                .iter()
+                .map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)),
+        );
+
         let user_result: R =
             query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
 
@@ -567,6 +590,7 @@ pub fn make_query_region_constraints<'tcx>(
     tcx: TyCtxt<'tcx>,
     outlives_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
     region_constraints: &RegionConstraintData<'tcx>,
+    assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
 ) -> QueryRegionConstraints<'tcx> {
     let RegionConstraintData { constraints, verifys } = region_constraints;
 
@@ -602,5 +626,5 @@ pub fn make_query_region_constraints<'tcx>(
         }))
         .collect();
 
-    QueryRegionConstraints { outlives }
+    QueryRegionConstraints { outlives, assumptions }
 }
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index cc3ad921489..2d269e320b6 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -149,6 +149,13 @@ pub struct InferCtxtInner<'tcx> {
     /// that all type inference variables have been bound and so forth.
     region_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
 
+    /// The outlives bounds that we assume must hold about placeholders that
+    /// come from instantiating the binder of coroutine-witnesses. These bounds
+    /// are deduced from the well-formedness of the witness's types, and are
+    /// necessary because of the way we anonymize the regions in a coroutine,
+    /// which may cause types to no longer be considered well-formed.
+    region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
+
     /// Caches for opaque type inference.
     opaque_type_storage: OpaqueTypeStorage<'tcx>,
 }
@@ -164,7 +171,8 @@ impl<'tcx> InferCtxtInner<'tcx> {
             int_unification_storage: Default::default(),
             float_unification_storage: Default::default(),
             region_constraint_storage: Some(Default::default()),
-            region_obligations: vec![],
+            region_obligations: Default::default(),
+            region_assumptions: Default::default(),
             opaque_type_storage: Default::default(),
         }
     }
@@ -175,6 +183,11 @@ impl<'tcx> InferCtxtInner<'tcx> {
     }
 
     #[inline]
+    pub fn region_assumptions(&self) -> &[ty::ArgOutlivesPredicate<'tcx>] {
+        &self.region_assumptions
+    }
+
+    #[inline]
     pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> {
         self.projection_cache.with_log(&mut self.undo_log)
     }
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs
index cb5a33c5c97..47b738a4079 100644
--- a/compiler/rustc_infer/src/infer/outlives/env.rs
+++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -1,4 +1,4 @@
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_data_structures::transitive_relation::TransitiveRelationBuilder;
 use rustc_middle::{bug, ty};
 use tracing::debug;
@@ -39,6 +39,9 @@ pub struct OutlivesEnvironment<'tcx> {
     /// optimized in the future, though.
     region_bound_pairs: RegionBoundPairs<'tcx>,
     known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
+    /// Assumptions that come from the well-formedness of coroutines that we prove
+    /// auto trait bounds for during the type checking of this body.
+    higher_ranked_assumptions: FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
 }
 
 /// "Region-bound pairs" tracks outlives relations that are known to
@@ -52,6 +55,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
         extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
+        higher_ranked_assumptions: FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
     ) -> Self {
         let mut region_relation = TransitiveRelationBuilder::default();
         let mut region_bound_pairs = RegionBoundPairs::default();
@@ -88,6 +92,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
             known_type_outlives,
             free_region_map: FreeRegionMap { relation: region_relation.freeze() },
             region_bound_pairs,
+            higher_ranked_assumptions,
         }
     }
 
@@ -102,4 +107,8 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
     pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] {
         &self.known_type_outlives
     }
+
+    pub fn higher_ranked_assumptions(&self) -> &FxHashSet<ty::ArgOutlivesPredicate<'tcx>> {
+        &self.higher_ranked_assumptions
+    }
 }
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index 2a4544c1140..19911bfbd48 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -10,6 +10,7 @@ use super::region_constraints::{RegionConstraintData, UndoLog};
 use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
 use crate::infer::free_regions::RegionRelations;
 use crate::infer::lexical_region_resolve;
+use crate::infer::region_constraints::Constraint;
 
 pub mod env;
 pub mod for_liveness;
@@ -54,18 +55,29 @@ impl<'tcx> InferCtxt<'tcx> {
             }
         };
 
-        let storage = {
+        let mut storage = {
             let mut inner = self.inner.borrow_mut();
             let inner = &mut *inner;
             assert!(
                 self.tainted_by_errors().is_some() || inner.region_obligations.is_empty(),
                 "region_obligations not empty: {:#?}",
-                inner.region_obligations
+                inner.region_obligations,
             );
             assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
             inner.region_constraint_storage.take().expect("regions already resolved")
         };
 
+        // Filter out any region-region outlives assumptions that are implied by
+        // coroutine well-formedness.
+        if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions {
+            storage.data.constraints.retain(|(constraint, _)| match *constraint {
+                Constraint::RegSubReg(r1, r2) => !outlives_env
+                    .higher_ranked_assumptions()
+                    .contains(&ty::OutlivesPredicate(r2.into(), r1)),
+                _ => true,
+            });
+        }
+
         let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map());
 
         let (lexical_region_resolutions, errors) =
@@ -93,6 +105,11 @@ impl<'tcx> InferCtxt<'tcx> {
             "region_obligations not empty: {:#?}",
             self.inner.borrow().region_obligations
         );
+        assert!(
+            self.inner.borrow().region_assumptions.is_empty(),
+            "region_assumptions not empty: {:#?}",
+            self.inner.borrow().region_assumptions
+        );
 
         self.inner.borrow_mut().unwrap_region_constraints().take_and_reset_data()
     }
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 43504bd77c1..a8520c0e71d 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -84,7 +84,7 @@ use crate::traits::{ObligationCause, ObligationCauseCode};
 impl<'tcx> InferCtxt<'tcx> {
     pub fn register_outlives_constraint(
         &self,
-        ty::OutlivesPredicate(arg, r2): ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
+        ty::OutlivesPredicate(arg, r2): ty::ArgOutlivesPredicate<'tcx>,
         cause: &ObligationCause<'tcx>,
     ) {
         match arg.kind() {
@@ -170,6 +170,16 @@ impl<'tcx> InferCtxt<'tcx> {
         std::mem::take(&mut self.inner.borrow_mut().region_obligations)
     }
 
+    pub fn register_region_assumption(&self, assumption: ty::ArgOutlivesPredicate<'tcx>) {
+        let mut inner = self.inner.borrow_mut();
+        inner.undo_log.push(UndoLog::PushRegionAssumption);
+        inner.region_assumptions.push(assumption);
+    }
+
+    pub fn take_registered_region_assumptions(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
+        std::mem::take(&mut self.inner.borrow_mut().region_assumptions)
+    }
+
     /// Process the region obligations that must be proven (during
     /// `regionck`) for the given `body_id`, given information about
     /// the region bounds in scope and so forth.
@@ -220,6 +230,14 @@ impl<'tcx> InferCtxt<'tcx> {
                 let (sup_type, sub_region) =
                     (sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));
 
+                if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
+                    && outlives_env
+                        .higher_ranked_assumptions()
+                        .contains(&ty::OutlivesPredicate(sup_type.into(), sub_region))
+                {
+                    continue;
+                }
+
                 debug!(?sup_type, ?sub_region, ?origin);
 
                 let outlives = &mut TypeOutlives::new(
diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs
index 34e649afedc..40e4c329446 100644
--- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs
+++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs
@@ -28,6 +28,7 @@ pub(crate) enum UndoLog<'tcx> {
     RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
     ProjectionCache(traits::UndoLog<'tcx>),
     PushTypeOutlivesConstraint,
+    PushRegionAssumption,
 }
 
 macro_rules! impl_from {
@@ -77,6 +78,9 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
                 let popped = self.region_obligations.pop();
                 assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
             }
+            UndoLog::PushRegionAssumption => {
+                self.region_assumptions.pop();
+            }
         }
     }
 }
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8244f0bed4c..73e68834232 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1585,6 +1585,8 @@ impl EarlyLintPass for DoubleNegations {
         if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind
             && let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind
             && !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _))
+            // Don't lint if this jumps macro expansion boundary (Issue #143980)
+            && expr.span.eq_ctxt(inner.span)
         {
             cx.emit_span_lint(
                 DOUBLE_NEGATIONS,
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 2bbc48b633c..4fe4c2dadee 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -81,13 +81,18 @@ pub struct QueryResponse<'tcx, R> {
 #[derive(HashStable, TypeFoldable, TypeVisitable)]
 pub struct QueryRegionConstraints<'tcx> {
     pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
+    pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
 }
 
 impl QueryRegionConstraints<'_> {
-    /// Represents an empty (trivially true) set of region
-    /// constraints.
+    /// Represents an empty (trivially true) set of region constraints.
+    ///
+    /// FIXME(higher_ranked_auto): This could still just be true if there are only assumptions?
+    /// Because I don't expect for us to get cases where an assumption from one query would
+    /// discharge a requirement from another query, which is a potential problem if we did throw
+    /// away these assumptions because there were no constraints.
     pub fn is_empty(&self) -> bool {
-        self.outlives.is_empty()
+        self.outlives.is_empty() && self.assumptions.is_empty()
     }
 }
 
@@ -130,8 +135,7 @@ impl<'tcx, R> QueryResponse<'tcx, R> {
     }
 }
 
-pub type QueryOutlivesConstraint<'tcx> =
-    (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>);
+pub type QueryOutlivesConstraint<'tcx> = (ty::ArgOutlivesPredicate<'tcx>, ConstraintCategory<'tcx>);
 
 #[derive(Default)]
 pub struct CanonicalParamEnvCache<'tcx> {
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 2f16d385efb..6eae3b51e29 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
 use rustc_abi::Align;
 use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs;
 use rustc_attr_data_structures::{InlineAttr, InstructionSetAttr, OptimizeAttr};
@@ -6,6 +8,26 @@ use rustc_span::Symbol;
 use rustc_target::spec::SanitizerSet;
 
 use crate::mir::mono::Linkage;
+use crate::ty::{InstanceKind, TyCtxt};
+
+impl<'tcx> TyCtxt<'tcx> {
+    pub fn codegen_instance_attrs(
+        self,
+        instance_kind: InstanceKind<'_>,
+    ) -> Cow<'tcx, CodegenFnAttrs> {
+        let mut attrs = Cow::Borrowed(self.codegen_fn_attrs(instance_kind.def_id()));
+
+        // Drop the `#[naked]` attribute on non-item `InstanceKind`s, like the shims that
+        // are generated for indirect function calls.
+        if !matches!(instance_kind, InstanceKind::Item(_)) {
+            if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
+                attrs.to_mut().flags.remove(CodegenFnAttrFlags::NAKED);
+            }
+        }
+
+        attrs
+    }
+}
 
 #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
 pub struct CodegenFnAttrs {
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 47ba850d50d..2d7ddd105bd 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -152,7 +152,7 @@ impl<'tcx> MonoItem<'tcx> {
         // If the function is #[naked] or contains any other attribute that requires exactly-once
         // instantiation:
         // We emit an unused_attributes lint for this case, which should be kept in sync if possible.
-        let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
+        let codegen_fn_attrs = tcx.codegen_instance_attrs(instance.def);
         if codegen_fn_attrs.contains_extern_indicator()
             || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED)
         {
@@ -219,7 +219,7 @@ impl<'tcx> MonoItem<'tcx> {
         // functions the same as those that unconditionally get LocalCopy codegen. It's only when
         // we get here that we can at least not codegen a #[inline(never)] generic function in all
         // of our CGUs.
-        if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline
+        if let InlineAttr::Never = codegen_fn_attrs.inline
             && self.is_generic_fn()
         {
             return InstantiationMode::GloballyShared { may_conflict: true };
@@ -234,14 +234,13 @@ impl<'tcx> MonoItem<'tcx> {
     }
 
     pub fn explicit_linkage(&self, tcx: TyCtxt<'tcx>) -> Option<Linkage> {
-        let def_id = match *self {
-            MonoItem::Fn(ref instance) => instance.def_id(),
-            MonoItem::Static(def_id) => def_id,
+        let instance_kind = match *self {
+            MonoItem::Fn(ref instance) => instance.def,
+            MonoItem::Static(def_id) => InstanceKind::Item(def_id),
             MonoItem::GlobalAsm(..) => return None,
         };
 
-        let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
-        codegen_fn_attrs.linkage
+        tcx.codegen_instance_attrs(instance_kind).linkage
     }
 
     /// Returns `true` if this instance is instantiable - whether it has no unsatisfied
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 935cc889574..d5c2b6de4ae 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -988,7 +988,7 @@ rustc_queries! {
     }
 
     query coroutine_hidden_types(
-        def_id: DefId
+        def_id: DefId,
     ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes<TyCtxt<'tcx>>>> {
         desc { "looking up the hidden types stored across await points in a coroutine" }
     }
@@ -1505,6 +1505,15 @@ rustc_queries! {
         separate_provide_extern
     }
 
+    /// Returns the `CodegenFnAttrs` for the item at `def_id`.
+    ///
+    /// If possible, use `tcx.codegen_instance_attrs` instead. That function takes the
+    /// instance kind into account.
+    ///
+    /// For example, the `#[naked]` attribute should be applied for `InstanceKind::Item`,
+    /// but should not be applied if the instance kind is `InstanceKind::ReifyShim`.
+    /// Using this query would include the attribute regardless of the actual instance
+    /// kind at the call site.
     query codegen_fn_attrs(def_id: DefId) -> &'tcx CodegenFnAttrs {
         desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) }
         arena_cache
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 915b062417f..81c13e55dd8 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -163,6 +163,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type BoundRegion = ty::BoundRegion;
     type PlaceholderRegion = ty::PlaceholderRegion;
 
+    type RegionAssumptions = &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>;
+
     type ParamEnv = ty::ParamEnv<'tcx>;
     type Predicate = Predicate<'tcx>;
 
@@ -714,17 +716,13 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self,
         defining_anchor: Self::LocalDefId,
     ) -> Self::LocalDefIds {
-        if self.next_trait_solver_globally() {
-            let coroutines_defined_by = self
-                .nested_bodies_within(defining_anchor)
-                .iter()
-                .filter(|def_id| self.is_coroutine(def_id.to_def_id()));
-            self.mk_local_def_ids_from_iter(
-                self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by),
-            )
-        } else {
-            self.opaque_types_defined_by(defining_anchor)
-        }
+        let coroutines_defined_by = self
+            .nested_bodies_within(defining_anchor)
+            .iter()
+            .filter(|def_id| self.is_coroutine(def_id.to_def_id()));
+        self.mk_local_def_ids_from_iter(
+            self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by),
+        )
     }
 }
 
@@ -882,6 +880,7 @@ pub struct CtxtInterners<'tcx> {
     offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>,
     valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>,
     patterns: InternedSet<'tcx, List<ty::Pattern<'tcx>>>,
+    outlives: InternedSet<'tcx, List<ty::ArgOutlivesPredicate<'tcx>>>,
 }
 
 impl<'tcx> CtxtInterners<'tcx> {
@@ -919,6 +918,7 @@ impl<'tcx> CtxtInterners<'tcx> {
             offset_of: InternedSet::with_capacity(N),
             valtree: InternedSet::with_capacity(N),
             patterns: InternedSet::with_capacity(N),
+            outlives: InternedSet::with_capacity(N),
         }
     }
 
@@ -2700,6 +2700,7 @@ slice_interners!(
     captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>),
     offset_of: pub mk_offset_of((VariantIdx, FieldIdx)),
     patterns: pub mk_patterns(Pattern<'tcx>),
+    outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>),
 );
 
 impl<'tcx> TyCtxt<'tcx> {
@@ -3115,6 +3116,17 @@ impl<'tcx> TyCtxt<'tcx> {
         T::collect_and_apply(iter, |xs| self.mk_bound_variable_kinds(xs))
     }
 
+    pub fn mk_outlives_from_iter<I, T>(self, iter: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<
+                ty::ArgOutlivesPredicate<'tcx>,
+                &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>,
+            >,
+    {
+        T::collect_and_apply(iter, |xs| self.mk_outlives(xs))
+    }
+
     /// Emit a lint at `span` from a lint struct (some type that implements `LintDiagnostic`,
     /// typically generated by `#[derive(LintDiagnostic)]`).
     #[track_caller]
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 6e8f1e8fdd5..a7cde2ad485 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -88,7 +88,7 @@ pub use self::opaque_types::OpaqueTypeKey;
 pub use self::parameterized::ParameterizedOverTcx;
 pub use self::pattern::{Pattern, PatternKind};
 pub use self::predicate::{
-    AliasTerm, Clause, ClauseKind, CoercePredicate, ExistentialPredicate,
+    AliasTerm, ArgOutlivesPredicate, Clause, ClauseKind, CoercePredicate, ExistentialPredicate,
     ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef,
     HostEffectPredicate, NormalizesTo, OutlivesPredicate, PolyCoercePredicate,
     PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index ec2224877a8..46f254e9d30 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -26,6 +26,7 @@ pub type SubtypePredicate<'tcx> = ir::SubtypePredicate<TyCtxt<'tcx>>;
 pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate<TyCtxt<'tcx>, T>;
 pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>;
 pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>;
+pub type ArgOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>;
 pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
 pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
 pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index af9c98bd87d..ab31d943408 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -802,4 +802,5 @@ list_fold! {
     &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> : mk_poly_existential_predicates,
     &'tcx ty::List<PlaceElem<'tcx>> : mk_place_elems,
     &'tcx ty::List<ty::Pattern<'tcx>> : mk_patterns,
+    &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> : mk_outlives,
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index ccf76dc7108..986c001de5e 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -1,3 +1,4 @@
+use rustc_attr_data_structures::{AttributeKind, CoverageStatus, find_attr};
 use rustc_index::bit_set::DenseBitSet;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind};
@@ -5,7 +6,6 @@ use rustc_middle::mir::{Body, Statement, StatementKind};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::util::Providers;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::sym;
 use tracing::trace;
 
 use crate::coverage::counters::node_flow::make_node_counters;
@@ -58,26 +58,20 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
 /// Query implementation for `coverage_attr_on`.
 fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
     // Check for annotations directly on this def.
-    if let Some(attr) = tcx.get_attr(def_id, sym::coverage) {
-        match attr.meta_item_list().as_deref() {
-            Some([item]) if item.has_name(sym::off) => return false,
-            Some([item]) if item.has_name(sym::on) => return true,
-            Some(_) | None => {
-                // Other possibilities should have been rejected by `rustc_parse::validate_attr`.
-                // Use `span_delayed_bug` to avoid an ICE in failing builds (#127880).
-                tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute");
-            }
+    if let Some(coverage_status) =
+        find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status)
+    {
+        *coverage_status == CoverageStatus::On
+    } else {
+        match tcx.opt_local_parent(def_id) {
+            // Check the parent def (and so on recursively) until we find an
+            // enclosing attribute or reach the crate root.
+            Some(parent) => tcx.coverage_attr_on(parent),
+            // We reached the crate root without seeing a coverage attribute, so
+            // allow coverage instrumentation by default.
+            None => true,
         }
     }
-
-    match tcx.opt_local_parent(def_id) {
-        // Check the parent def (and so on recursively) until we find an
-        // enclosing attribute or reach the crate root.
-        Some(parent) => tcx.coverage_attr_on(parent),
-        // We reached the crate root without seeing a coverage attribute, so
-        // allow coverage instrumentation by default.
-        None => true,
-    }
 }
 
 /// Query implementation for `coverage_ids_info`.
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 6b11706d2b5..07717b7c069 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -105,7 +105,6 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::layout::HasTypingEnv;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::DUMMY_SP;
-use rustc_span::def_id::DefId;
 use smallvec::SmallVec;
 use tracing::{debug, instrument, trace};
 
@@ -130,7 +129,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
         let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
 
         for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
-            let opaque = state.new_opaque();
+            let opaque = state.new_opaque(body.local_decls[local].ty);
             state.assign(local, opaque);
         }
 
@@ -155,22 +154,6 @@ newtype_index! {
     struct VnIndex {}
 }
 
-/// Computing the aggregate's type can be quite slow, so we only keep the minimal amount of
-/// information to reconstruct it when needed.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-enum AggregateTy<'tcx> {
-    /// Invariant: this must not be used for an empty array.
-    Array,
-    Tuple,
-    Def(DefId, ty::GenericArgsRef<'tcx>),
-    RawPtr {
-        /// Needed for cast propagation.
-        data_pointer_ty: Ty<'tcx>,
-        /// The data pointer can be anything thin, so doesn't determine the output.
-        output_pointer_ty: Ty<'tcx>,
-    },
-}
-
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 enum AddressKind {
     Ref(BorrowKind),
@@ -193,7 +176,14 @@ enum Value<'tcx> {
     },
     /// An aggregate value, either tuple/closure/struct/enum.
     /// This does not contain unions, as we cannot reason with the value.
-    Aggregate(AggregateTy<'tcx>, VariantIdx, Vec<VnIndex>),
+    Aggregate(VariantIdx, Vec<VnIndex>),
+    /// A raw pointer aggregate built from a thin pointer and metadata.
+    RawPtr {
+        /// Thin pointer component. This is field 0 in MIR.
+        pointer: VnIndex,
+        /// Metadata component. This is field 1 in MIR.
+        metadata: VnIndex,
+    },
     /// This corresponds to a `[value; count]` expression.
     Repeat(VnIndex, ty::Const<'tcx>),
     /// The address of a place.
@@ -206,7 +196,7 @@ enum Value<'tcx> {
 
     // Extractions.
     /// This is the *value* obtained by projecting another value.
-    Projection(VnIndex, ProjectionElem<VnIndex, Ty<'tcx>>),
+    Projection(VnIndex, ProjectionElem<VnIndex, ()>),
     /// Discriminant of the given value.
     Discriminant(VnIndex),
     /// Length of an array or slice.
@@ -219,8 +209,6 @@ enum Value<'tcx> {
     Cast {
         kind: CastKind,
         value: VnIndex,
-        from: Ty<'tcx>,
-        to: Ty<'tcx>,
     },
 }
 
@@ -228,12 +216,13 @@ struct VnState<'body, 'tcx> {
     tcx: TyCtxt<'tcx>,
     ecx: InterpCx<'tcx, DummyMachine>,
     local_decls: &'body LocalDecls<'tcx>,
+    is_coroutine: bool,
     /// Value stored in each local.
     locals: IndexVec<Local, Option<VnIndex>>,
     /// Locals that are assigned that value.
     // This vector does not hold all the values of `VnIndex` that we create.
     rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
-    values: FxIndexSet<Value<'tcx>>,
+    values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>,
     /// Values evaluated as constants if possible.
     evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
     /// Counter to generate different values.
@@ -265,6 +254,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             tcx,
             ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine),
             local_decls,
+            is_coroutine: body.coroutine.is_some(),
             locals: IndexVec::from_elem(None, local_decls),
             rev_locals: IndexVec::with_capacity(num_values),
             values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
@@ -282,8 +272,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     }
 
     #[instrument(level = "trace", skip(self), ret)]
-    fn insert(&mut self, value: Value<'tcx>) -> VnIndex {
-        let (index, new) = self.values.insert_full(value);
+    fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex {
+        let (index, new) = self.values.insert_full((value, ty));
         let index = VnIndex::from_usize(index);
         if new {
             // Grow `evaluated` and `rev_locals` here to amortize the allocations.
@@ -305,20 +295,33 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     /// Create a new `Value` for which we have no information at all, except that it is distinct
     /// from all the others.
     #[instrument(level = "trace", skip(self), ret)]
-    fn new_opaque(&mut self) -> VnIndex {
+    fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
         let value = Value::Opaque(self.next_opaque());
-        self.insert(value)
+        self.insert(ty, value)
     }
 
     /// Create a new `Value::Address` distinct from all the others.
     #[instrument(level = "trace", skip(self), ret)]
     fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex {
+        let pty = place.ty(self.local_decls, self.tcx).ty;
+        let ty = match kind {
+            AddressKind::Ref(bk) => {
+                Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, pty, bk.to_mutbl_lossy())
+            }
+            AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()),
+        };
         let value = Value::Address { place, kind, provenance: self.next_opaque() };
-        self.insert(value)
+        self.insert(ty, value)
     }
 
+    #[inline]
     fn get(&self, index: VnIndex) -> &Value<'tcx> {
-        self.values.get_index(index.as_usize()).unwrap()
+        &self.values.get_index(index.as_usize()).unwrap().0
+    }
+
+    #[inline]
+    fn ty(&self, index: VnIndex) -> Ty<'tcx> {
+        self.values.get_index(index.as_usize()).unwrap().1
     }
 
     /// Record that `local` is assigned `value`. `local` must be SSA.
@@ -341,29 +344,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             debug_assert_ne!(disambiguator, 0);
             disambiguator
         };
-        self.insert(Value::Constant { value, disambiguator })
+        self.insert(value.ty(), Value::Constant { value, disambiguator })
     }
 
     fn insert_bool(&mut self, flag: bool) -> VnIndex {
         // Booleans are deterministic.
         let value = Const::from_bool(self.tcx, flag);
         debug_assert!(value.is_deterministic());
-        self.insert(Value::Constant { value, disambiguator: 0 })
+        self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 })
     }
 
-    fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
+    fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex {
         // Scalars are deterministic.
         let value = Const::from_scalar(self.tcx, scalar, ty);
         debug_assert!(value.is_deterministic());
-        self.insert(Value::Constant { value, disambiguator: 0 })
+        self.insert(ty, Value::Constant { value, disambiguator: 0 })
     }
 
-    fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
-        self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values))
+    fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex {
+        self.insert(ty, Value::Aggregate(VariantIdx::ZERO, values))
     }
 
-    fn insert_deref(&mut self, value: VnIndex) -> VnIndex {
-        let value = self.insert(Value::Projection(value, ProjectionElem::Deref));
+    fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex {
+        let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref));
         self.derefs.push(value);
         value
     }
@@ -371,14 +374,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     fn invalidate_derefs(&mut self) {
         for deref in std::mem::take(&mut self.derefs) {
             let opaque = self.next_opaque();
-            *self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque);
+            self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque);
         }
     }
 
     #[instrument(level = "trace", skip(self), ret)]
     fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
         use Value::*;
+        let ty = self.ty(value);
+        // Avoid computing layouts inside a coroutine, as that can cause cycles.
+        let ty = if !self.is_coroutine || ty.is_scalar() {
+            self.ecx.layout_of(ty).ok()?
+        } else {
+            return None;
+        };
         let op = match *self.get(value) {
+            _ if ty.is_zst() => ImmTy::uninit(ty).into(),
+
             Opaque(_) => return None,
             // Do not bother evaluating repeat expressions. This would uselessly consume memory.
             Repeat(..) => return None,
@@ -386,42 +398,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             Constant { ref value, disambiguator: _ } => {
                 self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()?
             }
-            Aggregate(kind, variant, ref fields) => {
+            Aggregate(variant, ref fields) => {
                 let fields = fields
                     .iter()
                     .map(|&f| self.evaluated[f].as_ref())
                     .collect::<Option<Vec<_>>>()?;
-                let ty = match kind {
-                    AggregateTy::Array => {
-                        assert!(fields.len() > 0);
-                        Ty::new_array(self.tcx, fields[0].layout.ty, fields.len() as u64)
-                    }
-                    AggregateTy::Tuple => {
-                        Ty::new_tup_from_iter(self.tcx, fields.iter().map(|f| f.layout.ty))
-                    }
-                    AggregateTy::Def(def_id, args) => {
-                        self.tcx.type_of(def_id).instantiate(self.tcx, args)
-                    }
-                    AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty,
-                };
-                let variant = if ty.is_enum() { Some(variant) } else { None };
-                let ty = self.ecx.layout_of(ty).ok()?;
-                if ty.is_zst() {
-                    ImmTy::uninit(ty).into()
-                } else if matches!(kind, AggregateTy::RawPtr { .. }) {
-                    // Pointers don't have fields, so don't `project_field` them.
-                    let data = self.ecx.read_pointer(fields[0]).discard_err()?;
-                    let meta = if fields[1].layout.is_zst() {
-                        MemPlaceMeta::None
-                    } else {
-                        MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_err()?)
-                    };
-                    let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx);
-                    ImmTy::from_immediate(ptr_imm, ty).into()
-                } else if matches!(
-                    ty.backend_repr,
-                    BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
-                ) {
+                let variant = if ty.ty.is_enum() { Some(variant) } else { None };
+                if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
+                {
                     let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
                     let variant_dest = if let Some(variant) = variant {
                         self.ecx.project_downcast(&dest, variant).discard_err()?
@@ -446,32 +430,46 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     return None;
                 }
             }
+            RawPtr { pointer, metadata } => {
+                let pointer = self.evaluated[pointer].as_ref()?;
+                let metadata = self.evaluated[metadata].as_ref()?;
+
+                // Pointers don't have fields, so don't `project_field` them.
+                let data = self.ecx.read_pointer(pointer).discard_err()?;
+                let meta = if metadata.layout.is_zst() {
+                    MemPlaceMeta::None
+                } else {
+                    MemPlaceMeta::Meta(self.ecx.read_scalar(metadata).discard_err()?)
+                };
+                let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx);
+                ImmTy::from_immediate(ptr_imm, ty).into()
+            }
 
             Projection(base, elem) => {
-                let value = self.evaluated[base].as_ref()?;
+                let base = self.evaluated[base].as_ref()?;
                 let elem = match elem {
                     ProjectionElem::Deref => ProjectionElem::Deref,
                     ProjectionElem::Downcast(name, read_variant) => {
                         ProjectionElem::Downcast(name, read_variant)
                     }
-                    ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty),
+                    ProjectionElem::Field(f, ()) => ProjectionElem::Field(f, ty.ty),
                     ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
                         ProjectionElem::ConstantIndex { offset, min_length, from_end }
                     }
                     ProjectionElem::Subslice { from, to, from_end } => {
                         ProjectionElem::Subslice { from, to, from_end }
                     }
-                    ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
-                    ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
-                    ProjectionElem::UnwrapUnsafeBinder(ty) => {
-                        ProjectionElem::UnwrapUnsafeBinder(ty)
+                    ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty.ty),
+                    ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty.ty),
+                    ProjectionElem::UnwrapUnsafeBinder(()) => {
+                        ProjectionElem::UnwrapUnsafeBinder(ty.ty)
                     }
                     // This should have been replaced by a `ConstantIndex` earlier.
                     ProjectionElem::Index(_) => return None,
                 };
-                self.ecx.project(value, elem).discard_err()?
+                self.ecx.project(base, elem).discard_err()?
             }
-            Address { place, kind, provenance: _ } => {
+            Address { place, kind: _, provenance: _ } => {
                 if !place.is_indirect_first_projection() {
                     return None;
                 }
@@ -487,19 +485,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     mplace = self.ecx.project(&mplace, proj).discard_err()?;
                 }
                 let pointer = mplace.to_ref(&self.ecx);
-                let ty = match kind {
-                    AddressKind::Ref(bk) => Ty::new_ref(
-                        self.tcx,
-                        self.tcx.lifetimes.re_erased,
-                        mplace.layout.ty,
-                        bk.to_mutbl_lossy(),
-                    ),
-                    AddressKind::Address(mutbl) => {
-                        Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy())
-                    }
-                };
-                let layout = self.ecx.layout_of(ty).ok()?;
-                ImmTy::from_immediate(pointer, layout).into()
+                ImmTy::from_immediate(pointer, ty).into()
             }
 
             Discriminant(base) => {
@@ -511,32 +497,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
             Len(slice) => {
                 let slice = self.evaluated[slice].as_ref()?;
-                let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
                 let len = slice.len(&self.ecx).discard_err()?;
-                let imm = ImmTy::from_uint(len, usize_layout);
-                imm.into()
+                ImmTy::from_uint(len, ty).into()
             }
-            NullaryOp(null_op, ty) => {
-                let layout = self.ecx.layout_of(ty).ok()?;
+            NullaryOp(null_op, arg_ty) => {
+                let arg_layout = self.ecx.layout_of(arg_ty).ok()?;
                 if let NullOp::SizeOf | NullOp::AlignOf = null_op
-                    && layout.is_unsized()
+                    && arg_layout.is_unsized()
                 {
                     return None;
                 }
                 let val = match null_op {
-                    NullOp::SizeOf => layout.size.bytes(),
-                    NullOp::AlignOf => layout.align.abi.bytes(),
+                    NullOp::SizeOf => arg_layout.size.bytes(),
+                    NullOp::AlignOf => arg_layout.align.abi.bytes(),
                     NullOp::OffsetOf(fields) => self
                         .ecx
                         .tcx
-                        .offset_of_subfield(self.typing_env(), layout, fields.iter())
+                        .offset_of_subfield(self.typing_env(), arg_layout, fields.iter())
                         .bytes(),
                     NullOp::UbChecks => return None,
                     NullOp::ContractChecks => return None,
                 };
-                let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
-                let imm = ImmTy::from_uint(val, usize_layout);
-                imm.into()
+                ImmTy::from_uint(val, ty).into()
             }
             UnaryOp(un_op, operand) => {
                 let operand = self.evaluated[operand].as_ref()?;
@@ -552,30 +534,27 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?;
                 val.into()
             }
-            Cast { kind, value, from: _, to } => match kind {
+            Cast { kind, value } => match kind {
                 CastKind::IntToInt | CastKind::IntToFloat => {
                     let value = self.evaluated[value].as_ref()?;
                     let value = self.ecx.read_immediate(value).discard_err()?;
-                    let to = self.ecx.layout_of(to).ok()?;
-                    let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?;
+                    let res = self.ecx.int_to_int_or_float(&value, ty).discard_err()?;
                     res.into()
                 }
                 CastKind::FloatToFloat | CastKind::FloatToInt => {
                     let value = self.evaluated[value].as_ref()?;
                     let value = self.ecx.read_immediate(value).discard_err()?;
-                    let to = self.ecx.layout_of(to).ok()?;
-                    let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?;
+                    let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?;
                     res.into()
                 }
                 CastKind::Transmute => {
                     let value = self.evaluated[value].as_ref()?;
-                    let to = self.ecx.layout_of(to).ok()?;
                     // `offset` for immediates generally only supports projections that match the
                     // type of the immediate. However, as a HACK, we exploit that it can also do
                     // limited transmutes: it only works between types with the same layout, and
                     // cannot transmute pointers to integers.
                     if value.as_mplace_or_imm().is_right() {
-                        let can_transmute = match (value.layout.backend_repr, to.backend_repr) {
+                        let can_transmute = match (value.layout.backend_repr, ty.backend_repr) {
                             (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => {
                                 s1.size(&self.ecx) == s2.size(&self.ecx)
                                     && !matches!(s1.primitive(), Primitive::Pointer(..))
@@ -595,13 +574,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                             return None;
                         }
                     }
-                    value.offset(Size::ZERO, to, &self.ecx).discard_err()?
+                    value.offset(Size::ZERO, ty, &self.ecx).discard_err()?
                 }
                 CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => {
                     let src = self.evaluated[value].as_ref()?;
-                    let to = self.ecx.layout_of(to).ok()?;
-                    let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_err()?;
-                    self.ecx.unsize_into(src, to, &dest.clone().into()).discard_err()?;
+                    let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
+                    self.ecx.unsize_into(src, ty, &dest.clone().into()).discard_err()?;
                     self.ecx
                         .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
                         .discard_err()?;
@@ -610,15 +588,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
                     let src = self.evaluated[value].as_ref()?;
                     let src = self.ecx.read_immediate(src).discard_err()?;
-                    let to = self.ecx.layout_of(to).ok()?;
-                    let ret = self.ecx.ptr_to_ptr(&src, to).discard_err()?;
+                    let ret = self.ecx.ptr_to_ptr(&src, ty).discard_err()?;
                     ret.into()
                 }
                 CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => {
                     let src = self.evaluated[value].as_ref()?;
                     let src = self.ecx.read_immediate(src).discard_err()?;
-                    let to = self.ecx.layout_of(to).ok()?;
-                    ImmTy::from_immediate(*src, to).into()
+                    ImmTy::from_immediate(*src, ty).into()
                 }
                 _ => return None,
             },
@@ -628,31 +604,30 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
     fn project(
         &mut self,
-        place: PlaceRef<'tcx>,
+        place_ty: PlaceTy<'tcx>,
         value: VnIndex,
         proj: PlaceElem<'tcx>,
         from_non_ssa_index: &mut bool,
-    ) -> Option<VnIndex> {
+    ) -> Option<(PlaceTy<'tcx>, VnIndex)> {
+        let projection_ty = place_ty.projection_ty(self.tcx, proj);
         let proj = match proj {
             ProjectionElem::Deref => {
-                let ty = place.ty(self.local_decls, self.tcx).ty;
-                if let Some(Mutability::Not) = ty.ref_mutability()
-                    && let Some(pointee_ty) = ty.builtin_deref(true)
-                    && pointee_ty.is_freeze(self.tcx, self.typing_env())
+                if let Some(Mutability::Not) = place_ty.ty.ref_mutability()
+                    && projection_ty.ty.is_freeze(self.tcx, self.typing_env())
                 {
                     // An immutable borrow `_x` always points to the same value for the
                     // lifetime of the borrow, so we can merge all instances of `*_x`.
-                    return Some(self.insert_deref(value));
+                    return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
                 } else {
                     return None;
                 }
             }
             ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index),
-            ProjectionElem::Field(f, ty) => {
-                if let Value::Aggregate(_, _, fields) = self.get(value) {
-                    return Some(fields[f.as_usize()]);
+            ProjectionElem::Field(f, _) => {
+                if let Value::Aggregate(_, fields) = self.get(value) {
+                    return Some((projection_ty, fields[f.as_usize()]));
                 } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value)
-                    && let Value::Aggregate(_, written_variant, fields) = self.get(*outer_value)
+                    && let Value::Aggregate(written_variant, fields) = self.get(*outer_value)
                     // This pass is not aware of control-flow, so we do not know whether the
                     // replacement we are doing is actually reachable. We could be in any arm of
                     // ```
@@ -670,14 +645,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     // a downcast to an inactive variant.
                     && written_variant == read_variant
                 {
-                    return Some(fields[f.as_usize()]);
+                    return Some((projection_ty, fields[f.as_usize()]));
                 }
-                ProjectionElem::Field(f, ty)
+                ProjectionElem::Field(f, ())
             }
             ProjectionElem::Index(idx) => {
                 if let Value::Repeat(inner, _) = self.get(value) {
                     *from_non_ssa_index |= self.locals[idx].is_none();
-                    return Some(*inner);
+                    return Some((projection_ty, *inner));
                 }
                 let idx = self.locals[idx]?;
                 ProjectionElem::Index(idx)
@@ -685,15 +660,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
                 match self.get(value) {
                     Value::Repeat(inner, _) => {
-                        return Some(*inner);
+                        return Some((projection_ty, *inner));
                     }
-                    Value::Aggregate(AggregateTy::Array, _, operands) => {
+                    Value::Aggregate(_, operands) => {
                         let offset = if from_end {
                             operands.len() - offset as usize
                         } else {
                             offset as usize
                         };
-                        return operands.get(offset).copied();
+                        let value = operands.get(offset).copied()?;
+                        return Some((projection_ty, value));
                     }
                     _ => {}
                 };
@@ -702,12 +678,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             ProjectionElem::Subslice { from, to, from_end } => {
                 ProjectionElem::Subslice { from, to, from_end }
             }
-            ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
-            ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
-            ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
+            ProjectionElem::OpaqueCast(_) => ProjectionElem::OpaqueCast(()),
+            ProjectionElem::Subtype(_) => ProjectionElem::Subtype(()),
+            ProjectionElem::UnwrapUnsafeBinder(_) => ProjectionElem::UnwrapUnsafeBinder(()),
         };
 
-        Some(self.insert(Value::Projection(value, proj)))
+        let value = self.insert(projection_ty.ty, Value::Projection(value, proj));
+        Some((projection_ty, value))
     }
 
     /// Simplify the projection chain if we know better.
@@ -769,6 +746,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
         // Invariant: `value` holds the value up-to the `index`th projection excluded.
         let mut value = self.locals[place.local]?;
+        // Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
+        let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
         let mut from_non_ssa_index = false;
         for (index, proj) in place.projection.iter().enumerate() {
             if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
@@ -786,8 +765,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 place_ref = PlaceRef { local, projection: &place.projection[index..] };
             }
 
-            let base = PlaceRef { local: place.local, projection: &place.projection[..index] };
-            value = self.project(base, value, proj, &mut from_non_ssa_index)?;
+            (place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?;
         }
 
         if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
@@ -864,14 +842,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 self.simplify_place_projection(place, location);
                 return Some(self.new_pointer(*place, AddressKind::Address(mutbl)));
             }
-            Rvalue::WrapUnsafeBinder(ref mut op, ty) => {
+            Rvalue::WrapUnsafeBinder(ref mut op, _) => {
                 let value = self.simplify_operand(op, location)?;
-                Value::Cast {
-                    kind: CastKind::Transmute,
-                    value,
-                    from: op.ty(self.local_decls, self.tcx),
-                    to: ty,
-                }
+                Value::Cast { kind: CastKind::Transmute, value }
             }
 
             // Operations.
@@ -896,18 +869,17 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             // Unsupported values.
             Rvalue::ThreadLocalRef(..) | Rvalue::ShallowInitBox(..) => return None,
         };
-        debug!(?value);
-        Some(self.insert(value))
+        let ty = rvalue.ty(self.local_decls, self.tcx);
+        Some(self.insert(ty, value))
     }
 
     fn simplify_discriminant(&mut self, place: VnIndex) -> Option<VnIndex> {
-        if let Value::Aggregate(enum_ty, variant, _) = *self.get(place)
-            && let AggregateTy::Def(enum_did, enum_args) = enum_ty
-            && let DefKind::Enum = self.tcx.def_kind(enum_did)
+        let enum_ty = self.ty(place);
+        if enum_ty.is_enum()
+            && let Value::Aggregate(variant, _) = *self.get(place)
         {
-            let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args);
             let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?;
-            return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty));
+            return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar()));
         }
 
         None
@@ -915,12 +887,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
     fn try_as_place_elem(
         &mut self,
-        proj: ProjectionElem<VnIndex, Ty<'tcx>>,
+        ty: Ty<'tcx>,
+        proj: ProjectionElem<VnIndex, ()>,
         loc: Location,
     ) -> Option<PlaceElem<'tcx>> {
         Some(match proj {
             ProjectionElem::Deref => ProjectionElem::Deref,
-            ProjectionElem::Field(idx, ty) => ProjectionElem::Field(idx, ty),
+            ProjectionElem::Field(idx, ()) => ProjectionElem::Field(idx, ty),
             ProjectionElem::Index(idx) => {
                 let Some(local) = self.try_as_local(idx, loc) else {
                     return None;
@@ -935,9 +908,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 ProjectionElem::Subslice { from, to, from_end }
             }
             ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
-            ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx),
-            ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx),
-            ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
+            ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty),
+            ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty),
+            ProjectionElem::UnwrapUnsafeBinder(()) => ProjectionElem::UnwrapUnsafeBinder(ty),
         })
     }
 
@@ -983,8 +956,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
         // Allow introducing places with non-constant offsets, as those are still better than
         // reconstructing an aggregate.
-        if let Some(place) = self.try_as_place(copy_from_local_value, location, true)
-            && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty
+        if self.ty(copy_from_local_value) == rvalue.ty(self.local_decls, self.tcx)
+            && let Some(place) = self.try_as_place(copy_from_local_value, location, true)
         {
             // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments.
             // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections.
@@ -1004,9 +977,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         rvalue: &mut Rvalue<'tcx>,
         location: Location,
     ) -> Option<VnIndex> {
+        let tcx = self.tcx;
+        let ty = rvalue.ty(self.local_decls, tcx);
+
         let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() };
 
-        let tcx = self.tcx;
         if field_ops.is_empty() {
             let is_zst = match *kind {
                 AggregateKind::Array(..)
@@ -1021,87 +996,72 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             };
 
             if is_zst {
-                let ty = rvalue.ty(self.local_decls, tcx);
                 return Some(self.insert_constant(Const::zero_sized(ty)));
             }
         }
 
-        let (mut ty, variant_index) = match *kind {
-            AggregateKind::Array(..) => {
-                assert!(!field_ops.is_empty());
-                (AggregateTy::Array, FIRST_VARIANT)
-            }
-            AggregateKind::Tuple => {
+        let fields: Vec<_> = field_ops
+            .iter_mut()
+            .map(|op| {
+                self.simplify_operand(op, location)
+                    .unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx)))
+            })
+            .collect();
+
+        let variant_index = match *kind {
+            AggregateKind::Array(..) | AggregateKind::Tuple => {
                 assert!(!field_ops.is_empty());
-                (AggregateTy::Tuple, FIRST_VARIANT)
-            }
-            AggregateKind::Closure(did, args)
-            | AggregateKind::CoroutineClosure(did, args)
-            | AggregateKind::Coroutine(did, args) => (AggregateTy::Def(did, args), FIRST_VARIANT),
-            AggregateKind::Adt(did, variant_index, args, _, None) => {
-                (AggregateTy::Def(did, args), variant_index)
+                FIRST_VARIANT
             }
+            AggregateKind::Closure(..)
+            | AggregateKind::CoroutineClosure(..)
+            | AggregateKind::Coroutine(..) => FIRST_VARIANT,
+            AggregateKind::Adt(_, variant_index, _, _, None) => variant_index,
             // Do not track unions.
             AggregateKind::Adt(_, _, _, _, Some(_)) => return None,
-            AggregateKind::RawPtr(pointee_ty, mtbl) => {
+            AggregateKind::RawPtr(..) => {
                 assert_eq!(field_ops.len(), 2);
-                let data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx);
-                let output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl);
-                (AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty }, FIRST_VARIANT)
-            }
-        };
-
-        let mut fields: Vec<_> = field_ops
-            .iter_mut()
-            .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque()))
-            .collect();
-
-        if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty {
-            let mut was_updated = false;
+                let [mut pointer, metadata] = fields.try_into().unwrap();
+
+                // Any thin pointer of matching mutability is fine as the data pointer.
+                let mut was_updated = false;
+                while let Value::Cast { kind: CastKind::PtrToPtr, value: cast_value } =
+                    self.get(pointer)
+                    && let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(*cast_value).kind()
+                    && let ty::RawPtr(_, output_mtbl) = ty.kind()
+                    && from_mtbl == output_mtbl
+                    && from_pointee_ty.is_sized(self.tcx, self.typing_env())
+                {
+                    pointer = *cast_value;
+                    was_updated = true;
+                }
 
-            // Any thin pointer of matching mutability is fine as the data pointer.
-            while let Value::Cast {
-                kind: CastKind::PtrToPtr,
-                value: cast_value,
-                from: cast_from,
-                to: _,
-            } = self.get(fields[0])
-                && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind()
-                && let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind()
-                && from_mtbl == output_mtbl
-                && from_pointee_ty.is_sized(self.tcx, self.typing_env())
-            {
-                fields[0] = *cast_value;
-                *data_pointer_ty = *cast_from;
-                was_updated = true;
-            }
+                if was_updated && let Some(op) = self.try_as_operand(pointer, location) {
+                    field_ops[FieldIdx::ZERO] = op;
+                }
 
-            if was_updated && let Some(op) = self.try_as_operand(fields[0], location) {
-                field_ops[FieldIdx::ZERO] = op;
+                return Some(self.insert(ty, Value::RawPtr { pointer, metadata }));
             }
-        }
+        };
 
-        if let AggregateTy::Array = ty
-            && fields.len() > 4
-        {
+        if ty.is_array() && fields.len() > 4 {
             let first = fields[0];
             if fields.iter().all(|&v| v == first) {
                 let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap());
                 if let Some(op) = self.try_as_operand(first, location) {
                     *rvalue = Rvalue::Repeat(op, len);
                 }
-                return Some(self.insert(Value::Repeat(first, len)));
+                return Some(self.insert(ty, Value::Repeat(first, len)));
             }
         }
 
-        if let AggregateTy::Def(_, _) = ty
-            && let Some(value) =
-                self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
+        if let Some(value) =
+            self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
         {
             return Some(value);
         }
 
-        Some(self.insert(Value::Aggregate(ty, variant_index, fields)))
+        Some(self.insert(ty, Value::Aggregate(variant_index, fields)))
     }
 
     #[instrument(level = "trace", skip(self), ret)]
@@ -1112,6 +1072,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         location: Location,
     ) -> Option<VnIndex> {
         let mut arg_index = self.simplify_operand(arg_op, location)?;
+        let arg_ty = self.ty(arg_index);
+        let ret_ty = op.ty(self.tcx, arg_ty);
 
         // PtrMetadata doesn't care about *const vs *mut vs & vs &mut,
         // so start by removing those distinctions so we can update the `Operand`
@@ -1127,8 +1089,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     // we can't always know exactly what the metadata are.
                     // To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`,
                     // it's fine to get a projection as the type.
-                    Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to }
-                        if self.pointers_have_same_metadata(*from, *to) =>
+                    Value::Cast { kind: CastKind::PtrToPtr, value: inner }
+                        if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) =>
                     {
                         arg_index = *inner;
                         was_updated = true;
@@ -1165,26 +1127,22 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => {
                 Value::BinaryOp(BinOp::Eq, *lhs, *rhs)
             }
-            (UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => {
-                return Some(fields[1]);
-            }
+            (UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(*metadata),
             // We have an unsizing cast, which assigns the length to wide pointer metadata.
             (
                 UnOp::PtrMetadata,
                 Value::Cast {
                     kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _),
-                    from,
-                    to,
-                    ..
+                    value: inner,
                 },
-            ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind()
-                && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() =>
+            ) if let ty::Slice(..) = arg_ty.builtin_deref(true).unwrap().kind()
+                && let ty::Array(_, len) = self.ty(*inner).builtin_deref(true).unwrap().kind() =>
             {
                 return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
             }
             _ => Value::UnaryOp(op, arg_index),
         };
-        Some(self.insert(value))
+        Some(self.insert(ret_ty, value))
     }
 
     #[instrument(level = "trace", skip(self), ret)]
@@ -1197,25 +1155,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     ) -> Option<VnIndex> {
         let lhs = self.simplify_operand(lhs_operand, location);
         let rhs = self.simplify_operand(rhs_operand, location);
+
         // Only short-circuit options after we called `simplify_operand`
         // on both operands for side effect.
         let mut lhs = lhs?;
         let mut rhs = rhs?;
 
-        let lhs_ty = lhs_operand.ty(self.local_decls, self.tcx);
+        let lhs_ty = self.ty(lhs);
 
         // If we're comparing pointers, remove `PtrToPtr` casts if the from
         // types of both casts and the metadata all match.
         if let BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge = op
             && lhs_ty.is_any_ptr()
-            && let Value::Cast {
-                kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, ..
-            } = self.get(lhs)
-            && let Value::Cast {
-                kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, ..
-            } = self.get(rhs)
-            && lhs_from == rhs_from
-            && self.pointers_have_same_metadata(*lhs_from, lhs_ty)
+            && let Value::Cast { kind: CastKind::PtrToPtr, value: lhs_value } = self.get(lhs)
+            && let Value::Cast { kind: CastKind::PtrToPtr, value: rhs_value } = self.get(rhs)
+            && let lhs_from = self.ty(*lhs_value)
+            && lhs_from == self.ty(*rhs_value)
+            && self.pointers_have_same_metadata(lhs_from, lhs_ty)
         {
             lhs = *lhs_value;
             rhs = *rhs_value;
@@ -1230,8 +1186,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) {
             return Some(value);
         }
+        let ty = op.ty(self.tcx, lhs_ty, self.ty(rhs));
         let value = Value::BinaryOp(op, lhs, rhs);
-        Some(self.insert(value))
+        Some(self.insert(ty, value))
     }
 
     fn simplify_binary_inner(
@@ -1323,19 +1280,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 | BinOp::Shr,
                 Left(0),
                 _,
-            ) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty),
+            ) => self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size)),
             // Attempt to simplify `x | ALL_ONES` to `ALL_ONES`.
             (BinOp::BitOr, _, Left(ones)) | (BinOp::BitOr, Left(ones), _)
                 if ones == layout.size.truncate(u128::MAX)
                     || (layout.ty.is_bool() && ones == 1) =>
             {
-                self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty)
+                self.insert_scalar(lhs_ty, Scalar::from_uint(ones, layout.size))
             }
             // Sub/Xor with itself.
             (BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked | BinOp::BitXor, a, b)
                 if a == b =>
             {
-                self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty)
+                self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size))
             }
             // Comparison:
             // - if both operands can be computed as bits, just compare the bits;
@@ -1349,8 +1306,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         };
 
         if op.is_overflowing() {
+            let ty = Ty::new_tup(self.tcx, &[self.ty(result), self.tcx.types.bool]);
             let false_val = self.insert_bool(false);
-            Some(self.insert_tuple(vec![result, false_val]))
+            Some(self.insert_tuple(ty, vec![result, false_val]))
         } else {
             Some(result)
         }
@@ -1366,9 +1324,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         use CastKind::*;
         use rustc_middle::ty::adjustment::PointerCoercion::*;
 
-        let mut from = initial_operand.ty(self.local_decls, self.tcx);
         let mut kind = *initial_kind;
         let mut value = self.simplify_operand(initial_operand, location)?;
+        let mut from = self.ty(value);
         if from == to {
             return Some(value);
         }
@@ -1376,7 +1334,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind {
             // Each reification of a generic fn may get a different pointer.
             // Do not try to merge them.
-            return Some(self.new_opaque());
+            return Some(self.new_opaque(to));
         }
 
         let mut was_ever_updated = false;
@@ -1399,23 +1357,22 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             // If a cast just casts away the metadata again, then we can get it by
             // casting the original thin pointer passed to `from_raw_parts`
             if let PtrToPtr = kind
-                && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) =
-                    self.get(value)
+                && let Value::RawPtr { pointer, .. } = self.get(value)
                 && let ty::RawPtr(to_pointee, _) = to.kind()
                 && to_pointee.is_sized(self.tcx, self.typing_env())
             {
-                from = *data_pointer_ty;
-                value = fields[0];
+                from = self.ty(*pointer);
+                value = *pointer;
                 was_updated_this_iteration = true;
-                if *data_pointer_ty == to {
-                    return Some(fields[0]);
+                if from == to {
+                    return Some(*pointer);
                 }
             }
 
             // Aggregate-then-Transmute can just transmute the original field value,
             // so long as the bytes of a value from only from a single field.
             if let Transmute = kind
-                && let Value::Aggregate(_aggregate_ty, variant_idx, field_values) = self.get(value)
+                && let Value::Aggregate(variant_idx, field_values) = self.get(value)
                 && let Some((field_idx, field_ty)) =
                     self.value_is_all_in_one_field(from, *variant_idx)
             {
@@ -1428,13 +1385,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
 
             // Various cast-then-cast cases can be simplified.
-            if let Value::Cast {
-                kind: inner_kind,
-                value: inner_value,
-                from: inner_from,
-                to: inner_to,
-            } = *self.get(value)
-            {
+            if let Value::Cast { kind: inner_kind, value: inner_value } = *self.get(value) {
+                let inner_from = self.ty(inner_value);
                 let new_kind = match (inner_kind, kind) {
                     // Even if there's a narrowing cast in here that's fine, because
                     // things like `*mut [i32] -> *mut i32 -> *const i32` and
@@ -1443,9 +1395,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     // PtrToPtr-then-Transmute is fine so long as the pointer cast is identity:
                     // `*const T -> *mut T -> NonNull<T>` is fine, but we need to check for narrowing
                     // to skip things like `*const [i32] -> *const i32 -> NonNull<T>`.
-                    (PtrToPtr, Transmute)
-                        if self.pointers_have_same_metadata(inner_from, inner_to) =>
-                    {
+                    (PtrToPtr, Transmute) if self.pointers_have_same_metadata(inner_from, from) => {
                         Some(Transmute)
                     }
                     // Similarly, for Transmute-then-PtrToPtr. Note that we need to check different
@@ -1456,7 +1406,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     // If would be legal to always do this, but we don't want to hide information
                     // from the backend that it'd otherwise be able to use for optimizations.
                     (Transmute, Transmute)
-                        if !self.type_may_have_niche_of_interest_to_backend(inner_to) =>
+                        if !self.type_may_have_niche_of_interest_to_backend(from) =>
                     {
                         Some(Transmute)
                     }
@@ -1485,7 +1435,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             *initial_kind = kind;
         }
 
-        Some(self.insert(Value::Cast { kind, value, from, to }))
+        Some(self.insert(to, Value::Cast { kind, value }))
     }
 
     fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
@@ -1507,18 +1457,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         }
 
         // We have an unsizing cast, which assigns the length to wide pointer metadata.
-        if let Value::Cast { kind, from, to, .. } = self.get(inner)
+        if let Value::Cast { kind, value: from } = self.get(inner)
             && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind
-            && let Some(from) = from.builtin_deref(true)
+            && let Some(from) = self.ty(*from).builtin_deref(true)
             && let ty::Array(_, len) = from.kind()
-            && let Some(to) = to.builtin_deref(true)
+            && let Some(to) = self.ty(inner).builtin_deref(true)
             && let ty::Slice(..) = to.kind()
         {
             return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
         }
 
         // Fallback: a symbolic `Len`.
-        Some(self.insert(Value::Len(inner)))
+        Some(self.insert(self.tcx.types.usize, Value::Len(inner)))
     }
 
     fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
@@ -1727,7 +1677,7 @@ impl<'tcx> VnState<'_, 'tcx> {
                 return Some(place);
             } else if let Value::Projection(pointer, proj) = *self.get(index)
                 && (allow_complex_projection || proj.is_stable_offset())
-                && let Some(proj) = self.try_as_place_elem(proj, loc)
+                && let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc)
             {
                 projection.push(proj);
                 index = pointer;
@@ -1755,7 +1705,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
 
     fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
         self.simplify_place_projection(place, location);
-        if context.is_mutating_use() && !place.projection.is_empty() {
+        if context.is_mutating_use() && place.is_indirect() {
             // Non-local mutation maybe invalidate deref.
             self.invalidate_derefs();
         }
@@ -1767,36 +1717,42 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
         self.super_operand(operand, location);
     }
 
-    fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) {
-        if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
-            self.simplify_place_projection(lhs, location);
-
-            let value = self.simplify_rvalue(lhs, rvalue, location);
-            let value = if let Some(local) = lhs.as_local()
-                && self.ssa.is_ssa(local)
-                // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
-                // `local` as reusable if we have an exact type match.
-                && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx)
+    fn visit_assign(
+        &mut self,
+        lhs: &mut Place<'tcx>,
+        rvalue: &mut Rvalue<'tcx>,
+        location: Location,
+    ) {
+        self.simplify_place_projection(lhs, location);
+
+        let value = self.simplify_rvalue(lhs, rvalue, location);
+        if let Some(value) = value {
+            if let Some(const_) = self.try_as_constant(value) {
+                *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
+            } else if let Some(place) = self.try_as_place(value, location, false)
+                && *rvalue != Rvalue::Use(Operand::Move(place))
+                && *rvalue != Rvalue::Use(Operand::Copy(place))
             {
-                let value = value.unwrap_or_else(|| self.new_opaque());
-                self.assign(local, value);
-                Some(value)
-            } else {
-                value
-            };
-            if let Some(value) = value {
-                if let Some(const_) = self.try_as_constant(value) {
-                    *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
-                } else if let Some(place) = self.try_as_place(value, location, false)
-                    && *rvalue != Rvalue::Use(Operand::Move(place))
-                    && *rvalue != Rvalue::Use(Operand::Copy(place))
-                {
-                    *rvalue = Rvalue::Use(Operand::Copy(place));
-                    self.reused_locals.insert(place.local);
-                }
+                *rvalue = Rvalue::Use(Operand::Copy(place));
+                self.reused_locals.insert(place.local);
             }
         }
-        self.super_statement(stmt, location);
+
+        if lhs.is_indirect() {
+            // Non-local mutation maybe invalidate deref.
+            self.invalidate_derefs();
+        }
+
+        if let Some(local) = lhs.as_local()
+            && self.ssa.is_ssa(local)
+            && let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
+            // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
+            // `local` as reusable if we have an exact type match.
+            && self.local_decls[local].ty == rvalue_ty
+        {
+            let value = value.unwrap_or_else(|| self.new_opaque(rvalue_ty));
+            self.assign(local, value);
+        }
     }
 
     fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
@@ -1804,7 +1760,8 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
             if let Some(local) = destination.as_local()
                 && self.ssa.is_ssa(local)
             {
-                let opaque = self.new_opaque();
+                let ty = self.local_decls[local].ty;
+                let opaque = self.new_opaque(ty);
                 self.assign(local, opaque);
             }
         }
diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs
index 0b97d4e6993..6de001fc998 100644
--- a/compiler/rustc_parse/src/lexer/diagnostics.rs
+++ b/compiler/rustc_parse/src/lexer/diagnostics.rs
@@ -34,9 +34,12 @@ pub(super) fn same_indentation_level(sm: &SourceMap, open_sp: Span, close_sp: Sp
 
 // When we get a `)` or `]` for `{`, we should emit help message here
 // it's more friendly compared to report `unmatched error` in later phase
-fn report_missing_open_delim(err: &mut Diag<'_>, unmatched_delims: &[UnmatchedDelim]) -> bool {
+pub(super) fn report_missing_open_delim(
+    err: &mut Diag<'_>,
+    unmatched_delims: &mut Vec<UnmatchedDelim>,
+) -> bool {
     let mut reported_missing_open = false;
-    for unmatch_brace in unmatched_delims.iter() {
+    unmatched_delims.retain(|unmatch_brace| {
         if let Some(delim) = unmatch_brace.found_delim
             && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
         {
@@ -45,13 +48,20 @@ fn report_missing_open_delim(err: &mut Diag<'_>, unmatched_delims: &[UnmatchedDe
                 Delimiter::Bracket => "[",
                 _ => unreachable!(),
             };
+
+            if let Some(unclosed_span) = unmatch_brace.unclosed_span {
+                err.span_label(unclosed_span, "the nearest open delimiter");
+            }
             err.span_label(
                 unmatch_brace.found_span.shrink_to_lo(),
                 format!("missing open `{missed_open}` for this delimiter"),
             );
             reported_missing_open = true;
+            false
+        } else {
+            true
         }
-    }
+    });
     reported_missing_open
 }
 
@@ -61,10 +71,6 @@ pub(super) fn report_suspicious_mismatch_block(
     sm: &SourceMap,
     delim: Delimiter,
 ) {
-    if report_missing_open_delim(err, &diag_info.unmatched_delims) {
-        return;
-    }
-
     let mut matched_spans: Vec<(Span, bool)> = diag_info
         .matching_block_spans
         .iter()
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index fbea958dcc5..64748199f28 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -3,7 +3,9 @@ use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, Toke
 use rustc_ast_pretty::pprust::token_to_string;
 use rustc_errors::Diag;
 
-use super::diagnostics::{report_suspicious_mismatch_block, same_indentation_level};
+use super::diagnostics::{
+    report_missing_open_delim, report_suspicious_mismatch_block, same_indentation_level,
+};
 use super::{Lexer, UnmatchedDelim};
 
 impl<'psess, 'src> Lexer<'psess, 'src> {
@@ -244,7 +246,16 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
         let msg = format!("unexpected closing delimiter: `{token_str}`");
         let mut err = self.dcx().struct_span_err(self.token.span, msg);
 
-        report_suspicious_mismatch_block(&mut err, &self.diag_info, self.psess.source_map(), delim);
+        // if there is no missing open delim, report suspicious mismatch block
+        if !report_missing_open_delim(&mut err, &mut self.diag_info.unmatched_delims) {
+            report_suspicious_mismatch_block(
+                &mut err,
+                &self.diag_info,
+                self.psess.source_map(),
+                delim,
+            );
+        }
+
         err.span_label(self.token.span, "unexpected closing delimiter");
         err
     }
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 783d79d978a..a476f0db37e 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -318,6 +318,7 @@ pub fn check_builtin_meta_item(
                 | sym::rustc_layout_scalar_valid_range_end
                 | sym::no_implicit_prelude
                 | sym::automatically_derived
+                | sym::coverage
         ) {
             return;
         }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index d8ffcedeb88..2766b14bb8d 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -288,6 +288,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 &Attribute::Parsed(AttributeKind::StdInternalSymbol(attr_span)) => {
                     self.check_rustc_std_internal_symbol(attr_span, span, target)
                 }
+                &Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => {
+                    self.check_coverage(attr_span, span, target)
+                }
                 Attribute::Unparsed(attr_item) => {
                     style = Some(attr_item.style);
                     match attr.path().as_slice() {
@@ -297,7 +300,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::diagnostic, sym::on_unimplemented, ..] => {
                             self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
                         }
-                        [sym::coverage, ..] => self.check_coverage(attr, span, target),
                         [sym::no_sanitize, ..] => {
                             self.check_no_sanitize(attr, span, target)
                         }
@@ -588,7 +590,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
 
     /// Checks that `#[coverage(..)]` is applied to a function/closure/method,
     /// or to an impl block or module.
-    fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) {
+    fn check_coverage(&self, attr_span: Span, target_span: Span, target: Target) {
         let mut not_fn_impl_mod = None;
         let mut no_body = None;
 
@@ -611,7 +613,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
 
         self.dcx().emit_err(errors::CoverageAttributeNotAllowed {
-            attr_span: attr.span(),
+            attr_span,
             not_fn_impl_mod,
             no_body,
             help: (),
diff --git a/compiler/rustc_public/src/rustc_internal/mod.rs b/compiler/rustc_public/src/rustc_internal/mod.rs
index 5d7c8256d5b..01354fc7bd4 100644
--- a/compiler/rustc_public/src/rustc_internal/mod.rs
+++ b/compiler/rustc_public/src/rustc_internal/mod.rs
@@ -144,10 +144,10 @@ where
 #[macro_export]
 macro_rules! run {
     ($args:expr, $callback_fn:ident) => {
-        run_driver!($args, || $callback_fn())
+        $crate::run_driver!($args, || $callback_fn())
     };
     ($args:expr, $callback:expr) => {
-        run_driver!($args, $callback)
+        $crate::run_driver!($args, $callback)
     };
 }
 
@@ -158,10 +158,10 @@ macro_rules! run {
 #[macro_export]
 macro_rules! run_with_tcx {
     ($args:expr, $callback_fn:ident) => {
-        run_driver!($args, |tcx| $callback_fn(tcx), with_tcx)
+        $crate::run_driver!($args, |tcx| $callback_fn(tcx), with_tcx)
     };
     ($args:expr, $callback:expr) => {
-        run_driver!($args, $callback, with_tcx)
+        $crate::run_driver!($args, $callback, with_tcx)
     };
 }
 
@@ -191,11 +191,11 @@ macro_rules! run_driver {
         use rustc_public::CompilerError;
         use std::ops::ControlFlow;
 
-        pub struct StableMir<B = (), C = (), F = fn($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C>>
+        pub struct StableMir<B = (), C = (), F = fn($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C>>
         where
             B: Send,
             C: Send,
-            F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+            F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
         {
             callback: Option<F>,
             result: Option<ControlFlow<B, C>>,
@@ -205,7 +205,7 @@ macro_rules! run_driver {
         where
             B: Send,
             C: Send,
-            F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+            F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
         {
             /// Creates a new `StableMir` instance, with given test_function and arguments.
             pub fn new(callback: F) -> Self {
@@ -240,7 +240,7 @@ macro_rules! run_driver {
         where
             B: Send,
             C: Send,
-            F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
+            F: FnOnce($($crate::optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send,
         {
             /// Called after analysis. Return value instructs the compiler whether to
             /// continue the compilation afterwards (defaults to `Compilation::Continue`)
@@ -251,7 +251,7 @@ macro_rules! run_driver {
             ) -> Compilation {
                 if let Some(callback) = self.callback.take() {
                     rustc_internal::run(tcx, || {
-                        self.result = Some(callback($(optional!($with_tcx tcx))?));
+                        self.result = Some(callback($($crate::optional!($with_tcx tcx))?));
                     })
                     .unwrap();
                     if self.result.as_ref().is_some_and(|val| val.is_continue()) {
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index e56aabfd414..675ea9d1e98 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -49,8 +49,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         ns: Namespace,
         binding: NameBinding<'ra>,
     ) {
-        let key = self.new_disambiguated_key(ident, ns);
-        if let Err(old_binding) = self.try_define(parent, key, binding, false) {
+        if let Err(old_binding) = self.try_define(parent, ident, ns, binding, false) {
             self.report_conflict(parent, ident, ns, old_binding, binding);
         }
     }
@@ -442,16 +441,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
 
         self.r.indeterminate_imports.push(import);
         match import.kind {
-            // Don't add unresolved underscore imports to modules
-            ImportKind::Single { target: Ident { name: kw::Underscore, .. }, .. } => {}
             ImportKind::Single { target, type_ns_only, .. } => {
-                self.r.per_ns(|this, ns| {
-                    if !type_ns_only || ns == TypeNS {
-                        let key = BindingKey::new(target, ns);
-                        let mut resolution = this.resolution(current_module, key).borrow_mut();
-                        resolution.single_imports.insert(import);
-                    }
-                });
+                // Don't add underscore imports to `single_imports`
+                // because they cannot define any usable names.
+                if target.name != kw::Underscore {
+                    self.r.per_ns(|this, ns| {
+                        if !type_ns_only || ns == TypeNS {
+                            let key = BindingKey::new(target, ns);
+                            let mut resolution = this.resolution(current_module, key).borrow_mut();
+                            resolution.single_imports.insert(import);
+                        }
+                    });
+                }
             }
             // We don't add prelude imports to the globs since they only affect lexical scopes,
             // which are not relevant to import resolution.
@@ -1405,9 +1406,12 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             let parent = self.parent_scope.module;
             let expansion = self.parent_scope.expansion;
             self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
-        } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) {
+        } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob)
+            && ident.name != kw::Underscore
+        {
+            // Don't add underscore names, they cannot be looked up anyway.
             let impl_def_id = self.r.tcx.local_parent(local_def_id);
-            let key = BindingKey::new(ident.normalize_to_macros_2_0(), ns);
+            let key = BindingKey::new(ident, ns);
             self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key);
         }
 
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 0a4c25b0eb0..b4c15ed1ca7 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -25,7 +25,7 @@ use rustc_span::{Ident, Span, Symbol, kw, sym};
 use smallvec::SmallVec;
 use tracing::debug;
 
-use crate::Namespace::*;
+use crate::Namespace::{self, *};
 use crate::diagnostics::{DiagMode, Suggestion, import_candidates};
 use crate::errors::{
     CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate,
@@ -338,13 +338,20 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
     pub(crate) fn try_define(
         &mut self,
         module: Module<'ra>,
-        key: BindingKey,
+        ident: Ident,
+        ns: Namespace,
         binding: NameBinding<'ra>,
         warn_ambiguity: bool,
     ) -> Result<(), NameBinding<'ra>> {
         let res = binding.res();
-        self.check_reserved_macro_name(key.ident, res);
+        self.check_reserved_macro_name(ident, res);
         self.set_binding_parent_module(binding, module);
+        // Even if underscore names cannot be looked up, we still need to add them to modules,
+        // because they can be fetched by glob imports from those modules, and bring traits
+        // into scope both directly and through glob imports.
+        let key = BindingKey::new_disambiguated(ident, ns, || {
+            (module.0.0.lazy_resolutions.borrow().len() + 1).try_into().unwrap()
+        });
         self.update_resolution(module, key, warn_ambiguity, |this, resolution| {
             if let Some(old_binding) = resolution.best_binding() {
                 if res == Res::Err && old_binding.res() != Res::Err {
@@ -383,7 +390,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     (old_glob @ true, false) | (old_glob @ false, true) => {
                         let (glob_binding, non_glob_binding) =
                             if old_glob { (old_binding, binding) } else { (binding, old_binding) };
-                        if key.ns == MacroNS
+                        if ns == MacroNS
                             && non_glob_binding.expansion != LocalExpnId::ROOT
                             && glob_binding.res() != non_glob_binding.res()
                         {
@@ -489,10 +496,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             };
             if self.is_accessible_from(binding.vis, scope) {
                 let imported_binding = self.import(binding, *import);
-                let key = BindingKey { ident, ..key };
                 let _ = self.try_define(
                     import.parent_scope.module,
-                    key,
+                    ident,
+                    key.ns,
                     imported_binding,
                     warn_ambiguity,
                 );
@@ -514,11 +521,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             let dummy_binding = self.dummy_binding;
             let dummy_binding = self.import(dummy_binding, import);
             self.per_ns(|this, ns| {
-                let key = BindingKey::new(target, ns);
-                let _ = this.try_define(import.parent_scope.module, key, dummy_binding, false);
-                this.update_resolution(import.parent_scope.module, key, false, |_, resolution| {
-                    resolution.single_imports.swap_remove(&import);
-                })
+                let module = import.parent_scope.module;
+                let _ = this.try_define(module, target, ns, dummy_binding, false);
+                // Don't remove underscores from `single_imports`, they were never added.
+                if target.name != kw::Underscore {
+                    let key = BindingKey::new(target, ns);
+                    this.update_resolution(module, key, false, |_, resolution| {
+                        resolution.single_imports.swap_remove(&import);
+                    })
+                }
             });
             self.record_use(target, dummy_binding, Used::Other);
         } else if import.imported_module.get().is_none() {
@@ -895,7 +906,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         PendingBinding::Ready(Some(imported_binding))
                     }
                     Err(Determinacy::Determined) => {
-                        // Don't update the resolution for underscores, because it was never added.
+                        // Don't remove underscores from `single_imports`, they were never added.
                         if target.name != kw::Underscore {
                             let key = BindingKey::new(target, ns);
                             this.update_resolution(parent, key, false, |_, resolution| {
@@ -1510,7 +1521,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     .is_some_and(|binding| binding.warn_ambiguity_recursive());
                 let _ = self.try_define(
                     import.parent_scope.module,
-                    key,
+                    key.ident,
+                    key.ns,
                     imported_binding,
                     warn_ambiguity,
                 );
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 753b9365cd8..93cec8daa5a 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2899,9 +2899,21 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 }
 
                 if param.ident.name == kw::UnderscoreLifetime {
+                    // To avoid emitting two similar errors,
+                    // we need to check if the span is a raw underscore lifetime, see issue #143152
+                    let is_raw_underscore_lifetime = self
+                        .r
+                        .tcx
+                        .sess
+                        .psess
+                        .raw_identifier_spans
+                        .iter()
+                        .any(|span| span == param.span());
+
                     self.r
                         .dcx()
-                        .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span });
+                        .create_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span })
+                        .emit_unless(is_raw_underscore_lifetime);
                     // Record lifetime res, so lowering knows there is something fishy.
                     self.record_lifetime_param(param.id, LifetimeRes::Error);
                     continue;
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index dae30b77ec1..f38fee8dea5 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -532,15 +532,26 @@ struct BindingKey {
     /// identifier.
     ident: Ident,
     ns: Namespace,
-    /// 0 if ident is not `_`, otherwise a value that's unique to the specific
-    /// `_` in the expanded AST that introduced this binding.
+    /// When we add an underscore binding (with ident `_`) to some module, this field has
+    /// a non-zero value that uniquely identifies this binding in that module.
+    /// For non-underscore bindings this field is zero.
+    /// When a key is constructed for name lookup (as opposed to name definition), this field is
+    /// also zero, even for underscore names, so for underscores the lookup will never succeed.
     disambiguator: u32,
 }
 
 impl BindingKey {
     fn new(ident: Ident, ns: Namespace) -> Self {
-        let ident = ident.normalize_to_macros_2_0();
-        BindingKey { ident, ns, disambiguator: 0 }
+        BindingKey { ident: ident.normalize_to_macros_2_0(), ns, disambiguator: 0 }
+    }
+
+    fn new_disambiguated(
+        ident: Ident,
+        ns: Namespace,
+        disambiguator: impl FnOnce() -> u32,
+    ) -> BindingKey {
+        let disambiguator = if ident.name == kw::Underscore { disambiguator() } else { 0 };
+        BindingKey { ident: ident.normalize_to_macros_2_0(), ns, disambiguator }
     }
 }
 
@@ -1087,8 +1098,6 @@ pub struct Resolver<'ra, 'tcx> {
     extern_module_map: RefCell<FxIndexMap<DefId, Module<'ra>>>,
     binding_parent_modules: FxHashMap<NameBinding<'ra>, Module<'ra>>,
 
-    underscore_disambiguator: u32,
-
     /// Maps glob imports to the names of items actually imported.
     glob_map: FxIndexMap<LocalDefId, FxIndexSet<Symbol>>,
     glob_error: Option<ErrorGuaranteed>,
@@ -1501,7 +1510,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             extern_crate_map: Default::default(),
             module_children: Default::default(),
             trait_map: NodeMap::default(),
-            underscore_disambiguator: 0,
             empty_module,
             local_module_map,
             extern_module_map: Default::default(),
@@ -1887,17 +1895,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         import_ids
     }
 
-    fn new_disambiguated_key(&mut self, ident: Ident, ns: Namespace) -> BindingKey {
-        let ident = ident.normalize_to_macros_2_0();
-        let disambiguator = if ident.name == kw::Underscore {
-            self.underscore_disambiguator += 1;
-            self.underscore_disambiguator
-        } else {
-            0
-        };
-        BindingKey { ident, ns, disambiguator }
-    }
-
     fn resolutions(&mut self, module: Module<'ra>) -> &'ra Resolutions<'ra> {
         if module.populate_on_access.get() {
             module.populate_on_access.set(false);
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 77ef7f56c09..0e8904a7dea 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -530,7 +530,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
         target_trait.for_each_child(self, |this, ident, ns, _binding| {
             // FIXME: Adjust hygiene for idents from globs, like for glob imports.
             if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id)
-                && overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns))
+                && overriding_keys.contains(&BindingKey::new(ident, ns))
             {
                 // The name is overridden, do not produce it from the glob delegation.
             } else {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index a91e2140fd4..d6215e1de04 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1708,6 +1708,11 @@ impl RustcOptGroup {
             OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc),
         };
     }
+
+    /// This is for diagnostics-only.
+    pub fn long_name(&self) -> &str {
+        self.long_name
+    }
 }
 
 pub fn make_opt(
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 626262c8442..2bdde2f887a 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -2256,6 +2256,8 @@ options! {
         environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
     has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "explicitly enable the `cfg(target_thread_local)` directive"),
+    higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED],
+        "allow deducing higher-ranked outlives assumptions from coroutines when proving auto traits"),
     hint_mostly_unused: bool = (false, parse_bool, [TRACKED],
         "hint that most of this crate will go unused, to minimize work for uncalled functions"),
     human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 7147f37efc1..d28a73bc139 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -840,6 +840,7 @@ symbols! {
         derive,
         derive_coerce_pointee,
         derive_const,
+        derive_const_issue: "118304",
         derive_default_enum,
         derive_smart_pointer,
         destruct,
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index a9bf5eae445..6bcb7f6e093 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -180,7 +180,7 @@ fn compute_symbol_name<'tcx>(
 
     // FIXME(eddyb) Precompute a custom symbol name based on attributes.
     let attrs = if tcx.def_kind(def_id).has_codegen_attrs() {
-        tcx.codegen_fn_attrs(def_id)
+        &tcx.codegen_instance_attrs(instance.def)
     } else {
         CodegenFnAttrs::EMPTY
     };
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 0118321befb..7c6b7b14ecb 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -33,8 +33,8 @@ impl<'tcx> InferCtxt<'tcx> {
         let ty = self.resolve_vars_if_possible(ty);
 
         // FIXME(#132279): This should be removed as it causes us to incorrectly
-        // handle opaques in their defining scope.
-        if !self.next_trait_solver() && !(param_env, ty).has_infer() {
+        // handle opaques in their defining scope, and stalled coroutines.
+        if !self.next_trait_solver() && !(param_env, ty).has_infer() && !ty.has_coroutines() {
             return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty);
         }
 
diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs
index 068e90b00b8..2b33b8ac9f8 100644
--- a/compiler/rustc_trait_selection/src/regions.rs
+++ b/compiler/rustc_trait_selection/src/regions.rs
@@ -4,7 +4,7 @@ use rustc_infer::infer::{InferCtxt, RegionResolutionError};
 use rustc_macros::extension;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::query::NoSolution;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, elaborate};
 
 use crate::traits::ScrubbedTraitError;
 use crate::traits::outlives_bounds::InferCtxtExt;
@@ -46,6 +46,11 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
             }
         }
 
+        // FIXME(-Znext-trait-solver): Normalize these.
+        let higher_ranked_assumptions = infcx.take_registered_region_assumptions();
+        let higher_ranked_assumptions =
+            elaborate::elaborate_outlives_assumptions(infcx.tcx, higher_ranked_assumptions);
+
         // FIXME: This needs to be modified so that we normalize the known type
         // outlives obligations then elaborate them into their region/type components.
         // Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even
@@ -59,6 +64,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
                 assumed_wf_tys,
                 disable_implied_bounds_hack,
             ),
+            higher_ranked_assumptions,
         )
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs
index 5a5d16167d2..f58961683a9 100644
--- a/compiler/rustc_trait_selection/src/solve.rs
+++ b/compiler/rustc_trait_selection/src/solve.rs
@@ -7,7 +7,7 @@ mod normalize;
 mod select;
 
 pub(crate) use delegate::SolverDelegate;
-pub use fulfill::{FulfillmentCtxt, NextSolverError};
+pub use fulfill::{FulfillmentCtxt, NextSolverError, StalledOnCoroutines};
 pub(crate) use normalize::deeply_normalize_for_diagnostics;
 pub use normalize::{
     deeply_normalize, deeply_normalize_with_skipped_universes,
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 1a24254d57f..17429e15cce 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -206,14 +206,18 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
     }
 
-    fn make_deduplicated_outlives_constraints(
-        &self,
-    ) -> Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
+    fn make_deduplicated_outlives_constraints(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
         // Cannot use `take_registered_region_obligations` as we may compute the response
         // inside of a `probe` whenever we have multiple choices inside of the solver.
         let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
+        let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
         let region_constraints = self.0.with_region_constraints(|region_constraints| {
-            make_query_region_constraints(self.tcx, region_obligations, region_constraints)
+            make_query_region_constraints(
+                self.tcx,
+                region_obligations,
+                region_constraints,
+                region_assumptions,
+            )
         });
 
         let mut seen = FxHashSet::default();
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index d56042a5ca2..3ce0f025512 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -10,7 +10,8 @@ use rustc_infer::traits::{
     FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
 };
 use rustc_middle::ty::{
-    self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode,
+    self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+    TypingMode,
 };
 use rustc_next_trait_solver::delegate::SolverDelegate as _;
 use rustc_next_trait_solver::solve::{
@@ -254,7 +255,7 @@ where
         &mut self,
         infcx: &InferCtxt<'tcx>,
     ) -> PredicateObligations<'tcx> {
-        let stalled_generators = match infcx.typing_mode() {
+        let stalled_coroutines = match infcx.typing_mode() {
             TypingMode::Analysis { defining_opaque_types_and_generators } => {
                 defining_opaque_types_and_generators
             }
@@ -264,7 +265,7 @@ where
             | TypingMode::PostAnalysis => return Default::default(),
         };
 
-        if stalled_generators.is_empty() {
+        if stalled_coroutines.is_empty() {
             return Default::default();
         }
 
@@ -275,7 +276,7 @@ where
                         .visit_proof_tree(
                             obl.as_goal(),
                             &mut StalledOnCoroutines {
-                                stalled_generators,
+                                stalled_coroutines,
                                 span: obl.cause.span,
                                 cache: Default::default(),
                             },
@@ -297,10 +298,10 @@ where
 ///
 /// This function can be also return false positives, which will lead to poor diagnostics
 /// so we want to keep this visitor *precise* too.
-struct StalledOnCoroutines<'tcx> {
-    stalled_generators: &'tcx ty::List<LocalDefId>,
-    span: Span,
-    cache: DelayedSet<Ty<'tcx>>,
+pub struct StalledOnCoroutines<'tcx> {
+    pub stalled_coroutines: &'tcx ty::List<LocalDefId>,
+    pub span: Span,
+    pub cache: DelayedSet<Ty<'tcx>>,
 }
 
 impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
@@ -330,12 +331,14 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> {
         }
 
         if let ty::CoroutineWitness(def_id, _) = *ty.kind()
-            && def_id.as_local().is_some_and(|def_id| self.stalled_generators.contains(&def_id))
+            && def_id.as_local().is_some_and(|def_id| self.stalled_coroutines.contains(&def_id))
         {
-            return ControlFlow::Break(());
+            ControlFlow::Break(())
+        } else if ty.has_coroutines() {
+            ty.super_visit_with(self)
+        } else {
+            ControlFlow::Continue(())
         }
-
-        ty.super_visit_with(self)
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index ce5a4edeaaa..f50f01a285b 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -461,6 +461,7 @@ fn impl_intersection_has_negative_obligation(
     // requirements, when proving the negated where clauses below.
     drop(equate_obligations);
     drop(infcx.take_registered_region_obligations());
+    drop(infcx.take_registered_region_assumptions());
     drop(infcx.take_and_reset_region_constraints());
 
     plug_infer_with_placeholders(
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 2b5a41ef5a7..e35f89358e9 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -3,6 +3,7 @@ use std::marker::PhantomData;
 use rustc_data_structures::obligation_forest::{
     Error, ForestObligation, ObligationForest, ObligationProcessor, Outcome, ProcessResult,
 };
+use rustc_hir::def_id::LocalDefId;
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::traits::{
     FromSolverError, PolyTraitObligation, PredicateObligations, ProjectionCacheKey, SelectionError,
@@ -12,8 +13,10 @@ use rustc_middle::bug;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{
-    self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode, may_use_unstable_feature,
+    self, Binder, Const, GenericArgsRef, TypeVisitable, TypeVisitableExt, TypingMode,
+    may_use_unstable_feature,
 };
+use rustc_span::DUMMY_SP;
 use thin_vec::{ThinVec, thin_vec};
 use tracing::{debug, debug_span, instrument};
 
@@ -26,6 +29,7 @@ use super::{
 };
 use crate::error_reporting::InferCtxtErrorExt;
 use crate::infer::{InferCtxt, TyOrConstInferVar};
+use crate::solve::StalledOnCoroutines;
 use crate::traits::normalize::normalize_with_depth_to;
 use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
@@ -168,8 +172,25 @@ where
         &mut self,
         infcx: &InferCtxt<'tcx>,
     ) -> PredicateObligations<'tcx> {
-        let mut processor =
-            DrainProcessor { removed_predicates: PredicateObligations::new(), infcx };
+        let stalled_coroutines = match infcx.typing_mode() {
+            TypingMode::Analysis { defining_opaque_types_and_generators } => {
+                defining_opaque_types_and_generators
+            }
+            TypingMode::Coherence
+            | TypingMode::Borrowck { defining_opaque_types: _ }
+            | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
+            | TypingMode::PostAnalysis => return Default::default(),
+        };
+
+        if stalled_coroutines.is_empty() {
+            return Default::default();
+        }
+
+        let mut processor = DrainProcessor {
+            infcx,
+            removed_predicates: PredicateObligations::new(),
+            stalled_coroutines,
+        };
         let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut processor);
         assert!(outcome.errors.is_empty());
         return processor.removed_predicates;
@@ -177,6 +198,7 @@ where
         struct DrainProcessor<'a, 'tcx> {
             infcx: &'a InferCtxt<'tcx>,
             removed_predicates: PredicateObligations<'tcx>,
+            stalled_coroutines: &'tcx ty::List<LocalDefId>,
         }
 
         impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> {
@@ -185,10 +207,14 @@ where
             type OUT = Outcome<Self::Obligation, Self::Error>;
 
             fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool {
-                pending_obligation
-                    .stalled_on
-                    .iter()
-                    .any(|&var| self.infcx.ty_or_const_infer_var_changed(var))
+                self.infcx
+                    .resolve_vars_if_possible(pending_obligation.obligation.predicate)
+                    .visit_with(&mut StalledOnCoroutines {
+                        stalled_coroutines: self.stalled_coroutines,
+                        span: DUMMY_SP,
+                        cache: Default::default(),
+                    })
+                    .is_break()
             }
 
             fn process_obligation(
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 59d3ac21387..53518038f8d 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -78,7 +78,10 @@ fn implied_outlives_bounds<'a, 'tcx>(
     bounds.retain(|bound| !bound.has_placeholders());
 
     if !constraints.is_empty() {
-        let QueryRegionConstraints { outlives } = constraints;
+        // FIXME(higher_ranked_auto): Should we register assumptions here?
+        // We otherwise would get spurious errors if normalizing an implied
+        // outlives bound required proving some higher-ranked coroutine obl.
+        let QueryRegionConstraints { outlives, assumptions: _ } = constraints;
         let cause = ObligationCause::misc(span, body_id);
         for &(predicate, _) in &outlives {
             infcx.register_outlives_constraint(predicate, &cause);
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
index 3b549244431..f027ba1c5cb 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -80,6 +80,11 @@ where
         pre_obligations.is_empty(),
         "scrape_region_constraints: incoming region obligations = {pre_obligations:#?}",
     );
+    let pre_assumptions = infcx.take_registered_region_assumptions();
+    assert!(
+        pre_assumptions.is_empty(),
+        "scrape_region_constraints: incoming region assumptions = {pre_assumptions:#?}",
+    );
 
     let value = infcx.commit_if_ok(|_| {
         let ocx = ObligationCtxt::new(infcx);
@@ -100,11 +105,13 @@ where
     let value = infcx.resolve_vars_if_possible(value);
 
     let region_obligations = infcx.take_registered_region_obligations();
+    let region_assumptions = infcx.take_registered_region_assumptions();
     let region_constraint_data = infcx.take_and_reset_region_constraints();
     let region_constraints = query_response::make_query_region_constraints(
         infcx.tcx,
         region_obligations,
         &region_constraint_data,
+        region_assumptions,
     );
 
     if region_constraints.is_empty() {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 4bdf04311a0..018e9748cf0 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -180,8 +180,9 @@ where
             span,
         )?;
         output.error_info = error_info;
-        if let Some(QueryRegionConstraints { outlives }) = output.constraints {
+        if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints {
             region_constraints.outlives.extend(outlives.iter().cloned());
+            region_constraints.assumptions.extend(assumptions.iter().cloned());
         }
         output.constraints = if region_constraints.is_empty() {
             None
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 cc188a280aa..2c7089507a8 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -842,6 +842,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     }
                 }
 
+                ty::CoroutineWitness(def_id, _) => {
+                    if self.should_stall_coroutine_witness(def_id) {
+                        candidates.ambiguous = true;
+                    } else {
+                        candidates.vec.push(AutoImplCandidate);
+                    }
+                }
+
                 ty::Bool
                 | ty::Char
                 | ty::Int(_)
@@ -861,7 +869,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | ty::Coroutine(..)
                 | ty::Never
                 | ty::Tuple(_)
-                | ty::CoroutineWitness(..)
                 | ty::UnsafeBinder(_) => {
                     // Only consider auto impls of unsafe traits when there are
                     // no unsafe fields.
@@ -1119,12 +1126,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         match *self_ty.kind() {
             // These impls are built-in because we cannot express sufficiently
             // generic impls in libcore.
-            ty::FnDef(..)
-            | ty::FnPtr(..)
-            | ty::Error(_)
-            | ty::Tuple(..)
-            | ty::CoroutineWitness(..)
-            | ty::Pat(..) => {
+            ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::Tuple(..) | ty::Pat(..) => {
                 candidates.vec.push(BuiltinCandidate);
             }
 
@@ -1192,6 +1194,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 }
             }
 
+            ty::CoroutineWitness(coroutine_def_id, _) => {
+                if self.should_stall_coroutine_witness(coroutine_def_id) {
+                    candidates.ambiguous = true;
+                } else {
+                    candidates.vec.push(SizedCandidate);
+                }
+            }
+
             // Fallback to whatever user-defined impls or param-env clauses exist in this case.
             ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {}
 
@@ -1229,7 +1239,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             | ty::Char
             | ty::Ref(..)
             | ty::Coroutine(..)
-            | ty::CoroutineWitness(..)
             | ty::Array(..)
             | ty::Closure(..)
             | ty::CoroutineClosure(..)
@@ -1238,6 +1247,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 candidates.vec.push(SizedCandidate);
             }
 
+            ty::CoroutineWitness(coroutine_def_id, _) => {
+                if self.should_stall_coroutine_witness(coroutine_def_id) {
+                    candidates.ambiguous = true;
+                } else {
+                    candidates.vec.push(SizedCandidate);
+                }
+            }
+
             // Conditionally `Sized`.
             ty::Tuple(..) | ty::Pat(..) | ty::Adt(..) | ty::UnsafeBinder(_) => {
                 candidates.vec.push(SizedCandidate);
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index ee8cef20279..488094b15ac 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -411,18 +411,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty));
             let self_ty = self.infcx.enter_forall_and_leak_universe(self_ty);
 
-            let types = self.constituent_types_for_ty(self_ty)?;
-            let types = self.infcx.enter_forall_and_leak_universe(types);
+            let constituents = self.constituent_types_for_auto_trait(self_ty)?;
+            let constituents = self.infcx.enter_forall_and_leak_universe(constituents);
 
             let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
-            let obligations = self.collect_predicates_for_types(
+            let mut obligations = self.collect_predicates_for_types(
                 obligation.param_env,
-                cause,
+                cause.clone(),
                 obligation.recursion_depth + 1,
                 obligation.predicate.def_id(),
-                types,
+                constituents.types,
             );
 
+            // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types`
+            // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really
+            // matter yet.
+            for assumption in constituents.assumptions {
+                let assumption = normalize_with_depth_to(
+                    self,
+                    obligation.param_env,
+                    cause.clone(),
+                    obligation.recursion_depth + 1,
+                    assumption,
+                    &mut obligations,
+                );
+                self.infcx.register_region_assumption(assumption);
+            }
+
             Ok(obligations)
         })
     }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 10bcf861d35..2b563c5b8d5 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -20,6 +20,7 @@ use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::infer::at::ToTrace;
 use rustc_infer::infer::relate::TypeRelation;
 use rustc_infer::traits::{PredicateObligations, TraitObligation};
+use rustc_macros::{TypeFoldable, TypeVisitable};
 use rustc_middle::bug;
 use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds};
 pub use rustc_middle::traits::select::*;
@@ -1512,7 +1513,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 defining_opaque_types_and_generators: defining_opaque_types,
             }
             | TypingMode::Borrowck { defining_opaque_types } => {
-                defining_opaque_types.is_empty() || !pred.has_opaque_types()
+                defining_opaque_types.is_empty()
+                    || (!pred.has_opaque_types() && !pred.has_coroutines())
             }
             // The hidden types of `defined_opaque_types` is not local to the current
             // inference context, so we can freely move this to the global cache.
@@ -2255,10 +2257,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
     /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32]
     /// ```
     #[instrument(level = "debug", skip(self), ret)]
-    fn constituent_types_for_ty(
+    fn constituent_types_for_auto_trait(
         &self,
         t: Ty<'tcx>,
-    ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
+    ) -> Result<ty::Binder<'tcx, AutoImplConstituents<'tcx>>, SelectionError<'tcx>> {
         Ok(match *t.kind() {
             ty::Uint(_)
             | ty::Int(_)
@@ -2269,17 +2271,26 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             | ty::Error(_)
             | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
             | ty::Never
-            | ty::Char => ty::Binder::dummy(Vec::new()),
+            | ty::Char => {
+                ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] })
+            }
 
             // This branch is only for `experimental_default_bounds`.
             // Other foreign types were rejected earlier in
             // `assemble_candidates_from_auto_impls`.
-            ty::Foreign(..) => ty::Binder::dummy(Vec::new()),
+            ty::Foreign(..) => {
+                ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] })
+            }
 
-            ty::UnsafeBinder(ty) => ty.map_bound(|ty| vec![ty]),
+            ty::UnsafeBinder(ty) => {
+                ty.map_bound(|ty| AutoImplConstituents { types: vec![ty], assumptions: vec![] })
+            }
 
             // Treat this like `struct str([u8]);`
-            ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]),
+            ty::Str => ty::Binder::dummy(AutoImplConstituents {
+                types: vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)],
+                assumptions: vec![],
+            }),
 
             ty::Placeholder(..)
             | ty::Dynamic(..)
@@ -2291,30 +2302,41 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             }
 
             ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => {
-                ty::Binder::dummy(vec![element_ty])
+                ty::Binder::dummy(AutoImplConstituents {
+                    types: vec![element_ty],
+                    assumptions: vec![],
+                })
             }
 
-            ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => ty::Binder::dummy(vec![ty]),
+            ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => {
+                ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
+            }
 
             ty::Tuple(tys) => {
                 // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
-                ty::Binder::dummy(tys.iter().collect())
+                ty::Binder::dummy(AutoImplConstituents {
+                    types: tys.iter().collect(),
+                    assumptions: vec![],
+                })
             }
 
             ty::Closure(_, args) => {
                 let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
-                ty::Binder::dummy(vec![ty])
+                ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
             }
 
             ty::CoroutineClosure(_, args) => {
                 let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
-                ty::Binder::dummy(vec![ty])
+                ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] })
             }
 
             ty::Coroutine(_, args) => {
                 let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
                 let witness = args.as_coroutine().witness();
-                ty::Binder::dummy([ty].into_iter().chain(iter::once(witness)).collect())
+                ty::Binder::dummy(AutoImplConstituents {
+                    types: [ty].into_iter().chain(iter::once(witness)).collect(),
+                    assumptions: vec![],
+                })
             }
 
             ty::CoroutineWitness(def_id, args) => self
@@ -2322,16 +2344,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 .tcx
                 .coroutine_hidden_types(def_id)
                 .instantiate(self.infcx.tcx, args)
-                .map_bound(|witness| witness.types.to_vec()),
+                .map_bound(|witness| AutoImplConstituents {
+                    types: witness.types.to_vec(),
+                    assumptions: witness.assumptions.to_vec(),
+                }),
 
             // For `PhantomData<T>`, we pass `T`.
             ty::Adt(def, args) if def.is_phantom_data() => {
-                ty::Binder::dummy(args.types().collect())
+                ty::Binder::dummy(AutoImplConstituents {
+                    types: args.types().collect(),
+                    assumptions: vec![],
+                })
             }
 
-            ty::Adt(def, args) => {
-                ty::Binder::dummy(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect())
-            }
+            ty::Adt(def, args) => ty::Binder::dummy(AutoImplConstituents {
+                types: def.all_fields().map(|f| f.ty(self.tcx(), args)).collect(),
+                assumptions: vec![],
+            }),
 
             ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
                 if self.infcx.can_define_opaque_ty(def_id) {
@@ -2341,7 +2370,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                     // which enforces a DAG between the functions requiring
                     // the auto trait bounds in question.
                     match self.tcx().type_of_opaque(def_id) {
-                        Ok(ty) => ty::Binder::dummy(vec![ty.instantiate(self.tcx(), args)]),
+                        Ok(ty) => ty::Binder::dummy(AutoImplConstituents {
+                            types: vec![ty.instantiate(self.tcx(), args)],
+                            assumptions: vec![],
+                        }),
                         Err(_) => {
                             return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
                         }
@@ -2811,6 +2843,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
 
         obligations
     }
+
+    fn should_stall_coroutine_witness(&self, def_id: DefId) -> bool {
+        match self.infcx.typing_mode() {
+            TypingMode::Analysis { defining_opaque_types_and_generators: stalled_generators } => {
+                def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id))
+            }
+            TypingMode::Coherence
+            | TypingMode::PostAnalysis
+            | TypingMode::Borrowck { defining_opaque_types: _ }
+            | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => false,
+        }
+    }
 }
 
 impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
@@ -3121,3 +3165,9 @@ pub(crate) enum ProjectionMatchesProjection {
     Ambiguous,
     No,
 }
+
+#[derive(Clone, Debug, TypeFoldable, TypeVisitable)]
+pub(crate) struct AutoImplConstituents<'tcx> {
+    pub types: Vec<Ty<'tcx>>,
+    pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
+}
diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs
index 447e13126cc..87d17f3e131 100644
--- a/compiler/rustc_traits/src/coroutine_witnesses.rs
+++ b/compiler/rustc_traits/src/coroutine_witnesses.rs
@@ -1,5 +1,10 @@
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty::{self, TyCtxt, fold_regions};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
+use rustc_infer::infer::resolve::OpportunisticRegionResolver;
+use rustc_infer::traits::{Obligation, ObligationCause};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions};
+use rustc_trait_selection::traits::{ObligationCtxt, with_replaced_escaping_bound_vars};
 
 /// Return the set of types that should be taken into account when checking
 /// trait bounds on a coroutine's internal state. This properly replaces
@@ -30,8 +35,57 @@ pub(crate) fn coroutine_hidden_types<'tcx>(
             }),
     );
 
+    let assumptions = compute_assumptions(tcx, def_id, bound_tys);
+
     ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
-        ty::CoroutineWitnessTypes { types: bound_tys },
+        ty::CoroutineWitnessTypes { types: bound_tys, assumptions },
         tcx.mk_bound_variable_kinds(&vars),
     ))
 }
+
+fn compute_assumptions<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    bound_tys: &'tcx ty::List<Ty<'tcx>>,
+) -> &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> {
+    let infcx = tcx.infer_ctxt().build(ty::TypingMode::Analysis {
+        defining_opaque_types_and_generators: ty::List::empty(),
+    });
+    with_replaced_escaping_bound_vars(&infcx, &mut vec![None], bound_tys, |bound_tys| {
+        let param_env = tcx.param_env(def_id);
+        let ocx = ObligationCtxt::new(&infcx);
+
+        ocx.register_obligations(bound_tys.iter().map(|ty| {
+            Obligation::new(
+                tcx,
+                ObligationCause::dummy(),
+                param_env,
+                ty::ClauseKind::WellFormed(ty.into()),
+            )
+        }));
+        let _errors = ocx.select_all_or_error();
+
+        let region_obligations = infcx.take_registered_region_obligations();
+        let region_assumptions = infcx.take_registered_region_assumptions();
+        let region_constraints = infcx.take_and_reset_region_constraints();
+
+        let outlives = make_query_region_constraints(
+            tcx,
+            region_obligations,
+            &region_constraints,
+            region_assumptions,
+        )
+        .outlives
+        .fold_with(&mut OpportunisticRegionResolver::new(&infcx));
+
+        tcx.mk_outlives_from_iter(
+            outlives
+                .into_iter()
+                .map(|(o, _)| o)
+                // FIXME(higher_ranked_auto): We probably should deeply resolve these before
+                // filtering out infers which only correspond to unconstrained infer regions
+                // which we can sometimes get.
+                .filter(|o| !o.has_infer()),
+        )
+    })
+}
diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs
index 38008248757..dc15cc3eb55 100644
--- a/compiler/rustc_type_ir/src/elaborate.rs
+++ b/compiler/rustc_type_ir/src/elaborate.rs
@@ -371,3 +371,54 @@ impl<I: Interner, It: Iterator<Item = I::Clause>> Iterator for FilterToTraits<I,
         (0, upper)
     }
 }
+
+pub fn elaborate_outlives_assumptions<I: Interner>(
+    cx: I,
+    assumptions: impl IntoIterator<Item = ty::OutlivesPredicate<I, I::GenericArg>>,
+) -> HashSet<ty::OutlivesPredicate<I, I::GenericArg>> {
+    let mut collected = HashSet::default();
+
+    for ty::OutlivesPredicate(arg1, r2) in assumptions {
+        collected.insert(ty::OutlivesPredicate(arg1, r2));
+        match arg1.kind() {
+            // Elaborate the components of an type, since we may have substituted a
+            // generic coroutine with a more specific type.
+            ty::GenericArgKind::Type(ty1) => {
+                let mut components = smallvec![];
+                push_outlives_components(cx, ty1, &mut components);
+                for c in components {
+                    match c {
+                        Component::Region(r1) => {
+                            if !r1.is_bound() {
+                                collected.insert(ty::OutlivesPredicate(r1.into(), r2));
+                            }
+                        }
+
+                        Component::Param(p) => {
+                            let ty = Ty::new_param(cx, p);
+                            collected.insert(ty::OutlivesPredicate(ty.into(), r2));
+                        }
+
+                        Component::Placeholder(p) => {
+                            let ty = Ty::new_placeholder(cx, p);
+                            collected.insert(ty::OutlivesPredicate(ty.into(), r2));
+                        }
+
+                        Component::Alias(alias_ty) => {
+                            collected.insert(ty::OutlivesPredicate(alias_ty.to_ty(cx).into(), r2));
+                        }
+
+                        Component::UnresolvedInferenceVariable(_) | Component::EscapingAlias(_) => {
+                        }
+                    }
+                }
+            }
+            // Nothing to elaborate for a region.
+            ty::GenericArgKind::Lifetime(_) => {}
+            // Consts don't really participate in outlives.
+            ty::GenericArgKind::Const(_) => {}
+        }
+    }
+
+    collected
+}
diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs
index a231908f874..d7b9e0ca340 100644
--- a/compiler/rustc_type_ir/src/flags.rs
+++ b/compiler/rustc_type_ir/src/flags.rs
@@ -130,6 +130,12 @@ bitflags::bitflags! {
 
         /// Does this have any binders with bound vars (e.g. that need to be anonymized)?
         const HAS_BINDER_VARS             = 1 << 23;
+
+        /// Does this type have any coroutine witnesses in it?
+        // FIXME: This should probably be changed to track whether the type has any
+        // *coroutines* in it, though this will happen if we remove coroutine witnesses
+        // altogether.
+        const HAS_TY_CORO                 = 1 << 24;
     }
 }
 
@@ -240,10 +246,12 @@ impl<I: Interner> FlagComputation<I> {
                 self.add_flags(TypeFlags::HAS_TY_PARAM);
             }
 
-            ty::Closure(_, args)
-            | ty::Coroutine(_, args)
-            | ty::CoroutineClosure(_, args)
-            | ty::CoroutineWitness(_, args) => {
+            ty::Closure(_, args) | ty::Coroutine(_, args) | ty::CoroutineClosure(_, args) => {
+                self.add_args(args.as_slice());
+            }
+
+            ty::CoroutineWitness(_, args) => {
+                self.add_flags(TypeFlags::HAS_TY_CORO);
                 self.add_args(args.as_slice());
             }
 
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 0ec326d2116..e3231244577 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -146,6 +146,13 @@ pub trait Interner:
     type BoundRegion: BoundVarLike<Self>;
     type PlaceholderRegion: PlaceholderLike<Self, Bound = Self::BoundRegion>;
 
+    type RegionAssumptions: Copy
+        + Debug
+        + Hash
+        + Eq
+        + SliceLike<Item = ty::OutlivesPredicate<Self, Self::GenericArg>>
+        + TypeFoldable<Self>;
+
     // Predicates
     type ParamEnv: ParamEnv<Self>;
     type Predicate: Predicate<Self>;
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index db6fbefcf05..7c665424750 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -1150,4 +1150,5 @@ pub struct FnHeader<I: Interner> {
 #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
 pub struct CoroutineWitnessTypes<I: Interner> {
     pub types: I::Tys,
+    pub assumptions: I::RegionAssumptions,
 }
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index a96ac97f785..5104484e9c4 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -269,6 +269,10 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
         self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
     }
 
+    fn has_coroutines(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_CORO)
+    }
+
     fn references_error(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_ERROR)
     }