about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-24 08:12:24 +0000
committerbors <bors@rust-lang.org>2024-01-24 08:12:24 +0000
commitcf22ee0c0c86c0c60a3cee8a66bd361861e681b1 (patch)
treec50ccb0ecdbaa6422d4161c74abd2d120723c46a /compiler
parentb5a3acfe2b39c40d220bb9fc26b0719a7e073ac3 (diff)
parenta9bc7e99a32fe75bcc3d025ce8d08efb96b97cf9 (diff)
downloadrust-cf22ee0c0c86c0c60a3cee8a66bd361861e681b1.tar.gz
rust-cf22ee0c0c86c0c60a3cee8a66bd361861e681b1.zip
Auto merge of #3275 - rust-lang:rustup-2024-01-24, r=RalfJung
Automatic Rustup
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs1
-rw-r--r--compiler/rustc_const_eval/messages.ftl38
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs32
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs2
-rw-r--r--compiler/rustc_const_eval/src/errors.rs51
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs3
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs560
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs146
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs10
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/autoderef.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs78
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs25
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs4
-rw-r--r--compiler/rustc_index/src/bit_set.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs118
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/pointer.rs6
-rw-r--r--compiler/rustc_middle/src/query/mod.rs9
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs18
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs38
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs45
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse/instruction.rs4
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/initialized.rs7
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs10
-rw-r--r--compiler/rustc_mir_dataflow/src/points.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/rustc_peek.rs4
-rw-r--r--compiler/rustc_mir_transform/src/jump_threading.rs17
-rw-r--r--compiler/rustc_mir_transform/src/nrvo.rs4
-rw-r--r--compiler/rustc_mir_transform/src/remove_storage_markers.rs8
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs3
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs5
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/spec/base/dragonfly.rs1
-rw-r--r--compiler/rustc_target/src/spec/base/freebsd.rs1
-rw-r--r--compiler/rustc_target/src/spec/base/netbsd.rs1
-rw-r--r--compiler/rustc_target/src/spec/mod.rs1
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs36
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs39
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs7
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs7
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs213
54 files changed, 885 insertions, 749 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index eec128b5f1d..18975a4e3b2 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -1,6 +1,6 @@
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::graph::WithSuccessors;
-use rustc_index::bit_set::HybridBitSet;
+use rustc_index::bit_set::BitSet;
 use rustc_index::interval::IntervalSet;
 use rustc_infer::infer::canonical::QueryRegionConstraints;
 use rustc_infer::infer::outlives::for_liveness;
@@ -135,7 +135,7 @@ struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {
     cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>,
 
     /// Set of points that define the current local.
-    defs: HybridBitSet<PointIndex>,
+    defs: BitSet<PointIndex>,
 
     /// Points where the current variable is "use live" -- meaning
     /// that there is a future "full use" that may use its value.
@@ -158,7 +158,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
         let num_points = cx.elements.num_points();
         LivenessResults {
             cx,
-            defs: HybridBitSet::new_empty(num_points),
+            defs: BitSet::new_empty(num_points),
             use_live_at: IntervalSet::new(num_points),
             drop_live_at: IntervalSet::new(num_points),
             drop_locations: vec![],
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 881c0c0b56b..0afd6d0e670 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -682,7 +682,6 @@ fn codegen_stmt<'tcx>(
                                 args,
                                 ty::ClosureKind::FnOnce,
                             )
-                            .expect("failed to normalize and resolve closure during codegen")
                             .polymorphize(fx.tcx);
                             let func_ref = fx.get_function_ref(instance);
                             let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref);
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 02b51dfe5bf..266505d3f26 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -435,7 +435,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                     args,
                                     ty::ClosureKind::FnOnce,
                                 )
-                                .expect("failed to normalize and resolve closure during codegen")
                                 .polymorphize(bx.cx().tcx());
                                 OperandValue::Immediate(bx.cx().get_fn_addr(instance))
                             }
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 4a426ed16e5..e7e8b2b3600 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -46,8 +46,8 @@ const_eval_dangling_int_pointer =
     {$bad_pointer_message}: {$pointer} is a dangling pointer (it has no provenance)
 const_eval_dangling_null_pointer =
     {$bad_pointer_message}: null pointer is a dangling pointer (it has no provenance)
-const_eval_dangling_ptr_in_final = encountered dangling pointer in final constant
 
+const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind}
 const_eval_dead_local =
     accessing a dead local variable
 const_eval_dealloc_immutable =
@@ -134,6 +134,14 @@ const_eval_interior_mutable_data_refer =
         This would make multiple uses of a constant to be able to see different values and allow circumventing
         the `Send` and `Sync` requirements for shared mutable data, which is unsound.
 
+const_eval_intern_kind = {$kind ->
+    [static] static
+    [static_mut] mutable static
+    [const] constant
+    [promoted] promoted
+    *[other] {""}
+}
+
 const_eval_invalid_align =
     align has to be a power of 2
 
@@ -205,6 +213,8 @@ const_eval_modified_global =
 const_eval_mut_deref =
     mutation through a reference is not allowed in {const_eval_const_context}s
 
+const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
+
 const_eval_non_const_fmt_macro_call =
     cannot call non-const formatting macro in {const_eval_const_context}s
 
@@ -327,7 +337,7 @@ const_eval_too_many_caller_args =
 
 const_eval_transient_mut_borrow = mutable references are not allowed in {const_eval_const_context}s
 
-const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in {const_eval_const_context}s
+const_eval_transient_mut_raw = raw mutable pointers are not allowed in {const_eval_const_context}s
 
 const_eval_try_block_from_output_non_const =
     `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
@@ -341,21 +351,21 @@ const_eval_unallowed_heap_allocations =
 
 const_eval_unallowed_inline_asm =
     inline assembly is not allowed in {const_eval_const_context}s
-const_eval_unallowed_mutable_refs =
-    mutable references are not allowed in the final value of {const_eval_const_context}s
+const_eval_unallowed_mutable_raw =
+    raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
     .teach_note =
+        References in statics and constants may only refer to immutable values.
+
+
         Statics are shared everywhere, and if they refer to mutable data one might violate memory
         safety since holding multiple mutable references to shared data is not allowed.
 
 
         If you really want global mutable state, try using static mut or a global UnsafeCell.
 
-const_eval_unallowed_mutable_refs_raw =
-    raw mutable references are not allowed in the final value of {const_eval_const_context}s
+const_eval_unallowed_mutable_refs =
+    mutable references are not allowed in the final value of {const_eval_const_context}s
     .teach_note =
-        References in statics and constants may only refer to immutable values.
-
-
         Statics are shared everywhere, and if they refer to mutable data one might violate memory
         safety since holding multiple mutable references to shared data is not allowed.
 
@@ -392,9 +402,6 @@ const_eval_unstable_in_stable =
     .unstable_sugg = if it is not part of the public API, make this function unstably const
     .bypass_sugg = otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks
 
-const_eval_unsupported_untyped_pointer = unsupported untyped pointer in constant
-    .note = memory only reachable via raw pointers is not supported
-
 const_eval_unterminated_c_string =
     reading a null-terminated string starting at {$pointer} with no null found before end of allocation
 
@@ -406,7 +413,6 @@ const_eval_upcast_mismatch =
 
 ## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`.
 ## (We'd love to sort this differently to make that more clear but tidy won't let us...)
-const_eval_validation_box_to_mut = {$front_matter}: encountered a box pointing to mutable memory in a constant
 const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant
 const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
 const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
@@ -441,7 +447,8 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu
 const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object
 const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
 const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
-const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const`
+const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const` or `static`
+const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
 const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
 const_eval_validation_null_box = {$front_matter}: encountered a null box
 const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer
@@ -451,7 +458,6 @@ const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but
 const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers
 const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected}
 const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range}
-const_eval_validation_ref_to_mut = {$front_matter}: encountered a reference pointing to mutable memory in a constant
 const_eval_validation_ref_to_static = {$front_matter}: encountered a reference pointing to a static variable in a constant
 const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty}
 const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes})
@@ -459,7 +465,7 @@ const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned
 const_eval_validation_uninhabited_enum_variant = {$front_matter}: encountered an uninhabited enum variant
 const_eval_validation_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}`
 const_eval_validation_uninit = {$front_matter}: encountered uninitialized memory, but {$expected}
-const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in a `const`
+const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in read-only memory
 
 const_eval_write_through_immutable_pointer =
     writing through a pointer that was derived from a shared (immutable) reference
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 4236117d75b..6a92ed9717d 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -293,6 +293,9 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
     cid: GlobalId<'tcx>,
     is_static: bool,
 ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
+    // `is_static` just means "in static", it could still be a promoted!
+    debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some());
+
     let res = ecx.load_mir(cid.instance.def, cid.promoted);
     match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) {
         Err(error) => {
@@ -330,8 +333,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
         Ok(mplace) => {
             // Since evaluation had no errors, validate the resulting constant.
             // This is a separate `try` block to provide more targeted error reporting.
-            let validation =
-                const_validate_mplace(&ecx, &mplace, is_static, cid.promoted.is_some());
+            let validation = const_validate_mplace(&ecx, &mplace, cid);
 
             let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
 
@@ -350,22 +352,26 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
 pub fn const_validate_mplace<'mir, 'tcx>(
     ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
     mplace: &MPlaceTy<'tcx>,
-    is_static: bool,
-    is_promoted: bool,
+    cid: GlobalId<'tcx>,
 ) -> InterpResult<'tcx> {
     let mut ref_tracking = RefTracking::new(mplace.clone());
     let mut inner = false;
     while let Some((mplace, path)) = ref_tracking.todo.pop() {
-        let mode = if is_static {
-            if is_promoted {
-                // Promoteds in statics are allowed to point to statics.
-                CtfeValidationMode::Const { inner, allow_static_ptrs: true }
-            } else {
-                // a `static`
-                CtfeValidationMode::Regular
+        let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) {
+            Some(_) if cid.promoted.is_some() => {
+                // Promoteds in statics are consts that re allowed to point to statics.
+                CtfeValidationMode::Const {
+                    allow_immutable_unsafe_cell: false,
+                    allow_static_ptrs: true,
+                }
+            }
+            Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static`
+            None => {
+                // In normal `const` (not promoted), the outermost allocation is always only copied,
+                // so having `UnsafeCell` in there is okay despite them being in immutable memory.
+                let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner;
+                CtfeValidationMode::Const { allow_immutable_unsafe_cell, allow_static_ptrs: false }
             }
-        } else {
-            CtfeValidationMode::Const { inner, allow_static_ptrs: false }
         };
         ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;
         inner = true;
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index f2e8821d6b6..d5a33b6f01e 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -723,7 +723,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
             && ty.is_freeze(*ecx.tcx, ecx.param_env)
         {
             let place = ecx.ref_to_mplace(val)?;
-            let new_place = place.map_provenance(|p| p.map(CtfeProvenance::as_immutable));
+            let new_place = place.map_provenance(CtfeProvenance::as_immutable);
             Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout))
         } else {
             Ok(val.clone())
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 171cc89d6ad..4a654480ef5 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
 use rustc_errors::{
     DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee,
     IntoDiagnostic, Level,
@@ -13,12 +15,24 @@ use rustc_middle::ty::{self, Ty};
 use rustc_span::Span;
 use rustc_target::abi::call::AdjustForForeignAbiError;
 use rustc_target::abi::{Size, WrappingRange};
+use rustc_type_ir::Mutability;
+
+use crate::interpret::InternKind;
 
 #[derive(Diagnostic)]
 #[diag(const_eval_dangling_ptr_in_final)]
 pub(crate) struct DanglingPtrInFinal {
     #[primary_span]
     pub span: Span,
+    pub kind: InternKind,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_mutable_ptr_in_final)]
+pub(crate) struct MutablePtrInFinal {
+    #[primary_span]
+    pub span: Span,
+    pub kind: InternKind,
 }
 
 #[derive(Diagnostic)]
@@ -100,8 +114,8 @@ pub(crate) struct TransientMutBorrowErr {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_transient_mut_borrow_raw, code = "E0658")]
-pub(crate) struct TransientMutBorrowErrRaw {
+#[diag(const_eval_transient_mut_raw, code = "E0658")]
+pub(crate) struct TransientMutRawErr {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
@@ -142,8 +156,8 @@ pub(crate) struct UnallowedMutableRefs {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_unallowed_mutable_refs_raw, code = "E0764")]
-pub(crate) struct UnallowedMutableRefsRaw {
+#[diag(const_eval_unallowed_mutable_raw, code = "E0764")]
+pub(crate) struct UnallowedMutableRaw {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
@@ -195,14 +209,6 @@ pub(crate) struct UnallowedInlineAsm {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_unsupported_untyped_pointer)]
-#[note]
-pub(crate) struct UnsupportedUntypedPointer {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(const_eval_interior_mutable_data_refer, code = "E0492")]
 pub(crate) struct InteriorMutableDataRefer {
     #[primary_span]
@@ -615,18 +621,16 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_static,
             PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_static,
 
-            PtrToMut { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_mut,
-            PtrToMut { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_mut,
-
             PointerAsInt { .. } => const_eval_validation_pointer_as_int,
             PartialPointer => const_eval_validation_partial_pointer,
             MutableRefInConst => const_eval_validation_mutable_ref_in_const,
+            MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
             NullFnPtr => const_eval_validation_null_fn_ptr,
             NeverVal => const_eval_validation_never_val,
             NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range,
             PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range,
             OutOfRange { .. } => const_eval_validation_out_of_range,
-            UnsafeCell => const_eval_validation_unsafe_cell,
+            UnsafeCellInImmutable => const_eval_validation_unsafe_cell,
             UninhabitedVal { .. } => const_eval_validation_uninhabited_val,
             InvalidEnumTag { .. } => const_eval_validation_invalid_enum_tag,
             UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant,
@@ -772,11 +776,11 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             }
             NullPtr { .. }
             | PtrToStatic { .. }
-            | PtrToMut { .. }
             | MutableRefInConst
+            | MutableRefToImmutable
             | NullFnPtr
             | NeverVal
-            | UnsafeCell
+            | UnsafeCellInImmutable
             | InvalidMetaSliceTooLarge { .. }
             | InvalidMetaTooLarge { .. }
             | DanglingPtrUseAfterFree { .. }
@@ -905,3 +909,14 @@ impl ReportErrorExt for ResourceExhaustionInfo {
     }
     fn add_args<G: EmissionGuarantee>(self, _: &DiagCtxt, _: &mut DiagnosticBuilder<'_, G>) {}
 }
+
+impl rustc_errors::IntoDiagnosticArg for InternKind {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::Borrowed(match self {
+            InternKind::Static(Mutability::Not) => "static",
+            InternKind::Static(Mutability::Mut) => "static_mut",
+            InternKind::Constant => "const",
+            InternKind::Promoted => "promoted",
+        }))
+    }
+}
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index d296ff5928b..0cb5c634b22 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -117,8 +117,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             def_id,
                             args,
                             ty::ClosureKind::FnOnce,
-                        )
-                        .ok_or_else(|| err_inval!(TooGeneric))?;
+                        );
                         let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
                         self.write_pointer(fn_ptr, dest)?;
                     }
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 202819ee633..751fbfacaad 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -5,30 +5,24 @@
 //!
 //! In principle, this is not very complicated: we recursively walk the final value, follow all the
 //! pointers, and move all reachable allocations to the global `tcx` memory. The only complication
-//! is picking the right mutability for the allocations in a `static` initializer: we want to make
-//! as many allocations as possible immutable so LLVM can put them into read-only memory. At the
-//! same time, we need to make memory that could be mutated by the program mutable to avoid
-//! incorrect compilations. To achieve this, we do a type-based traversal of the final value,
-//! tracking mutable and shared references and `UnsafeCell` to determine the current mutability.
-//! (In principle, we could skip this type-based part for `const` and promoteds, as they need to be
-//! always immutable. At least for `const` however we use this opportunity to reject any `const`
-//! that contains allocations whose mutability we cannot identify.)
+//! is picking the right mutability: the outermost allocation generally has a clear mutability, but
+//! what about the other allocations it points to that have also been created with this value? We
+//! don't want to do guesswork here. The rules are: `static`, `const`, and promoted can only create
+//! immutable allocations that way. `static mut` can be initialized with expressions like `&mut 42`,
+//! so all inner allocations are marked mutable. Some of them could potentially be made immutable,
+//! but that would require relying on type information, and given how many ways Rust has to lie
+//! about type information, we want to avoid doing that.
 
-use super::validity::RefTracking;
-use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
+use rustc_ast::Mutability;
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult};
-use rustc_middle::ty::{self, layout::TyAndLayout, Ty};
-
-use rustc_ast::Mutability;
+use rustc_middle::ty::layout::TyAndLayout;
 
-use super::{
-    AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, Projectable,
-    ValueVisitor,
-};
+use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy};
 use crate::const_eval;
-use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
+use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal};
 
 pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
         'mir,
@@ -41,271 +35,44 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
         MemoryMap = FxIndexMap<AllocId, (MemoryKind<T>, Allocation)>,
     >;
 
-struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>> {
-    /// The ectx from which we intern.
-    ecx: &'rt mut InterpCx<'mir, 'tcx, M>,
-    /// Previously encountered safe references.
-    ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, InternMode)>,
-    /// A list of all encountered allocations. After type-based interning, we traverse this list to
-    /// also intern allocations that are only referenced by a raw pointer or inside a union.
-    leftover_allocations: &'rt mut FxIndexSet<AllocId>,
-    /// The root kind of the value that we're looking at. This field is never mutated for a
-    /// particular allocation. It is primarily used to make as many allocations as possible
-    /// read-only so LLVM can place them in const memory.
-    mode: InternMode,
-    /// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect
-    /// the intern mode of references we encounter.
-    inside_unsafe_cell: bool,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
-enum InternMode {
-    /// A static and its current mutability. Below shared references inside a `static mut`,
-    /// this is *immutable*, and below mutable references inside an `UnsafeCell`, this
-    /// is *mutable*.
-    Static(hir::Mutability),
-    /// A `const`.
-    Const,
-}
-
-/// Signalling data structure to ensure we don't recurse
-/// into the memory of other constants or statics
-struct IsStaticOrFn;
-
-/// Intern an allocation without looking at its children.
-/// `mode` is the mode of the environment where we found this pointer.
-/// `mutability` is the mutability of the place to be interned; even if that says
-/// `immutable` things might become mutable if `ty` is not frozen.
-/// `ty` can be `None` if there is no potential interior mutability
-/// to account for (e.g. for vtables).
-fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>(
+/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory.
+///
+/// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the
+/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be*
+/// already mutable (as a sanity check).
+///
+/// `recursive_alloc` is called for all recursively encountered allocations.
+fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
     ecx: &'rt mut InterpCx<'mir, 'tcx, M>,
-    leftover_allocations: &'rt mut FxIndexSet<AllocId>,
     alloc_id: AllocId,
-    mode: InternMode,
-    ty: Option<Ty<'tcx>>,
-) -> Option<IsStaticOrFn> {
-    trace!("intern_shallow {:?} with {:?}", alloc_id, mode);
+    mutability: Mutability,
+    mut recursive_alloc: impl FnMut(&InterpCx<'mir, 'tcx, M>, CtfeProvenance),
+) -> Result<(), ()> {
+    trace!("intern_shallow {:?}", alloc_id);
     // remove allocation
-    let tcx = ecx.tcx;
-    let Some((kind, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else {
-        // Pointer not found in local memory map. It is either a pointer to the global
-        // map, or dangling.
-        // If the pointer is dangling (neither in local nor global memory), we leave it
-        // to validation to error -- it has the much better error messages, pointing out where
-        // in the value the dangling reference lies.
-        // The `span_delayed_bug` ensures that we don't forget such a check in validation.
-        if tcx.try_get_global_alloc(alloc_id).is_none() {
-            tcx.dcx().span_delayed_bug(ecx.tcx.span, "tried to intern dangling pointer");
-        }
-        // treat dangling pointers like other statics
-        // just to stop trying to recurse into them
-        return Some(IsStaticOrFn);
+    let Some((_kind, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else {
+        return Err(());
     };
-    // This match is just a canary for future changes to `MemoryKind`, which most likely need
-    // changes in this function.
-    match kind {
-        MemoryKind::Stack
-        | MemoryKind::Machine(const_eval::MemoryKind::Heap)
-        | MemoryKind::CallerLocation => {}
-    }
     // Set allocation mutability as appropriate. This is used by LLVM to put things into
     // read-only memory, and also by Miri when evaluating other globals that
     // access this one.
-    if let InternMode::Static(mutability) = mode {
-        // For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume
-        // no interior mutability.
-        let frozen = ty.map_or(true, |ty| ty.is_freeze(*ecx.tcx, ecx.param_env));
-        // For statics, allocation mutability is the combination of place mutability and
-        // type mutability.
-        // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere.
-        let immutable = mutability == Mutability::Not && frozen;
-        if immutable {
+    match mutability {
+        Mutability::Not => {
             alloc.mutability = Mutability::Not;
-        } else {
-            // Just making sure we are not "upgrading" an immutable allocation to mutable.
+        }
+        Mutability::Mut => {
+            // This must be already mutable, we won't "un-freeze" allocations ever.
             assert_eq!(alloc.mutability, Mutability::Mut);
         }
-    } else {
-        // No matter what, *constants are never mutable*. Mutating them is UB.
-        // See const_eval::machine::MemoryExtra::can_access_statics for why
-        // immutability is so important.
-
-        // Validation will ensure that there is no `UnsafeCell` on an immutable allocation.
-        alloc.mutability = Mutability::Not;
-    };
-    // link the alloc id to the actual allocation
-    leftover_allocations.extend(alloc.provenance().ptrs().iter().map(|&(_, prov)| prov.alloc_id()));
-    let alloc = tcx.mk_const_alloc(alloc);
-    tcx.set_alloc_id_memory(alloc_id, alloc);
-    None
-}
-
-impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>
-    InternVisitor<'rt, 'mir, 'tcx, M>
-{
-    fn intern_shallow(
-        &mut self,
-        alloc_id: AllocId,
-        mode: InternMode,
-        ty: Option<Ty<'tcx>>,
-    ) -> Option<IsStaticOrFn> {
-        intern_shallow(self.ecx, self.leftover_allocations, alloc_id, mode, ty)
     }
-}
-
-impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>
-    ValueVisitor<'mir, 'tcx, M> for InternVisitor<'rt, 'mir, 'tcx, M>
-{
-    type V = MPlaceTy<'tcx>;
-
-    #[inline(always)]
-    fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
-        self.ecx
-    }
-
-    fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
-        // Handle Reference types, as these are the only types with provenance supported by const eval.
-        // Raw pointers (and boxes) are handled by the `leftover_allocations` logic.
-        let tcx = self.ecx.tcx;
-        let ty = mplace.layout.ty;
-        if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() {
-            let value = self.ecx.read_immediate(mplace)?;
-            let mplace = self.ecx.ref_to_mplace(&value)?;
-            assert_eq!(mplace.layout.ty, referenced_ty);
-            // Handle trait object vtables.
-            if let ty::Dynamic(_, _, ty::Dyn) =
-                tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind()
-            {
-                let ptr = mplace.meta().unwrap_meta().to_pointer(&tcx)?;
-                if let Some(prov) = ptr.provenance {
-                    // Explicitly choose const mode here, since vtables are immutable, even
-                    // if the reference of the fat pointer is mutable.
-                    self.intern_shallow(prov.alloc_id(), InternMode::Const, None);
-                } else {
-                    // Validation will error (with a better message) on an invalid vtable pointer.
-                    // Let validation show the error message, but make sure it *does* error.
-                    tcx.dcx()
-                        .span_delayed_bug(tcx.span, "vtables pointers cannot be integer pointers");
-                }
-            }
-            // Check if we have encountered this pointer+layout combination before.
-            // Only recurse for allocation-backed pointers.
-            if let Some(prov) = mplace.ptr().provenance {
-                // Compute the mode with which we intern this. Our goal here is to make as many
-                // statics as we can immutable so they can be placed in read-only memory by LLVM.
-                let ref_mode = match self.mode {
-                    InternMode::Static(mutbl) => {
-                        // In statics, merge outer mutability with reference mutability and
-                        // take into account whether we are in an `UnsafeCell`.
-
-                        // The only way a mutable reference actually works as a mutable reference is
-                        // by being in a `static mut` directly or behind another mutable reference.
-                        // If there's an immutable reference or we are inside a `static`, then our
-                        // mutable reference is equivalent to an immutable one. As an example:
-                        // `&&mut Foo` is semantically equivalent to `&&Foo`
-                        match ref_mutability {
-                            _ if self.inside_unsafe_cell => {
-                                // Inside an `UnsafeCell` is like inside a `static mut`, the "outer"
-                                // mutability does not matter.
-                                InternMode::Static(ref_mutability)
-                            }
-                            Mutability::Not => {
-                                // A shared reference, things become immutable.
-                                // We do *not* consider `freeze` here: `intern_shallow` considers
-                                // `freeze` for the actual mutability of this allocation; the intern
-                                // mode for references contained in this allocation is tracked more
-                                // precisely when traversing the referenced data (by tracking
-                                // `UnsafeCell`). This makes sure that `&(&i32, &Cell<i32>)` still
-                                // has the left inner reference interned into a read-only
-                                // allocation.
-                                InternMode::Static(Mutability::Not)
-                            }
-                            Mutability::Mut => {
-                                // Mutable reference.
-                                InternMode::Static(mutbl)
-                            }
-                        }
-                    }
-                    InternMode::Const => {
-                        // Ignore `UnsafeCell`, everything is immutable. Validity does some sanity
-                        // checking for mutable references that we encounter -- they must all be
-                        // ZST.
-                        InternMode::Const
-                    }
-                };
-                match self.intern_shallow(prov.alloc_id(), ref_mode, Some(referenced_ty)) {
-                    // No need to recurse, these are interned already and statics may have
-                    // cycles, so we don't want to recurse there
-                    Some(IsStaticOrFn) => {}
-                    // intern everything referenced by this value. The mutability is taken from the
-                    // reference. It is checked above that mutable references only happen in
-                    // `static mut`
-                    None => self.ref_tracking.track((mplace, ref_mode), || ()),
-                }
-            }
-            Ok(())
-        } else {
-            // Not a reference. Check if we want to recurse.
-            let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> {
-                // ZSTs cannot contain pointers, we can avoid the interning walk.
-                if mplace.layout.is_zst() {
-                    return Ok(false);
-                }
-
-                // Now, check whether this allocation could contain references.
-                //
-                // Note, this check may sometimes not be cheap, so we only do it when the walk we'd like
-                // to avoid could be expensive: on the potentially larger types, arrays and slices,
-                // rather than on all aggregates unconditionally.
-                if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) {
-                    let Some((size, _align)) = self.ecx.size_and_align_of_mplace(mplace)? else {
-                        // We do the walk if we can't determine the size of the mplace: we may be
-                        // dealing with extern types here in the future.
-                        return Ok(true);
-                    };
-
-                    // If there is no provenance in this allocation, it does not contain references
-                    // that point to another allocation, and we can avoid the interning walk.
-                    if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size)? {
-                        if !alloc.has_provenance() {
-                            return Ok(false);
-                        }
-                    } else {
-                        // We're encountering a ZST here, and can avoid the walk as well.
-                        return Ok(false);
-                    }
-                }
-
-                // In the general case, we do the walk.
-                Ok(true)
-            };
-
-            // If this allocation contains no references to intern, we avoid the potentially costly
-            // walk.
-            //
-            // We can do this before the checks for interior mutability below, because only references
-            // are relevant in that situation, and we're checking if there are any here.
-            if !is_walk_needed(mplace)? {
-                return Ok(());
-            }
-
-            if let Some(def) = mplace.layout.ty.ty_adt_def() {
-                if def.is_unsafe_cell() {
-                    // We are crossing over an `UnsafeCell`, we can mutate again. This means that
-                    // References we encounter inside here are interned as pointing to mutable
-                    // allocations.
-                    // Remember the `old` value to handle nested `UnsafeCell`.
-                    let old = std::mem::replace(&mut self.inside_unsafe_cell, true);
-                    let walked = self.walk_value(mplace);
-                    self.inside_unsafe_cell = old;
-                    return walked;
-                }
-            }
-
-            self.walk_value(mplace)
-        }
+    // record child allocations
+    for &(_, prov) in alloc.provenance().ptrs().iter() {
+        recursive_alloc(ecx, prov);
     }
+    // link the alloc id to the actual allocation
+    let alloc = ecx.tcx.mk_const_alloc(alloc);
+    ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
+    Ok(())
 }
 
 /// How a constant value should be interned.
@@ -332,122 +99,108 @@ pub fn intern_const_alloc_recursive<
     intern_kind: InternKind,
     ret: &MPlaceTy<'tcx>,
 ) -> Result<(), ErrorGuaranteed> {
-    let tcx = ecx.tcx;
-    let base_intern_mode = match intern_kind {
-        InternKind::Static(mutbl) => InternMode::Static(mutbl),
-        // `Constant` includes array lengths.
-        InternKind::Constant | InternKind::Promoted => InternMode::Const,
-    };
-
-    // Type based interning.
-    // `ref_tracking` tracks typed references we have already interned and still need to crawl for
-    // more typed information inside them.
-    // `leftover_allocations` collects *all* allocations we see, because some might not
-    // be available in a typed way. They get interned at the end.
-    let mut ref_tracking = RefTracking::empty();
-    let leftover_allocations = &mut FxIndexSet::default();
-
-    // start with the outermost allocation
-    intern_shallow(
-        ecx,
-        leftover_allocations,
-        // The outermost allocation must exist, because we allocated it with
-        // `Memory::allocate`.
-        ret.ptr().provenance.unwrap().alloc_id(),
-        base_intern_mode,
-        Some(ret.layout.ty),
-    );
-
-    ref_tracking.track((ret.clone(), base_intern_mode), || ());
-
-    while let Some(((mplace, mode), _)) = ref_tracking.todo.pop() {
-        let res = InternVisitor {
-            ref_tracking: &mut ref_tracking,
-            ecx,
-            mode,
-            leftover_allocations,
-            inside_unsafe_cell: false,
+    // We are interning recursively, and for mutability we are distinguishing the "root" allocation
+    // that we are starting in, and all other allocations that we are encountering recursively.
+    let (base_mutability, inner_mutability) = match intern_kind {
+        InternKind::Constant | InternKind::Promoted => {
+            // Completely immutable. Interning anything mutably here can only lead to unsoundness,
+            // since all consts are conceptually independent values but share the same underlying
+            // memory.
+            (Mutability::Not, Mutability::Not)
         }
-        .visit_value(&mplace);
-        // We deliberately *ignore* interpreter errors here. When there is a problem, the remaining
-        // references are "leftover"-interned, and later validation will show a proper error
-        // and point at the right part of the value causing the problem.
-        match res {
-            Ok(()) => {}
-            Err(error) => {
-                ecx.tcx.dcx().span_delayed_bug(
-                    ecx.tcx.span,
-                    format!(
-                        "error during interning should later cause validation failure: {}",
-                        ecx.format_error(error),
-                    ),
-                );
-            }
+        InternKind::Static(Mutability::Not) => {
+            (
+                // Outermost allocation is mutable if `!Freeze`.
+                if ret.layout.ty.is_freeze(*ecx.tcx, ecx.param_env) {
+                    Mutability::Not
+                } else {
+                    Mutability::Mut
+                },
+                // Inner allocations are never mutable. They can only arise via the "tail
+                // expression" / "outer scope" rule, and we treat them consistently with `const`.
+                Mutability::Not,
+            )
         }
-    }
-
-    // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
-    // pointers, ... So we can't intern them according to their type rules
+        InternKind::Static(Mutability::Mut) => {
+            // Just make everything mutable. We accept code like
+            // `static mut X = &mut [42]`, so even inner allocations need to be mutable.
+            (Mutability::Mut, Mutability::Mut)
+        }
+    };
 
-    let mut todo: Vec<_> = leftover_allocations.iter().cloned().collect();
-    debug!(?todo);
-    debug!("dead_alloc_map: {:#?}", ecx.memory.dead_alloc_map);
-    while let Some(alloc_id) = todo.pop() {
-        if let Some((_, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) {
-            // We can't call the `intern_shallow` method here, as its logic is tailored to safe
-            // references and a `leftover_allocations` set (where we only have a todo-list here).
-            // So we hand-roll the interning logic here again.
-            match intern_kind {
-                // Statics may point to mutable allocations.
-                // Even for immutable statics it would be ok to have mutable allocations behind
-                // raw pointers, e.g. for `static FOO: *const AtomicUsize = &AtomicUsize::new(42)`.
-                InternKind::Static(_) => {}
-                // Raw pointers in promoteds may only point to immutable things so we mark
-                // everything as immutable.
-                // It is UB to mutate through a raw pointer obtained via an immutable reference:
-                // Since all references and pointers inside a promoted must by their very definition
-                // be created from an immutable reference (and promotion also excludes interior
-                // mutability), mutating through them would be UB.
-                // There's no way we can check whether the user is using raw pointers correctly,
-                // so all we can do is mark this as immutable here.
-                InternKind::Promoted => {
-                    // See const_eval::machine::MemoryExtra::can_access_statics for why
-                    // immutability is so important.
-                    alloc.mutability = Mutability::Not;
-                }
-                // If it's a constant, we should not have any "leftovers" as everything
-                // is tracked by const-checking.
-                // FIXME: downgrade this to a warning? It rejects some legitimate consts,
-                // such as `const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;`.
-                //
-                // NOTE: it looks likes this code path is only reachable when we try to intern
-                // something that cannot be promoted, which in constants means values that have
-                // drop glue, such as the example above.
-                InternKind::Constant => {
-                    ecx.tcx.dcx().emit_err(UnsupportedUntypedPointer { span: ecx.tcx.span });
-                    // For better errors later, mark the allocation as immutable.
-                    alloc.mutability = Mutability::Not;
-                }
-            }
-            let alloc = tcx.mk_const_alloc(alloc);
-            tcx.set_alloc_id_memory(alloc_id, alloc);
-            for &(_, prov) in alloc.inner().provenance().ptrs().iter() {
-                let alloc_id = prov.alloc_id();
-                if leftover_allocations.insert(alloc_id) {
-                    todo.push(alloc_id);
+    // Initialize recursive interning.
+    let base_alloc_id = ret.ptr().provenance.unwrap().alloc_id();
+    let mut todo = vec![(base_alloc_id, base_mutability)];
+    // We need to distinguish "has just been interned" from "was already in `tcx`",
+    // so we track this in a separate set.
+    let mut just_interned = FxHashSet::default();
+    // Whether we encountered a bad mutable pointer.
+    // We want to first report "dangling" and then "mutable", so we need to delay reporting these
+    // errors.
+    let mut found_bad_mutable_pointer = false;
+
+    // Keep interning as long as there are things to intern.
+    // We show errors if there are dangling pointers, or mutable pointers in immutable contexts
+    // (i.e., everything except for `static mut`). When these errors affect references, it is
+    // unfortunate that we show these errors here and not during validation, since validation can
+    // show much nicer errors. However, we do need these checks to be run on all pointers, including
+    // raw pointers, so we cannot rely on validation to catch them -- and since interning runs
+    // before validation, and interning doesn't know the type of anything, this means we can't show
+    // better errors. Maybe we should consider doing validation before interning in the future.
+    while let Some((alloc_id, mutability)) = todo.pop() {
+        if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
+            // Already interned.
+            debug_assert!(!ecx.memory.alloc_map.contains_key(&alloc_id));
+            continue;
+        }
+        just_interned.insert(alloc_id);
+        intern_shallow(ecx, alloc_id, mutability, |ecx, prov| {
+            let alloc_id = prov.alloc_id();
+            if intern_kind != InternKind::Promoted
+                && inner_mutability == Mutability::Not
+                && !prov.immutable()
+            {
+                if ecx.tcx.try_get_global_alloc(alloc_id).is_some()
+                    && !just_interned.contains(&alloc_id)
+                {
+                    // This is a pointer to some memory from another constant. We encounter mutable
+                    // pointers to such memory since we do not always track immutability through
+                    // these "global" pointers. Allowing them is harmless; the point of these checks
+                    // during interning is to justify why we intern the *new* allocations immutably,
+                    // so we can completely ignore existing allocations. We also don't need to add
+                    // this to the todo list, since after all it is already interned.
+                    return;
                 }
+                // Found a mutable pointer inside a const where inner allocations should be
+                // immutable. We exclude promoteds from this, since things like `&mut []` and
+                // `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
+                // on the promotion analysis not screwing up to ensure that it is sound to intern
+                // promoteds as immutable.
+                found_bad_mutable_pointer = true;
             }
-        } else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) {
-            // Codegen does not like dangling pointers, and generally `tcx` assumes that
-            // all allocations referenced anywhere actually exist. So, make sure we error here.
-            let reported = ecx.tcx.dcx().emit_err(DanglingPtrInFinal { span: ecx.tcx.span });
-            return Err(reported);
-        } else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
-            // We have hit an `AllocId` that is neither in local or global memory and isn't
-            // marked as dangling by local memory. That should be impossible.
-            span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id);
-        }
+            // We always intern with `inner_mutability`, and furthermore we ensured above that if
+            // that is "immutable", then there are *no* mutable pointers anywhere in the newly
+            // interned memory -- justifying that we can indeed intern immutably. However this also
+            // means we can *not* easily intern immutably here if `prov.immutable()` is true and
+            // `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
+            // we'd have to somehow check that they are *all* immutable before deciding that this
+            // allocation can be made immutable. In the future we could consider analyzing all
+            // pointers before deciding which allocations can be made immutable; but for now we are
+            // okay with losing some potential for immutability here. This can anyway only affect
+            // `static mut`.
+            todo.push((alloc_id, inner_mutability));
+        })
+        .map_err(|()| {
+            ecx.tcx.dcx().emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })
+        })?;
     }
+    if found_bad_mutable_pointer {
+        return Err(ecx
+            .tcx
+            .dcx()
+            .emit_err(MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }));
+    }
+
     Ok(())
 }
 
@@ -462,29 +215,18 @@ pub fn intern_const_alloc_for_constprop<
     ecx: &mut InterpCx<'mir, 'tcx, M>,
     alloc_id: AllocId,
 ) -> InterpResult<'tcx, ()> {
-    // Move allocation to `tcx`.
-    let Some((_, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else {
-        // Pointer not found in local memory map. It is either a pointer to the global
-        // map, or dangling.
-        if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
-            throw_ub!(DeadLocal)
-        }
+    if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
         // The constant is already in global memory. Do nothing.
         return Ok(());
-    };
-
-    alloc.mutability = Mutability::Not;
-
-    // We are not doing recursive interning, so we don't currently support provenance.
-    // (If this assertion ever triggers, we should just implement a
-    // proper recursive interning loop.)
-    assert!(alloc.provenance().ptrs().is_empty());
-
-    // Link the alloc id to the actual allocation
-    let alloc = ecx.tcx.mk_const_alloc(alloc);
-    ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
-
-    Ok(())
+    }
+    // Move allocation to `tcx`.
+    intern_shallow(ecx, alloc_id, Mutability::Not, |_ecx, _| {
+        // We are not doing recursive interning, so we don't currently support provenance.
+        // (If this assertion ever triggers, we should just implement a
+        // proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
+        panic!("`intern_const_alloc_for_constprop` called on allocation with nested provenance")
+    })
+    .map_err(|()| err_ub!(DeadLocal).into())
 }
 
 impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
@@ -504,12 +246,16 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
         // `allocate` picks a fresh AllocId that we will associate with its data below.
         let dest = self.allocate(layout, MemoryKind::Stack)?;
         f(self, &dest.clone().into())?;
-        let mut alloc =
-            self.memory.alloc_map.remove(&dest.ptr().provenance.unwrap().alloc_id()).unwrap().1;
-        alloc.mutability = Mutability::Not;
-        let alloc = self.tcx.mk_const_alloc(alloc);
         let alloc_id = dest.ptr().provenance.unwrap().alloc_id(); // this was just allocated, it must have provenance
-        self.tcx.set_alloc_id_memory(alloc_id, alloc);
+        intern_shallow(self, alloc_id, Mutability::Not, |ecx, prov| {
+            // We are not doing recursive interning, so we don't currently support provenance.
+            // (If this assertion ever triggers, we should just implement a
+            // proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
+            if !ecx.tcx.try_get_global_alloc(prov.alloc_id()).is_some() {
+                panic!("`intern_with_temp_alloc` with nested allocations");
+            }
+        })
+        .unwrap();
         Ok(alloc_id)
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 639b269ac25..b39efad61bb 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -62,8 +62,8 @@ pub(super) struct MemPlace<Prov: Provenance = CtfeProvenance> {
 
 impl<Prov: Provenance> MemPlace<Prov> {
     /// Adjust the provenance of the main pointer (metadata is unaffected).
-    pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
-        MemPlace { ptr: self.ptr.map_provenance(f), ..self }
+    pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
+        MemPlace { ptr: self.ptr.map_provenance(|p| p.map(f)), ..self }
     }
 
     /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
@@ -128,7 +128,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
     }
 
     /// Adjust the provenance of the main pointer (metadata is unaffected).
-    pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
+    pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
         MPlaceTy { mplace: self.mplace.map_provenance(f), ..self }
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 8b44b87647d..b5cd3259520 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -9,12 +9,13 @@ use std::num::NonZeroUsize;
 
 use either::{Left, Right};
 
+use hir::def::DefKind;
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_middle::mir::interpret::{
-    ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, ValidationErrorInfo,
-    ValidationErrorKind, ValidationErrorKind::*,
+    ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
+    ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
 };
 use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
@@ -123,15 +124,41 @@ pub enum PathElem {
 }
 
 /// Extra things to check for during validation of CTFE results.
+#[derive(Copy, Clone)]
 pub enum CtfeValidationMode {
-    /// Regular validation, nothing special happening.
-    Regular,
-    /// Validation of a `const`.
-    /// `inner` says if this is an inner, indirect allocation (as opposed to the top-level const
-    /// allocation). Being an inner allocation makes a difference because the top-level allocation
-    /// of a `const` is copied for each use, but the inner allocations are implicitly shared.
+    /// Validation of a `static`
+    Static { mutbl: Mutability },
+    /// Validation of a `const` (including promoteds).
+    /// `allow_immutable_unsafe_cell` says whether we allow `UnsafeCell` in immutable memory (which is the
+    /// case for the top-level allocation of a `const`, where this is fine because the allocation will be
+    /// copied at each use site).
     /// `allow_static_ptrs` says if pointers to statics are permitted (which is the case for promoteds in statics).
-    Const { inner: bool, allow_static_ptrs: bool },
+    Const { allow_immutable_unsafe_cell: bool, allow_static_ptrs: bool },
+}
+
+impl CtfeValidationMode {
+    fn allow_immutable_unsafe_cell(self) -> bool {
+        match self {
+            CtfeValidationMode::Static { .. } => false,
+            CtfeValidationMode::Const { allow_immutable_unsafe_cell, .. } => {
+                allow_immutable_unsafe_cell
+            }
+        }
+    }
+
+    fn allow_static_ptrs(self) -> bool {
+        match self {
+            CtfeValidationMode::Static { .. } => true, // statics can point to statics
+            CtfeValidationMode::Const { allow_static_ptrs, .. } => allow_static_ptrs,
+        }
+    }
+
+    fn may_contain_mutable_ref(self) -> bool {
+        match self {
+            CtfeValidationMode::Static { mutbl } => mutbl == Mutability::Mut,
+            CtfeValidationMode::Const { .. } => false,
+        }
+    }
 }
 
 /// State for tracking recursive validation of references
@@ -418,26 +445,52 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         }
         // Recursive checking
         if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() {
+            // Determine whether this pointer expects to be pointing to something mutable.
+            let ptr_expected_mutbl = match ptr_kind {
+                PointerKind::Box => Mutability::Mut,
+                PointerKind::Ref => {
+                    let tam = value.layout.ty.builtin_deref(false).unwrap();
+                    // ZST never require mutability. We do not take into account interior mutability
+                    // here since we cannot know if there really is an `UnsafeCell` inside
+                    // `Option<UnsafeCell>` -- so we check that in the recursive descent behind this
+                    // reference.
+                    if size == Size::ZERO { Mutability::Not } else { tam.mutbl }
+                }
+            };
             // Proceed recursively even for ZST, no reason to skip them!
             // `!` is a ZST and we want to validate it.
             if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
                 // Let's see what kind of memory this points to.
-                let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id);
+                // `unwrap` since dangling pointers have already been handled.
+                let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id).unwrap();
                 match alloc_kind {
-                    Some(GlobalAlloc::Static(did)) => {
+                    GlobalAlloc::Static(did) => {
                         // Special handling for pointers to statics (irrespective of their type).
                         assert!(!self.ecx.tcx.is_thread_local_static(did));
                         assert!(self.ecx.tcx.is_static(did));
-                        if matches!(
-                            self.ctfe_mode,
-                            Some(CtfeValidationMode::Const { allow_static_ptrs: false, .. })
-                        ) {
+                        if self.ctfe_mode.is_some_and(|c| !c.allow_static_ptrs()) {
                             // See const_eval::machine::MemoryExtra::can_access_statics for why
                             // this check is so important.
                             // This check is reachable when the const just referenced the static,
                             // but never read it (so we never entered `before_access_global`).
                             throw_validation_failure!(self.path, PtrToStatic { ptr_kind });
                         }
+                        // Mutability check.
+                        if ptr_expected_mutbl == Mutability::Mut {
+                            if matches!(
+                                self.ecx.tcx.def_kind(did),
+                                DefKind::Static(Mutability::Not)
+                            ) && self
+                                .ecx
+                                .tcx
+                                .type_of(did)
+                                .no_bound_vars()
+                                .expect("statics should not have generic parameters")
+                                .is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all())
+                            {
+                                throw_validation_failure!(self.path, MutableRefToImmutable);
+                            }
+                        }
                         // We skip recursively checking other statics. These statics must be sound by
                         // themselves, and the only way to get broken statics here is by using
                         // unsafe code.
@@ -450,18 +503,31 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                         // referring to statics).
                         return Ok(());
                     }
-                    Some(GlobalAlloc::Memory(alloc)) => {
+                    GlobalAlloc::Memory(alloc) => {
                         if alloc.inner().mutability == Mutability::Mut
                             && matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
                         {
-                            // This should be unreachable, but if someone manages to copy a pointer
-                            // out of a `static`, then that pointer might point to mutable memory,
-                            // and we would catch that here.
-                            throw_validation_failure!(self.path, PtrToMut { ptr_kind });
+                            // This is impossible: this can only be some inner allocation of a
+                            // `static mut` (everything else either hits the `GlobalAlloc::Static`
+                            // case or is interned immutably). To get such a pointer we'd have to
+                            // load it from a static, but such loads lead to a CTFE error.
+                            span_bug!(
+                                self.ecx.tcx.span,
+                                "encountered reference to mutable memory inside a `const`"
+                            );
+                        }
+                        if ptr_expected_mutbl == Mutability::Mut
+                            && alloc.inner().mutability == Mutability::Not
+                        {
+                            throw_validation_failure!(self.path, MutableRefToImmutable);
+                        }
+                    }
+                    GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {
+                        // These are immutable, we better don't allow mutable pointers here.
+                        if ptr_expected_mutbl == Mutability::Mut {
+                            throw_validation_failure!(self.path, MutableRefToImmutable);
                         }
                     }
-                    // Nothing to check for these.
-                    None | Some(GlobalAlloc::Function(..) | GlobalAlloc::VTable(..)) => {}
                 }
             }
             let path = &self.path;
@@ -532,11 +598,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 Ok(true)
             }
             ty::Ref(_, ty, mutbl) => {
-                if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
+                if self.ctfe_mode.is_some_and(|c| !c.may_contain_mutable_ref())
                     && *mutbl == Mutability::Mut
                 {
-                    // A mutable reference inside a const? That does not seem right (except if it is
-                    // a ZST).
                     let layout = self.ecx.layout_of(*ty)?;
                     if !layout.is_zst() {
                         throw_validation_failure!(self.path, MutableRefInConst);
@@ -642,6 +706,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
             )
         }
     }
+
+    fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool {
+        if let Some(mplace) = op.as_mplace_or_imm().left() {
+            if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
+                if self.ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().mutability
+                    == Mutability::Mut
+                {
+                    return true;
+                }
+            }
+        }
+        false
+    }
 }
 
 impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
@@ -705,10 +782,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
         op: &OpTy<'tcx, M::Provenance>,
         _fields: NonZeroUsize,
     ) -> InterpResult<'tcx> {
-        // Special check preventing `UnsafeCell` inside unions in the inner part of constants.
-        if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) {
-            if !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) {
-                throw_validation_failure!(self.path, UnsafeCell);
+        // Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory.
+        if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) {
+            if !op.layout.is_zst() && !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) {
+                if !self.in_mutable_memory(op) {
+                    throw_validation_failure!(self.path, UnsafeCellInImmutable);
+                }
             }
         }
         Ok(())
@@ -730,11 +809,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
         }
 
         // Special check preventing `UnsafeCell` in the inner part of constants
-        if let Some(def) = op.layout.ty.ty_adt_def() {
-            if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. }))
+        if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) {
+            if !op.layout.is_zst()
+                && let Some(def) = op.layout.ty.ty_adt_def()
                 && def.is_unsafe_cell()
             {
-                throw_validation_failure!(self.path, UnsafeCell);
+                if !self.in_mutable_memory(op) {
+                    throw_validation_failure!(self.path, UnsafeCellInImmutable);
+                }
             }
         }
 
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 327c91731bf..39bc2b960e9 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -466,7 +466,7 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
 
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         match self.0 {
-            hir::BorrowKind::Raw => ccx.dcx().create_err(errors::UnallowedMutableRefsRaw {
+            hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::UnallowedMutableRaw {
                 span,
                 kind: ccx.const_kind(),
                 teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
@@ -491,10 +491,10 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         let kind = ccx.const_kind();
         match self.0 {
-            hir::BorrowKind::Raw => ccx.tcx.sess.create_feature_err(
-                errors::TransientMutBorrowErrRaw { span, kind },
-                sym::const_mut_refs,
-            ),
+            hir::BorrowKind::Raw => ccx
+                .tcx
+                .sess
+                .create_feature_err(errors::TransientMutRawErr { span, kind }, sym::const_mut_refs),
             hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err(
                 errors::TransientMutBorrowErr { span, kind },
                 sym::const_mut_refs,
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
index 4a3cfd50b44..36a315b8f30 100644
--- a/compiler/rustc_const_eval/src/util/caller_location.rs
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -27,6 +27,7 @@ fn alloc_caller_location<'mir, 'tcx>(
         // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398
         ecx.allocate_str("<redacted>", MemoryKind::CallerLocation, Mutability::Not).unwrap()
     };
+    let file = file.map_provenance(CtfeProvenance::as_immutable);
     let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
     let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) };
 
diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs
index 556560945e9..5bc904e5930 100644
--- a/compiler/rustc_hir_analysis/src/autoderef.rs
+++ b/compiler/rustc_hir_analysis/src/autoderef.rs
@@ -12,7 +12,9 @@ use rustc_trait_selection::traits::StructurallyNormalizeExt;
 
 #[derive(Copy, Clone, Debug)]
 pub enum AutoderefKind {
+    /// A true pointer type, such as `&T` and `*mut T`.
     Builtin,
+    /// A type which must dispatch to a `Deref` implementation.
     Overloaded,
 }
 
@@ -83,6 +85,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
                 (AutoderefKind::Builtin, ty)
             }
         } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
+            // The overloaded deref check already normalizes the pointee type.
             (AutoderefKind::Overloaded, ty)
         } else {
             return None;
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 5b264f6f034..57829d9d418 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -254,7 +254,7 @@ fn compare_method_predicate_entailment<'tcx>(
     // checks. For the comparison to be valid, we need to
     // normalize the associated types in the impl/trait methods
     // first. However, because function types bind regions, just
-    // calling `normalize_associated_types_in` would have no effect on
+    // calling `FnCtxt::normalize` would have no effect on
     // any associated types appearing in the fn arguments or return
     // type.
 
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index bfa9dc42422..3674a760cbf 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -530,9 +530,13 @@ pub(super) fn type_of_opaque(
         Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) {
             Node::Item(item) => match item.kind {
                 ItemKind::OpaqueTy(OpaqueTy {
-                    origin: hir::OpaqueTyOrigin::TyAlias { .. },
+                    origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false },
                     ..
                 }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
+                ItemKind::OpaqueTy(OpaqueTy {
+                    origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true },
+                    ..
+                }) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id),
                 // Opaque types desugared from `impl Trait`.
                 ItemKind::OpaqueTy(&OpaqueTy {
                     origin:
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
index 85093bc12b3..79cb384c5bd 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -23,6 +23,60 @@ pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed>
     res
 }
 
+/// Checks "defining uses" of opaque `impl Trait` in associated types.
+/// These can only be defined by associated items of the same trait.
+#[instrument(skip(tcx), level = "debug")]
+pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
+    tcx: TyCtxt<'_>,
+    def_id: LocalDefId,
+) -> Ty<'_> {
+    let mut parent_def_id = def_id;
+    while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
+        // Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
+        parent_def_id = tcx.local_parent(parent_def_id);
+    }
+    let impl_def_id = tcx.local_parent(parent_def_id);
+    match tcx.def_kind(impl_def_id) {
+        DefKind::Impl { .. } => {}
+        other => bug!("invalid impl trait in assoc type parent: {other:?}"),
+    }
+
+    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
+
+    for &assoc_id in tcx.associated_item_def_ids(impl_def_id) {
+        let assoc = tcx.associated_item(assoc_id);
+        match assoc.kind {
+            ty::AssocKind::Const | ty::AssocKind::Fn => {
+                locator.check(assoc_id.expect_local(), ImplTraitSource::AssocTy)
+            }
+            // Associated types don't have bodies, so they can't constrain hidden types
+            ty::AssocKind::Type => {}
+        }
+    }
+
+    if let Some(hidden) = locator.found {
+        // Only check against typeck if we didn't already error
+        if !hidden.ty.references_error() {
+            for concrete_type in locator.typeck_types {
+                if concrete_type.ty != tcx.erase_regions(hidden.ty)
+                    && !(concrete_type, hidden).references_error()
+                {
+                    hidden.report_mismatch(&concrete_type, def_id, tcx).emit();
+                }
+            }
+        }
+
+        hidden.ty
+    } else {
+        let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
+            span: tcx.def_span(def_id),
+            name: tcx.item_name(parent_def_id.to_def_id()),
+            what: "impl",
+        });
+        Ty::new_error(tcx, reported)
+    }
+}
+
 /// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions
 /// laid for "higher-order pattern unification".
 /// This ensures that inference is tractable.
@@ -128,9 +182,15 @@ struct TaitConstraintLocator<'tcx> {
     typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
 }
 
+#[derive(Debug)]
+enum ImplTraitSource {
+    AssocTy,
+    TyAlias,
+}
+
 impl TaitConstraintLocator<'_> {
     #[instrument(skip(self), level = "debug")]
-    fn check(&mut self, item_def_id: LocalDefId) {
+    fn check(&mut self, item_def_id: LocalDefId, source: ImplTraitSource) {
         // Don't try to check items that cannot possibly constrain the type.
         if !self.tcx.has_typeck_results(item_def_id) {
             debug!("no constraint: no typeck results");
@@ -182,7 +242,13 @@ impl TaitConstraintLocator<'_> {
                 continue;
             }
             constrained = true;
-            if !self.tcx.opaque_types_defined_by(item_def_id).contains(&self.def_id) {
+            let opaque_types_defined_by = match source {
+                ImplTraitSource::AssocTy => {
+                    self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id)
+                }
+                ImplTraitSource::TyAlias => self.tcx.opaque_types_defined_by(item_def_id),
+            };
+            if !opaque_types_defined_by.contains(&self.def_id) {
                 self.tcx.dcx().emit_err(TaitForwardCompat {
                     span: hidden_type.span,
                     item_span: self
@@ -240,7 +306,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
     }
     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
         if let hir::ExprKind::Closure(closure) = ex.kind {
-            self.check(closure.def_id);
+            self.check(closure.def_id, ImplTraitSource::TyAlias);
         }
         intravisit::walk_expr(self, ex);
     }
@@ -248,7 +314,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         trace!(?it.owner_id);
         // The opaque type itself or its children are not within its reveal scope.
         if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id);
+            self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
             intravisit::walk_item(self, it);
         }
     }
@@ -256,13 +322,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         trace!(?it.owner_id);
         // The opaque type itself or its children are not within its reveal scope.
         if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id);
+            self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
             intravisit::walk_impl_item(self, it);
         }
     }
     fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
         trace!(?it.owner_id);
-        self.check(it.owner_id.def_id);
+        self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
         intravisit::walk_trait_item(self, it);
     }
     fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 9850892bd36..b6dfc34d3ac 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -44,7 +44,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
             || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
             || self.suggest_no_capture_closure(err, expected, expr_ty)
-            || self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty)
+            || self.suggest_boxing_when_appropriate(
+                err,
+                expr.peel_blocks().span,
+                expr.hir_id,
+                expected,
+                expr_ty,
+            )
             || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
             || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected)
             || self.suggest_clone_for_ref(err, expr, expr_ty, expected)
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 09dbfaa57d2..c6b9197d0e9 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -2,7 +2,7 @@ use crate::callee::{self, DeferredCallResolution};
 use crate::errors::CtorIsPrivate;
 use crate::method::{self, MethodCallee, SelfSource};
 use crate::rvalue_scopes;
-use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, RawTy};
+use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey};
@@ -373,14 +373,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn handle_raw_ty(&self, span: Span, ty: Ty<'tcx>) -> RawTy<'tcx> {
-        RawTy { raw: ty, normalized: self.normalize(span, ty) }
-    }
-
-    pub fn to_ty(&self, ast_t: &hir::Ty<'tcx>) -> RawTy<'tcx> {
+    pub fn to_ty(&self, ast_t: &hir::Ty<'tcx>) -> LoweredTy<'tcx> {
         let t = self.astconv().ast_ty_to_ty(ast_t);
         self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None));
-        self.handle_raw_ty(ast_t.span, t)
+        LoweredTy::from_raw(self, ast_t.span, t)
     }
 
     pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
@@ -396,7 +392,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ty.normalized
     }
 
-    pub(super) fn user_args_for_adt(ty: RawTy<'tcx>) -> UserArgs<'tcx> {
+    pub(super) fn user_args_for_adt(ty: LoweredTy<'tcx>) -> UserArgs<'tcx> {
         match (ty.raw.kind(), ty.normalized.kind()) {
             (ty::Adt(_, args), _) => UserArgs { args, user_self_ty: None },
             (_, ty::Adt(adt, args)) => UserArgs {
@@ -801,7 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         hir_id: hir::HirId,
         span: Span,
         args: Option<&'tcx [hir::Expr<'tcx>]>,
-    ) -> (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
+    ) -> (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
         debug!(
             "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}",
             qpath, hir_id, span
@@ -825,7 +821,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // We manually call `register_wf_obligation` in the success path
                 // below.
                 let ty = self.astconv().ast_ty_to_ty_in_path(qself);
-                (self.handle_raw_ty(span, ty), qself, segment)
+                (LoweredTy::from_raw(self, span, ty), qself, segment)
             }
             QPath::LangItem(..) => {
                 bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`")
@@ -1074,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn instantiate_value_path(
         &self,
         segments: &'tcx [hir::PathSegment<'tcx>],
-        self_ty: Option<RawTy<'tcx>>,
+        self_ty: Option<LoweredTy<'tcx>>,
         res: Res,
         span: Span,
         hir_id: hir::HirId,
@@ -1201,8 +1197,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             path_segs.last().is_some_and(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self);
 
         let (res, self_ctor_args) = if let Res::SelfCtor(impl_def_id) = res {
-            let ty =
-                self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id).instantiate_identity());
+            let ty = LoweredTy::from_raw(
+                self,
+                span,
+                tcx.at(span).type_of(impl_def_id).instantiate_identity(),
+            );
             match ty.normalized.ty_adt_def() {
                 Some(adt_def) if adt_def.has_ctor() => {
                     let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap();
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 136ed1a709e..6a77450f075 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -6,7 +6,7 @@ use crate::method::MethodCallee;
 use crate::TupleArgumentsFlag::*;
 use crate::{errors, Expectation::*};
 use crate::{
-    struct_span_code_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy,
+    struct_span_code_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, Needs,
     TupleArgumentsFlag,
 };
 use itertools::Itertools;
@@ -1792,12 +1792,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         qpath: &QPath<'tcx>,
         path_span: Span,
         hir_id: hir::HirId,
-    ) -> (Res, RawTy<'tcx>) {
+    ) -> (Res, LoweredTy<'tcx>) {
         match *qpath {
             QPath::Resolved(ref maybe_qself, path) => {
                 let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw);
                 let ty = self.astconv().res_to_ty(self_ty, path, hir_id, true);
-                (path.res, self.handle_raw_ty(path_span, ty))
+                (path.res, LoweredTy::from_raw(self, path_span, ty))
             }
             QPath::TypeRelative(qself, segment) => {
                 let ty = self.to_ty(qself);
@@ -1808,7 +1808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let ty = result
                     .map(|(ty, _, _)| ty)
                     .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar));
-                let ty = self.handle_raw_ty(path_span, ty);
+                let ty = LoweredTy::from_raw(self, path_span, ty);
                 let result = result.map(|(_, kind, def_id)| (kind, def_id));
 
                 // Write back the new resolution.
@@ -1818,7 +1818,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             QPath::LangItem(lang_item, span) => {
                 let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id);
-                (res, self.handle_raw_ty(path_span, ty))
+                (res, LoweredTy::from_raw(self, path_span, ty))
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index f65e9b698ab..18f547be2a7 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -353,14 +353,22 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
     }
 }
 
-/// Represents a user-provided type in the raw form (never normalized).
+/// The `ty` representation of a user-provided type. Depending on the use-site
+/// we want to either use the unnormalized or the normalized form of this type.
 ///
 /// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`,
 /// and the API in this module, which expect `Ty` to be fully normalized.
 #[derive(Clone, Copy, Debug)]
-pub struct RawTy<'tcx> {
+pub struct LoweredTy<'tcx> {
+    /// The unnormalized type provided by the user.
     pub raw: Ty<'tcx>,
 
     /// The normalized form of `raw`, stored here for efficiency.
     pub normalized: Ty<'tcx>,
 }
+
+impl<'tcx> LoweredTy<'tcx> {
+    pub fn from_raw(fcx: &FnCtxt<'_, 'tcx>, span: Span, raw: Ty<'tcx>) -> LoweredTy<'tcx> {
+        LoweredTy { raw, normalized: fcx.normalize(span, raw) }
+    }
+}
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 80467ca9381..bdd7c382903 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -49,7 +49,7 @@ use crate::check::check_fn;
 use crate::coercion::DynamicCoerceMany;
 use crate::diverges::Diverges;
 use crate::expectation::Expectation;
-use crate::fn_ctxt::RawTy;
+use crate::fn_ctxt::LoweredTy;
 use crate::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::{struct_span_code_err, ErrorGuaranteed};
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 17eff54f7ae..a446c7aa42b 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -746,11 +746,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(item, impl_ty, impl_args);
             debug!("xform_self_ty: {:?}, xform_ret_ty: {:?}", xform_self_ty, xform_ret_ty);
 
-            // We can't use normalize_associated_types_in as it will pollute the
+            // We can't use `FnCtxt::normalize` as it will pollute the
             // fcx's fulfillment context after this probe is over.
+            //
             // Note: we only normalize `xform_self_ty` here since the normalization
             // of the return type can lead to inference results that prohibit
             // valid candidates from being found, see issue #85671
+            //
             // FIXME Postponing the normalization of the return type likely only hides a deeper bug,
             // which might be caused by the `param_env` itself. The clauses of the `param_env`
             // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized,
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 73fc0ee499e..e31eeab4c4a 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1,5 +1,5 @@
 use crate::gather_locals::DeclOrigin;
-use crate::{errors, FnCtxt, RawTy};
+use crate::{errors, FnCtxt, LoweredTy};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{
@@ -891,7 +891,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         pat: &Pat<'tcx>,
         qpath: &hir::QPath<'_>,
-        path_resolution: (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
+        path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
         expected: Ty<'tcx>,
         ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index dfa3ced9dc1..12f8e42c78f 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -284,7 +284,7 @@ impl<T: Idx> BitSet<T> {
         not_already
     }
 
-    fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> {
+    pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> {
         let (start, end) = inclusive_start_end(range, self.domain_size)?;
         let (start_word_index, _) = word_index_and_mask(start);
         let (end_word_index, end_mask) = word_index_and_mask(end);
@@ -1299,7 +1299,7 @@ impl<T: Idx> SparseBitSet<T> {
 }
 
 impl<T: Idx + Ord> SparseBitSet<T> {
-    fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> {
+    pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> {
         let mut last_leq = None;
         for e in self.iter() {
             if range.contains(e) {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index afb3c5c1e56..01cd3c57925 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -294,8 +294,9 @@ impl<T> Trait<T> for X {
                             );
                         }
                     }
-                    (ty::Alias(ty::Opaque, alias), _) | (_, ty::Alias(ty::Opaque, alias))
-                        if alias.def_id.is_local()
+                    (_, ty::Alias(ty::Opaque, opaque_ty))
+                    | (ty::Alias(ty::Opaque, opaque_ty), _) => {
+                        if opaque_ty.def_id.is_local()
                             && matches!(
                                 tcx.def_kind(body_owner_def_id),
                                 DefKind::Fn
@@ -303,21 +304,74 @@ impl<T> Trait<T> for X {
                                     | DefKind::Const
                                     | DefKind::AssocFn
                                     | DefKind::AssocConst
-                            ) =>
-                    {
-                        if tcx.is_type_alias_impl_trait(alias.def_id) {
-                            if !tcx
+                            )
+                            && tcx.is_type_alias_impl_trait(opaque_ty.def_id)
+                            && !tcx
                                 .opaque_types_defined_by(body_owner_def_id.expect_local())
-                                .contains(&alias.def_id.expect_local())
-                            {
-                                let sp = tcx
-                                    .def_ident_span(body_owner_def_id)
-                                    .unwrap_or_else(|| tcx.def_span(body_owner_def_id));
-                                diag.span_note(
-                                    sp,
-                                    "\
-                                    this item must have the opaque type in its signature \
-                                    in order to be able to register hidden types",
+                                .contains(&opaque_ty.def_id.expect_local())
+                        {
+                            let sp = tcx
+                                .def_ident_span(body_owner_def_id)
+                                .unwrap_or_else(|| tcx.def_span(body_owner_def_id));
+                            diag.span_note(
+                                sp,
+                                "this item must have the opaque type in its signature in order to \
+                                 be able to register hidden types",
+                            );
+                        }
+                        // If two if arms can be coerced to a trait object, provide a structured
+                        // suggestion.
+                        let ObligationCauseCode::IfExpression(cause) = cause.code() else {
+                            return;
+                        };
+                        let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else {
+                            return;
+                        };
+                        let Some(then) = blk.expr else {
+                            return;
+                        };
+                        let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else {
+                            return;
+                        };
+                        let Some(else_) = blk.expr else {
+                            return;
+                        };
+                        let expected = match values.found.kind() {
+                            ty::Alias(..) => values.expected,
+                            _ => values.found,
+                        };
+                        let preds = tcx.explicit_item_bounds(opaque_ty.def_id);
+                        for (pred, _span) in preds.skip_binder() {
+                            let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
+                            else {
+                                continue;
+                            };
+                            if trait_predicate.polarity != ty::ImplPolarity::Positive {
+                                continue;
+                            }
+                            let def_id = trait_predicate.def_id();
+                            let mut impl_def_ids = vec![];
+                            tcx.for_each_relevant_impl(def_id, expected, |did| {
+                                impl_def_ids.push(did)
+                            });
+                            if let [_] = &impl_def_ids[..] {
+                                let trait_name = tcx.item_name(def_id);
+                                diag.multipart_suggestion(
+                                    format!(
+                                        "`{expected}` implements `{trait_name}` so you can box \
+                                         both arms and coerce to the trait object \
+                                         `Box<dyn {trait_name}>`",
+                                    ),
+                                    vec![
+                                        (then.span.shrink_to_lo(), "Box::new(".to_string()),
+                                        (
+                                            then.span.shrink_to_hi(),
+                                            format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
+                                        ),
+                                        (else_.span.shrink_to_lo(), "Box::new(".to_string()),
+                                        (else_.span.shrink_to_hi(), ")".to_string()),
+                                    ],
+                                    MachineApplicable,
                                 );
                             }
                         }
@@ -330,6 +384,38 @@ impl<T> Trait<T> for X {
                             );
                         }
                     }
+                    (ty::Adt(_, _), ty::Adt(def, args))
+                        if let ObligationCauseCode::IfExpression(cause) = cause.code()
+                            && let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id)
+                            && let Some(then) = blk.expr
+                            && def.is_box()
+                            && let boxed_ty = args.type_at(0)
+                            && let ty::Dynamic(t, _, _) = boxed_ty.kind()
+                            && let Some(def_id) = t.principal_def_id()
+                            && let mut impl_def_ids = vec![]
+                            && let _ =
+                                tcx.for_each_relevant_impl(def_id, values.expected, |did| {
+                                    impl_def_ids.push(did)
+                                })
+                            && let [_] = &impl_def_ids[..] =>
+                    {
+                        // We have divergent if/else arms where the expected value is a type that
+                        // implements the trait of the found boxed trait object.
+                        diag.multipart_suggestion(
+                            format!(
+                                "`{}` implements `{}` so you can box it to coerce to the trait \
+                                 object `{}`",
+                                values.expected,
+                                tcx.item_name(def_id),
+                                values.found,
+                            ),
+                            vec![
+                                (then.span.shrink_to_lo(), "Box::new(".to_string()),
+                                (then.span.shrink_to_hi(), ")".to_string()),
+                            ],
+                            MachineApplicable,
+                        );
+                    }
                     _ => {}
                 }
                 debug!(
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 1b4e9c28635..1c9b4e558f0 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -416,14 +416,14 @@ pub enum ValidationErrorKind<'tcx> {
     PartialPointer,
     PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
     PtrToStatic { ptr_kind: PointerKind },
-    PtrToMut { ptr_kind: PointerKind },
     MutableRefInConst,
+    MutableRefToImmutable,
+    UnsafeCellInImmutable,
     NullFnPtr,
     NeverVal,
     NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
     PtrOutOfRange { range: WrappingRange, max_value: u128 },
     OutOfRange { value: String, range: WrappingRange, max_value: u128 },
-    UnsafeCell,
     UninhabitedVal { ty: Ty<'tcx> },
     InvalidEnumTag { value: String },
     UninhabitedEnumVariant,
diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs
index 6893387736a..dabf6297aa9 100644
--- a/compiler/rustc_middle/src/mir/interpret/pointer.rs
+++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs
@@ -277,6 +277,12 @@ impl From<AllocId> for Pointer {
         Pointer::new(alloc_id.into(), Size::ZERO)
     }
 }
+impl From<CtfeProvenance> for Pointer {
+    #[inline(always)]
+    fn from(prov: CtfeProvenance) -> Self {
+        Pointer::new(prov, Size::ZERO)
+    }
+}
 
 impl<Prov> From<Pointer<Prov>> for Pointer<Option<Prov>> {
     #[inline(always)]
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index a9dc7f5d11a..23daefd5a65 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -343,6 +343,15 @@ rustc_queries! {
         }
     }
 
+    query impl_trait_in_assoc_types_defined_by(
+        key: LocalDefId
+    ) -> &'tcx ty::List<LocalDefId> {
+        desc {
+            |tcx| "computing the opaque types defined by `{}`",
+            tcx.def_path_str(key.to_def_id())
+        }
+    }
+
     /// Returns the list of bounds that can be used for
     /// `SelectionCandidate::ProjectionCandidate(_)` and
     /// `ProjectionTyCandidate::TraitDef`.
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index b6c3c34078f..293fdb026b6 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -530,12 +530,12 @@ impl<'tcx> Instance<'tcx> {
         def_id: DefId,
         args: ty::GenericArgsRef<'tcx>,
         requested_kind: ty::ClosureKind,
-    ) -> Option<Instance<'tcx>> {
+    ) -> Instance<'tcx> {
         let actual_kind = args.as_closure().kind();
 
         match needs_fn_once_adapter_shim(actual_kind, requested_kind) {
             Ok(true) => Instance::fn_once_adapter_instance(tcx, def_id, args),
-            _ => Some(Instance::new(def_id, args)),
+            _ => Instance::new(def_id, args),
         }
     }
 
@@ -550,7 +550,7 @@ impl<'tcx> Instance<'tcx> {
         tcx: TyCtxt<'tcx>,
         closure_did: DefId,
         args: ty::GenericArgsRef<'tcx>,
-    ) -> Option<Instance<'tcx>> {
+    ) -> Instance<'tcx> {
         let fn_once = tcx.require_lang_item(LangItem::FnOnce, None);
         let call_once = tcx
             .associated_items(fn_once)
@@ -564,14 +564,12 @@ impl<'tcx> Instance<'tcx> {
 
         let self_ty = Ty::new_closure(tcx, closure_did, args);
 
-        let sig = args.as_closure().sig();
-        let sig =
-            tcx.try_normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig).ok()?;
-        assert_eq!(sig.inputs().len(), 1);
-        let args = tcx.mk_args_trait(self_ty, [sig.inputs()[0].into()]);
+        let tupled_inputs_ty = args.as_closure().sig().map_bound(|sig| sig.inputs()[0]);
+        let tupled_inputs_ty = tcx.instantiate_bound_regions_with_erased(tupled_inputs_ty);
+        let args = tcx.mk_args_trait(self_ty, [tupled_inputs_ty.into()]);
 
-        debug!(?self_ty, ?sig);
-        Some(Instance { def, args })
+        debug!(?self_ty, args=?tupled_inputs_ty.tuple_fields());
+        Instance { def, args }
     }
 
     pub fn try_resolve_item_for_coroutine(
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index a06f4c6ba12..73052be30ae 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -804,17 +804,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     }
                 } else {
                     p!(print_def_path(did, args));
-                    p!(" upvar_tys=(");
-                    if !args.as_coroutine().is_valid() {
-                        p!("unavailable");
-                    } else {
-                        self.comma_sep(args.as_coroutine().upvar_tys().iter())?;
-                    }
-                    p!(")");
-
-                    if args.as_coroutine().is_valid() {
-                        p!(" ", print(args.as_coroutine().witness()));
-                    }
+                    p!(
+                        " upvar_tys=",
+                        print(args.as_coroutine().tupled_upvars_ty()),
+                        " witness=",
+                        print(args.as_coroutine().witness())
+                    );
                 }
 
                 p!("}}")
@@ -868,19 +863,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     }
                 } else {
                     p!(print_def_path(did, args));
-                    if !args.as_closure().is_valid() {
-                        p!(" closure_args=(unavailable)");
-                        p!(write(" args={}", args.print_as_list()));
-                    } else {
-                        p!(" closure_kind_ty=", print(args.as_closure().kind_ty()));
-                        p!(
-                            " closure_sig_as_fn_ptr_ty=",
-                            print(args.as_closure().sig_as_fn_ptr_ty())
-                        );
-                        p!(" upvar_tys=(");
-                        self.comma_sep(args.as_closure().upvar_tys().iter())?;
-                        p!(")");
-                    }
+                    p!(
+                        " closure_kind_ty=",
+                        print(args.as_closure().kind_ty()),
+                        " closure_sig_as_fn_ptr_ty=",
+                        print(args.as_closure().sig_as_fn_ptr_ty()),
+                        " upvar_tys=",
+                        print(args.as_closure().tupled_upvars_ty())
+                    );
                 }
                 p!("}}");
             }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 8cf5fc8013f..b089f4a9e78 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -242,9 +242,15 @@ pub struct ClosureArgs<'tcx> {
 
 /// Struct returned by `split()`.
 pub struct ClosureArgsParts<'tcx> {
+    /// This is the args of the typeck root.
     pub parent_args: &'tcx [GenericArg<'tcx>],
+    /// Represents the maximum calling capability of the closure.
     pub closure_kind_ty: Ty<'tcx>,
+    /// Captures the closure's signature. This closure signature is "tupled", and
+    /// thus has a peculiar signature of `extern "rust-call" fn((Args, ...)) -> Ty`.
     pub closure_sig_as_fn_ptr_ty: Ty<'tcx>,
+    /// The upvars captured by the closure. Remains an inference variable
+    /// until the upvar analysis, which happens late in HIR typeck.
     pub tupled_upvars_ty: Ty<'tcx>,
 }
 
@@ -277,15 +283,6 @@ impl<'tcx> ClosureArgs<'tcx> {
         }
     }
 
-    /// Returns `true` only if enough of the synthetic types are known to
-    /// allow using all of the methods on `ClosureArgs` without panicking.
-    ///
-    /// Used primarily by `ty::print::pretty` to be able to handle closure
-    /// types that haven't had their synthetic types substituted in.
-    pub fn is_valid(self) -> bool {
-        self.args.len() >= 3 && matches!(self.split().tupled_upvars_ty.kind(), Tuple(_))
-    }
-
     /// Returns the substitutions of the closure's parent.
     pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] {
         self.split().parent_args
@@ -296,9 +293,9 @@ impl<'tcx> ClosureArgs<'tcx> {
     /// empty iterator is returned.
     #[inline]
     pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> {
-        match self.tupled_upvars_ty().kind() {
+        match *self.tupled_upvars_ty().kind() {
             TyKind::Error(_) => ty::List::empty(),
-            TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(),
+            TyKind::Tuple(tys) => tys,
             TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
             ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
         }
@@ -337,10 +334,9 @@ impl<'tcx> ClosureArgs<'tcx> {
 
     /// Extracts the signature from the closure.
     pub fn sig(self) -> ty::PolyFnSig<'tcx> {
-        let ty = self.sig_as_fn_ptr_ty();
-        match ty.kind() {
-            ty::FnPtr(sig) => *sig,
-            _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind()),
+        match *self.sig_as_fn_ptr_ty().kind() {
+            ty::FnPtr(sig) => sig,
+            ty => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {ty:?}"),
         }
     }
 
@@ -356,11 +352,17 @@ pub struct CoroutineArgs<'tcx> {
 }
 
 pub struct CoroutineArgsParts<'tcx> {
+    /// This is the args of the typeck root.
     pub parent_args: &'tcx [GenericArg<'tcx>],
     pub resume_ty: Ty<'tcx>,
     pub yield_ty: Ty<'tcx>,
     pub return_ty: Ty<'tcx>,
+    /// The interior type of the coroutine.
+    /// Represents all types that are stored in locals
+    /// in the coroutine's body.
     pub witness: Ty<'tcx>,
+    /// The upvars captured by the closure. Remains an inference variable
+    /// until the upvar analysis, which happens late in HIR typeck.
     pub tupled_upvars_ty: Ty<'tcx>,
 }
 
@@ -397,15 +399,6 @@ impl<'tcx> CoroutineArgs<'tcx> {
         }
     }
 
-    /// Returns `true` only if enough of the synthetic types are known to
-    /// allow using all of the methods on `CoroutineArgs` without panicking.
-    ///
-    /// Used primarily by `ty::print::pretty` to be able to handle coroutine
-    /// types that haven't had their synthetic types substituted in.
-    pub fn is_valid(self) -> bool {
-        self.args.len() >= 5 && matches!(self.split().tupled_upvars_ty.kind(), Tuple(_))
-    }
-
     /// Returns the substitutions of the coroutine's parent.
     pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] {
         self.split().parent_args
@@ -425,9 +418,9 @@ impl<'tcx> CoroutineArgs<'tcx> {
     /// empty iterator is returned.
     #[inline]
     pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> {
-        match self.tupled_upvars_ty().kind() {
+        match *self.tupled_upvars_ty().kind() {
             TyKind::Error(_) => ty::List::empty(),
-            TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(),
+            TyKind::Tuple(tys) => tys,
             TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
             ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
         }
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index 5428333a116..c669d3fd623 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -20,6 +20,10 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
             @call(mir_storage_dead, args) => {
                 Ok(StatementKind::StorageDead(self.parse_local(args[0])?))
             },
+            @call(mir_assume, args) => {
+                let op = self.parse_operand(args[0])?;
+                Ok(StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume(op))))
+            },
             @call(mir_deinit, args) => {
                 Ok(StatementKind::Deinit(Box::new(self.parse_place(args[0])?)))
             },
diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
index 6653b99b3f5..720515f262d 100644
--- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
@@ -305,7 +305,10 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
+    /// There can be many more `MovePathIndex` than there are locals in a MIR body.
+    /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = MaybeReachable<ChunkedBitSet<MovePathIndex>>;
+
     const NAME: &'static str = "maybe_init";
 
     fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
@@ -437,6 +440,8 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
+    /// There can be many more `MovePathIndex` than there are locals in a MIR body.
+    /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = ChunkedBitSet<MovePathIndex>;
 
     const NAME: &'static str = "maybe_uninit";
@@ -636,6 +641,8 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
+    /// There can be many more `InitIndex` than there are locals in a MIR body.
+    /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = ChunkedBitSet<InitIndex>;
 
     const NAME: &'static str = "ever_init";
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 04bae6ae2fe..334fa9976f0 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -1,4 +1,4 @@
-use rustc_index::bit_set::{BitSet, ChunkedBitSet};
+use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::{
     self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges,
@@ -26,14 +26,14 @@ use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis};
 pub struct MaybeLiveLocals;
 
 impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals {
-    type Domain = ChunkedBitSet<Local>;
+    type Domain = BitSet<Local>;
     type Direction = Backward;
 
     const NAME: &'static str = "liveness";
 
     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
         // bottom = not live
-        ChunkedBitSet::new_empty(body.local_decls.len())
+        BitSet::new_empty(body.local_decls.len())
     }
 
     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
@@ -233,14 +233,14 @@ impl<'a> MaybeTransitiveLiveLocals<'a> {
 }
 
 impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> {
-    type Domain = ChunkedBitSet<Local>;
+    type Domain = BitSet<Local>;
     type Direction = Backward;
 
     const NAME: &'static str = "transitive liveness";
 
     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
         // bottom = not live
-        ChunkedBitSet::new_empty(body.local_decls.len())
+        BitSet::new_empty(body.local_decls.len())
     }
 
     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs
index ff17ce1fe07..bbfb37d2a82 100644
--- a/compiler/rustc_mir_dataflow/src/points.rs
+++ b/compiler/rustc_mir_dataflow/src/points.rs
@@ -1,5 +1,5 @@
 use crate::framework::{visit_results, ResultsVisitable, ResultsVisitor};
-use rustc_index::bit_set::ChunkedBitSet;
+use rustc_index::bit_set::BitSet;
 use rustc_index::interval::SparseIntervalMatrix;
 use rustc_index::Idx;
 use rustc_index::IndexVec;
@@ -102,7 +102,7 @@ pub fn save_as_intervals<'tcx, N, R>(
 ) -> SparseIntervalMatrix<N, PointIndex>
 where
     N: Idx,
-    R: ResultsVisitable<'tcx, FlowState = ChunkedBitSet<N>>,
+    R: ResultsVisitable<'tcx, FlowState = BitSet<N>>,
 {
     let values = SparseIntervalMatrix::new(elements.num_points());
     let mut visitor = Visitor { elements, values };
@@ -124,7 +124,7 @@ impl<'mir, 'tcx, R, N> ResultsVisitor<'mir, 'tcx, R> for Visitor<'_, N>
 where
     N: Idx,
 {
-    type FlowState = ChunkedBitSet<N>;
+    type FlowState = BitSet<N>;
 
     fn visit_statement_after_primary_effect(
         &mut self,
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
index 08a5d70fb6f..cbbf3548c07 100644
--- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs
+++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -12,7 +12,7 @@ use crate::MoveDataParamEnv;
 use crate::{Analysis, JoinSemiLattice, ResultsCursor};
 use rustc_ast::MetaItem;
 use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::ChunkedBitSet;
+use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::MirPass;
 use rustc_middle::mir::{self, Body, Local, Location};
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -275,7 +275,7 @@ impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
         &self,
         tcx: TyCtxt<'tcx>,
         place: mir::Place<'tcx>,
-        flow_state: &ChunkedBitSet<Local>,
+        flow_state: &BitSet<Local>,
         call: PeekCall,
     ) {
         info!(?place, "peek_at");
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index e87f68a0905..7a70ed5cb7f 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -566,11 +566,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         cost: &CostChecker<'_, 'tcx>,
         depth: usize,
     ) {
-        let register_opportunity = |c: Condition| {
-            debug!(?bb, ?c.target, "register");
-            self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
-        };
-
         let term = self.body.basic_blocks[bb].terminator();
         let place_to_flood = match term.kind {
             // We come from a target, so those are not possible.
@@ -592,16 +587,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
             // Flood the overwritten place, and progress through.
             TerminatorKind::Drop { place: destination, .. }
             | TerminatorKind::Call { destination, .. } => Some(destination),
-            // Treat as an `assume(cond == expected)`.
-            TerminatorKind::Assert { ref cond, expected, .. } => {
-                if let Some(place) = cond.place()
-                    && let Some(conditions) = state.try_get(place.as_ref(), self.map)
-                {
-                    let expected = if expected { ScalarInt::TRUE } else { ScalarInt::FALSE };
-                    conditions.iter_matches(expected).for_each(register_opportunity);
-                }
-                None
-            }
+            // Ignore, as this can be a no-op at codegen time.
+            TerminatorKind::Assert { .. } => None,
         };
 
         // We can recurse through this terminator.
diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs
index ff309bd10ec..c3a92911bbf 100644
--- a/compiler/rustc_mir_transform/src/nrvo.rs
+++ b/compiler/rustc_mir_transform/src/nrvo.rs
@@ -1,7 +1,7 @@
 //! See the docs for [`RenameReturnPlace`].
 
 use rustc_hir::Mutability;
-use rustc_index::bit_set::HybridBitSet;
+use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::{self, BasicBlock, Local, Location};
 use rustc_middle::ty::TyCtxt;
@@ -123,7 +123,7 @@ fn find_local_assigned_to_return_place(
     body: &mut mir::Body<'_>,
 ) -> Option<Local> {
     let mut block = start;
-    let mut seen = HybridBitSet::new_empty(body.basic_blocks.len());
+    let mut seen = BitSet::new_empty(body.basic_blocks.len());
 
     // Iterate as long as `block` has exactly one predecessor that we have not yet visited.
     while seen.insert(block) {
diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs
index 795f5232ee3..f68e592db15 100644
--- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs
+++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs
@@ -7,14 +7,10 @@ pub struct RemoveStorageMarkers;
 
 impl<'tcx> MirPass<'tcx> for RemoveStorageMarkers {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        sess.mir_opt_level() > 0
+        sess.mir_opt_level() > 0 && !sess.emit_lifetime_markers()
     }
 
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        if tcx.sess.emit_lifetime_markers() {
-            return;
-        }
-
+    fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         trace!("Running RemoveStorageMarkers on {:?}", body.source);
         for data in body.basic_blocks.as_mut_preserves_cfg() {
             data.statements.retain(|statement| match statement.kind {
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 3e090661414..8b991268819 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -783,8 +783,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
                             def_id,
                             args,
                             ty::ClosureKind::FnOnce,
-                        )
-                        .expect("failed to normalize and resolve closure during codegen");
+                        );
                         if should_codegen_locally(self.tcx, &instance) {
                             self.output.push(create_fn_mono_item(self.tcx, instance, span));
                         }
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 10085f659b3..94a1fb33f99 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -464,7 +464,10 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let def_id = def.0.internal(&mut *tables, tcx);
         let args_ref = args.internal(&mut *tables, tcx);
         let closure_kind = kind.internal(&mut *tables, tcx);
-        Instance::resolve_closure(tables.tcx, def_id, args_ref, closure_kind).stable(&mut *tables)
+        Some(
+            Instance::resolve_closure(tables.tcx, def_id, args_ref, closure_kind)
+                .stable(&mut *tables),
+        )
     }
 
     fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result<Allocation, Error> {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6c39a38750e..90a38b26f73 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1028,6 +1028,7 @@ symbols! {
         minnumf32,
         minnumf64,
         mips_target_feature,
+        mir_assume,
         mir_basic_block,
         mir_call,
         mir_cast_transmute,
diff --git a/compiler/rustc_target/src/spec/base/dragonfly.rs b/compiler/rustc_target/src/spec/base/dragonfly.rs
index de2be781796..3c1846696f7 100644
--- a/compiler/rustc_target/src/spec/base/dragonfly.rs
+++ b/compiler/rustc_target/src/spec/base/dragonfly.rs
@@ -8,6 +8,7 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         position_independent_executables: true,
         relro_level: RelroLevel::Full,
+        has_thread_local: true,
         default_dwarf_version: 2,
         ..Default::default()
     }
diff --git a/compiler/rustc_target/src/spec/base/freebsd.rs b/compiler/rustc_target/src/spec/base/freebsd.rs
index 80b3da8a752..c772754aa8d 100644
--- a/compiler/rustc_target/src/spec/base/freebsd.rs
+++ b/compiler/rustc_target/src/spec/base/freebsd.rs
@@ -9,6 +9,7 @@ pub fn opts() -> TargetOptions {
         crt_static_respected: true,
         position_independent_executables: true,
         relro_level: RelroLevel::Full,
+        has_thread_local: true,
         abi_return_struct_as_int: true,
         default_dwarf_version: 2,
         ..Default::default()
diff --git a/compiler/rustc_target/src/spec/base/netbsd.rs b/compiler/rustc_target/src/spec/base/netbsd.rs
index be94ea23465..495e3d10fbc 100644
--- a/compiler/rustc_target/src/spec/base/netbsd.rs
+++ b/compiler/rustc_target/src/spec/base/netbsd.rs
@@ -9,6 +9,7 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         position_independent_executables: true,
         relro_level: RelroLevel::Full,
+        has_thread_local: true,
         use_ctors_section: true,
         default_dwarf_version: 2,
         ..Default::default()
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 5d74ebebdf3..884bd23e8cc 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1597,6 +1597,7 @@ supported_targets! {
     ("x86_64-unikraft-linux-musl", x86_64_unikraft_linux_musl),
 
     ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf),
+    ("riscv32im-risc0-zkvm-elf", riscv32im_risc0_zkvm_elf),
     ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf),
     ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf),
     ("riscv32imc-esp-espidf", riscv32imc_esp_espidf),
diff --git a/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs
new file mode 100644
index 00000000000..bf819de4133
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs
@@ -0,0 +1,36 @@
+use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+    Target {
+        data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+        llvm_target: "riscv32".into(),
+        pointer_width: 32,
+        arch: "riscv32".into(),
+
+        options: TargetOptions {
+            os: "zkvm".into(),
+            vendor: "risc0".into(),
+            linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+            linker: Some("rust-lld".into()),
+            cpu: "generic-rv32".into(),
+
+            // Some crates (*cough* crossbeam) assume you have 64 bit
+            // atomics if the target name is not in a hardcoded list.
+            // Since zkvm is singlethreaded and all operations are
+            // atomic, I guess we can just say we support 64-bit
+            // atomics.
+            max_atomic_width: Some(64),
+            atomic_cas: true,
+
+            features: "+m".into(),
+            executables: true,
+            panic_strategy: PanicStrategy::Abort,
+            relocation_model: RelocModel::Static,
+            emit_debug_gdb_scripts: false,
+            eh_frame_header: false,
+            singlethread: true,
+            ..Default::default()
+        },
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
index 6d5728797d1..9f91c02c1ab 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
@@ -1,7 +1,7 @@
 //! Computes a normalizes-to (projection) goal for inherent associated types,
 //! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`.
 //!
-//! Since a weak alias is not ambiguous, this just computes the `type_of` of
+//! Since a weak alias is never ambiguous, this just computes the `type_of` of
 //! the alias and registers the where-clauses of the type alias.
 use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult};
 use rustc_middle::ty;
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 0e33e9cd790..e31aaaa1969 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -3152,6 +3152,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         ],
                         Applicability::MachineApplicable,
                     );
+                } else {
+                    // FIXME: we may suggest array::repeat instead
+                    err.help("consider using `core::array::from_fn` to initialize the array");
+                    err.help("see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information");
                 }
 
                 if self.tcx.sess.is_nightly_build()
diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
index 138bc6129f7..79f03242c58 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -232,32 +232,12 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
             Ok::<_, NoSolution>(())
         })?,
 
-        ty::Closure(_, args) => {
-            if !args.as_closure().is_valid() {
-                // By the time this code runs, all type variables ought to
-                // be fully resolved.
-
-                tcx.dcx().span_delayed_bug(
-                    span,
-                    format!("upvar_tys for closure not found. Expected capture information for closure {ty}",),
-                );
-                return Err(NoSolution);
+        ty::Closure(_, args) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
+            for ty in args.as_closure().upvar_tys() {
+                dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, ty, constraints)?;
             }
-
-            rustc_data_structures::stack::ensure_sufficient_stack(|| {
-                for ty in args.as_closure().upvar_tys() {
-                    dtorck_constraint_for_ty_inner(
-                        tcx,
-                        param_env,
-                        span,
-                        depth + 1,
-                        ty,
-                        constraints,
-                    )?;
-                }
-                Ok::<_, NoSolution>(())
-            })?
-        }
+            Ok::<_, NoSolution>(())
+        })?,
 
         ty::Coroutine(_, args) => {
             // rust-lang/rust#49918: types can be constructed, stored
@@ -283,15 +263,6 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
             // derived from lifetimes attached to the upvars and resume
             // argument, and we *do* incorporate those here.
             let args = args.as_coroutine();
-            if !args.is_valid() {
-                // By the time this code runs, all type variables ought to
-                // be fully resolved.
-                tcx.dcx().span_delayed_bug(
-                    span,
-                    format!("upvar_tys for coroutine not found. Expected capture information for coroutine {ty}",),
-                );
-                return Err(NoSolution);
-            }
 
             // While we conservatively assume that all coroutines require drop
             // to avoid query cycles during MIR building, we can check the actual
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 957de925dd3..fb09f094d37 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
@@ -141,6 +141,13 @@ where
         infcx: &InferCtxt<'tcx>,
         span: Span,
     ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
+        // In the new trait solver, query type ops are performed locally. This
+        // is because query type ops currently use the old canonicalizer, and
+        // that doesn't preserve things like opaques which have been registered
+        // during MIR typeck. Even after the old canonicalizer is gone, it's
+        // probably worthwhile just keeping this run-locally logic, since we
+        // probably don't gain much from caching here given the new solver does
+        // caching internally.
         if infcx.next_trait_solver() {
             return Ok(scrape_region_constraints(
                 infcx,
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index e5e31f7caaa..2d76cf994e4 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -265,7 +265,12 @@ fn resolve_associated_item<'tcx>(
                 match *rcvr_args.type_at(0).kind() {
                     ty::Closure(closure_def_id, args) => {
                         let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
-                        Instance::resolve_closure(tcx, closure_def_id, args, trait_closure_kind)
+                        Some(Instance::resolve_closure(
+                            tcx,
+                            closure_def_id,
+                            args,
+                            trait_closure_kind,
+                        ))
                     }
                     ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
                         def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 0bb833c66fa..ef67317a601 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -118,6 +118,69 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
         }
         TaitInBodyFinder { collector: self }.visit_expr(body);
     }
+
+    fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) {
+        if !self.seen.insert(alias_ty.def_id.expect_local()) {
+            return;
+        }
+
+        // TAITs outside their defining scopes are ignored.
+        let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
+        trace!(?origin);
+        match origin {
+            rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
+            rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
+                if !in_assoc_ty {
+                    if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
+                        return;
+                    }
+                }
+            }
+        }
+
+        self.opaques.push(alias_ty.def_id.expect_local());
+
+        let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
+        // Only check that the parent generics of the TAIT/RPIT are unique.
+        // the args owned by the opaque are going to always be duplicate
+        // lifetime params for RPITs, and empty for TAITs.
+        match self
+            .tcx
+            .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
+        {
+            Ok(()) => {
+                // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
+                // supported at all, so this is sound to do, but once we want to support them, you'll
+                // start seeing the error below.
+
+                // Collect opaque types nested within the associated type bounds of this opaque type.
+                // We use identity args here, because we already know that the opaque type uses
+                // only generic parameters, and thus substituting would not give us more information.
+                for (pred, span) in self
+                    .tcx
+                    .explicit_item_bounds(alias_ty.def_id)
+                    .instantiate_identity_iter_copied()
+                {
+                    trace!(?pred);
+                    self.visit_spanned(span, pred);
+                }
+            }
+            Err(NotUniqueParam::NotParam(arg)) => {
+                self.tcx.dcx().emit_err(NotParam {
+                    arg,
+                    span: self.span(),
+                    opaque_span: self.tcx.def_span(alias_ty.def_id),
+                });
+            }
+            Err(NotUniqueParam::DuplicateParam(arg)) => {
+                self.tcx.dcx().emit_err(DuplicateArg {
+                    arg,
+                    span: self.span(),
+                    opaque_span: self.tcx.def_span(alias_ty.def_id),
+                });
+            }
+        }
+    }
 }
 
 impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
@@ -134,67 +197,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
         t.super_visit_with(self)?;
         match t.kind() {
             ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
-                if !self.seen.insert(alias_ty.def_id.expect_local()) {
-                    return ControlFlow::Continue(());
-                }
-
-                // TAITs outside their defining scopes are ignored.
-                let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
-                trace!(?origin);
-                match origin {
-                    rustc_hir::OpaqueTyOrigin::FnReturn(_)
-                    | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
-                    rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
-                        if !in_assoc_ty {
-                            if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
-                                return ControlFlow::Continue(());
-                            }
-                        }
-                    }
-                }
-
-                self.opaques.push(alias_ty.def_id.expect_local());
-
-                let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
-                // Only check that the parent generics of the TAIT/RPIT are unique.
-                // the args owned by the opaque are going to always be duplicate
-                // lifetime params for RPITs, and empty for TAITs.
-                match self.tcx.uses_unique_generic_params(
-                    &alias_ty.args[..parent_count],
-                    CheckRegions::FromFunction,
-                ) {
-                    Ok(()) => {
-                        // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
-                        // supported at all, so this is sound to do, but once we want to support them, you'll
-                        // start seeing the error below.
-
-                        // Collect opaque types nested within the associated type bounds of this opaque type.
-                        // We use identity args here, because we already know that the opaque type uses
-                        // only generic parameters, and thus substituting would not give us more information.
-                        for (pred, span) in self
-                            .tcx
-                            .explicit_item_bounds(alias_ty.def_id)
-                            .instantiate_identity_iter_copied()
-                        {
-                            trace!(?pred);
-                            self.visit_spanned(span, pred);
-                        }
-                    }
-                    Err(NotUniqueParam::NotParam(arg)) => {
-                        self.tcx.dcx().emit_err(NotParam {
-                            arg,
-                            span: self.span(),
-                            opaque_span: self.tcx.def_span(alias_ty.def_id),
-                        });
-                    }
-                    Err(NotUniqueParam::DuplicateParam(arg)) => {
-                        self.tcx.dcx().emit_err(DuplicateArg {
-                            arg,
-                            span: self.span(),
-                            opaque_span: self.tcx.def_span(alias_ty.def_id),
-                        });
-                    }
-                }
+                self.visit_opaque_ty(alias_ty);
             }
             ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
                 self.tcx
@@ -272,6 +275,91 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
     }
 }
 
+struct ImplTraitInAssocTypeCollector<'tcx>(OpaqueTypeCollector<'tcx>);
+
+impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for ImplTraitInAssocTypeCollector<'tcx> {
+    #[instrument(skip(self), ret, level = "trace")]
+    fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> ControlFlow<!> {
+        let old = self.0.span;
+        self.0.span = Some(span);
+        value.visit_with(self);
+        self.0.span = old;
+
+        ControlFlow::Continue(())
+    }
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInAssocTypeCollector<'tcx> {
+    #[instrument(skip(self), ret, level = "trace")]
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
+        t.super_visit_with(self)?;
+        match t.kind() {
+            ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
+                self.0.visit_opaque_ty(alias_ty);
+            }
+            ty::Alias(ty::Projection, alias_ty) => {
+                // This avoids having to do normalization of `Self::AssocTy` by only
+                // supporting the case of a method defining opaque types from assoc types
+                // in the same impl block.
+                let parent_trait_ref = self
+                    .0
+                    .parent_trait_ref()
+                    .expect("impl trait in assoc type collector used on non-assoc item");
+                // If the trait ref of the associated item and the impl differs,
+                // then we can't use the impl's identity substitutions below, so
+                // just skip.
+                if alias_ty.trait_ref(self.0.tcx) == parent_trait_ref {
+                    let parent = self.0.parent().expect("we should have a parent here");
+
+                    for &assoc in self.0.tcx.associated_items(parent).in_definition_order() {
+                        trace!(?assoc);
+                        if assoc.trait_item_def_id != Some(alias_ty.def_id) {
+                            continue;
+                        }
+
+                        // If the type is further specializable, then the type_of
+                        // is not actually correct below.
+                        if !assoc.defaultness(self.0.tcx).is_final() {
+                            continue;
+                        }
+
+                        let impl_args = alias_ty.args.rebase_onto(
+                            self.0.tcx,
+                            parent_trait_ref.def_id,
+                            ty::GenericArgs::identity_for_item(self.0.tcx, parent),
+                        );
+
+                        if check_args_compatible(self.0.tcx, assoc, impl_args) {
+                            return self
+                                .0
+                                .tcx
+                                .type_of(assoc.def_id)
+                                .instantiate(self.0.tcx, impl_args)
+                                .visit_with(self);
+                        } else {
+                            self.0.tcx.dcx().span_delayed_bug(
+                                self.0.tcx.def_span(assoc.def_id),
+                                "item had incorrect args",
+                            );
+                        }
+                    }
+                }
+            }
+            _ => trace!(kind=?t.kind()),
+        }
+        ControlFlow::Continue(())
+    }
+}
+
+fn impl_trait_in_assoc_types_defined_by<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    item: LocalDefId,
+) -> &'tcx ty::List<LocalDefId> {
+    let mut collector = ImplTraitInAssocTypeCollector(OpaqueTypeCollector::new(tcx, item));
+    super::sig_types::walk_types(tcx, item, &mut collector);
+    tcx.mk_local_def_ids(&collector.0.opaques)
+}
+
 fn opaque_types_defined_by<'tcx>(
     tcx: TyCtxt<'tcx>,
     item: LocalDefId,
@@ -321,5 +409,6 @@ fn opaque_types_defined_by<'tcx>(
 }
 
 pub(super) fn provide(providers: &mut Providers) {
-    *providers = Providers { opaque_types_defined_by, ..*providers };
+    *providers =
+        Providers { opaque_types_defined_by, impl_trait_in_assoc_types_defined_by, ..*providers };
 }