about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-09-15 11:19:28 +0000
committerbors <bors@rust-lang.org>2024-09-15 11:19:28 +0000
commitdde7d6649e421d8b2f074fd3ddd0d712d3448e65 (patch)
treef3624e6ef711faa0e975c21d76e82da3b5ab9b5d /compiler
parent1ae268816ca3f5f314ccbfd04affd61cb081e13f (diff)
parent729aa49d0fbb9f5058e0633471091b430453ae33 (diff)
Auto merge of #130390 - matthiaskrgr:rollup-evbfwe2, r=matthiaskrgr
Rollup of 5 pull requests

Successful merges:

 - #129195 (Stabilize `&mut` (and `*mut`) as well as `&Cell` (and `*const Cell`) in const)
 - #130118 (move Option::unwrap_unchecked into const_option feature gate)
 - #130295 (Fix target-cpu fpu features on Armv8-R.)
 - #130371 (Correctly account for niche-optimized tags in rustc_transmute)
 - #130381 (library: Compute Rust exception class from its string repr)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_const_eval/messages.ftl10
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs254
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs116
-rw-r--r--compiler/rustc_const_eval/src/errors.rs31
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0764.md4
-rw-r--r--compiler/rustc_feature/src/accepted.rs4
-rw-r--r--compiler/rustc_feature/src/unstable.rs4
-rw-r--r--compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs12
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs35
9 files changed, 65 insertions, 405 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 8a6dfb89249..2dc9d57e3b5 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -134,9 +134,6 @@ const_eval_incompatible_return_types =
 const_eval_incompatible_types =
     calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
 
-const_eval_interior_mutability_borrow =
-    cannot borrow here, since the borrowed element may contain interior mutability
-
 const_eval_interior_mutable_data_refer =
     {const_eval_const_context}s cannot refer to interior mutable data
     .label = this borrow of an interior mutable value may end up in the final value
@@ -230,9 +227,6 @@ const_eval_memory_exhausted =
 const_eval_modified_global =
     modifying a static's initial value from another static's initializer
 
-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_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
@@ -363,10 +357,6 @@ const_eval_too_generic =
 const_eval_too_many_caller_args =
     calling a function with more arguments than it expected
 
-const_eval_transient_mut_borrow = 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
 const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index c20c2c60c1c..6d09ed5b4bf 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -11,18 +11,17 @@ use rustc_hir::{self as hir, LangItem};
 use rustc_index::bit_set::BitSet;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::ObligationCause;
-use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
 use rustc_middle::span_bug;
 use rustc_middle::ty::adjustment::PointerCoercion;
-use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TypeVisitableExt};
 use rustc_mir_dataflow::impls::MaybeStorageLive;
 use rustc_mir_dataflow::storage::always_storage_live_locals;
 use rustc_mir_dataflow::Analysis;
 use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
-use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
 use tracing::{debug, instrument, trace};
 
 use super::ops::{self, NonConstOp, Status};
@@ -166,24 +165,6 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
     }
 }
 
-struct LocalReturnTyVisitor<'a, 'mir, 'tcx> {
-    kind: LocalKind,
-    checker: &'a mut Checker<'mir, 'tcx>,
-}
-
-impl<'a, 'mir, 'tcx> TypeVisitor<TyCtxt<'tcx>> for LocalReturnTyVisitor<'a, 'mir, 'tcx> {
-    fn visit_ty(&mut self, t: Ty<'tcx>) {
-        match t.kind() {
-            ty::FnPtr(..) => {}
-            ty::Ref(_, _, hir::Mutability::Mut) => {
-                self.checker.check_op(ops::mut_ref::MutRef(self.kind));
-                t.super_visit_with(self)
-            }
-            _ => t.super_visit_with(self),
-        }
-    }
-}
-
 pub struct Checker<'mir, 'tcx> {
     ccx: &'mir ConstCx<'mir, 'tcx>,
     qualifs: Qualifs<'mir, 'tcx>,
@@ -230,25 +211,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
             return;
         }
 
-        // The local type and predicate checks are not free and only relevant for `const fn`s.
-        if self.const_kind() == hir::ConstContext::ConstFn {
-            for (idx, local) in body.local_decls.iter_enumerated() {
-                // Handle the return place below.
-                if idx == RETURN_PLACE {
-                    continue;
-                }
-
-                self.span = local.source_info.span;
-                self.check_local_or_return_ty(local.ty, idx);
-            }
-
-            // impl trait is gone in MIR, so check the return type of a const fn by its signature
-            // instead of the type of the return place.
-            self.span = body.local_decls[RETURN_PLACE].source_info.span;
-            let return_ty = self.ccx.fn_sig().output();
-            self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE);
-        }
-
         if !tcx.has_attr(def_id, sym::rustc_do_not_const_check) {
             self.visit_body(body);
         }
@@ -358,16 +320,11 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
         self.check_op_spanned(ops::StaticAccess, span)
     }
 
-    fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>, local: Local) {
-        let kind = self.body.local_kind(local);
-
-        let mut visitor = LocalReturnTyVisitor { kind, checker: self };
-
-        visitor.visit_ty(ty);
-    }
-
-    fn check_mut_borrow(&mut self, place: &Place<'_>, kind: hir::BorrowKind) {
-        match self.const_kind() {
+    /// Returns whether this place can possibly escape the evaluation of the current const/static
+    /// initializer. The check assumes that all already existing pointers and references point to
+    /// non-escaping places.
+    fn place_may_escape(&mut self, place: &Place<'_>) -> bool {
+        let is_transient = match self.const_kind() {
             // In a const fn all borrows are transient or point to the places given via
             // references in the arguments (so we already checked them with
             // TransientMutBorrow/MutBorrow as appropriate).
@@ -375,7 +332,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
             // NOTE: Once we have heap allocations during CTFE we need to figure out
             // how to prevent `const fn` to create long-lived allocations that point
             // to mutable memory.
-            hir::ConstContext::ConstFn => self.check_op(ops::TransientMutBorrow(kind)),
+            hir::ConstContext::ConstFn => true,
             _ => {
                 // For indirect places, we are not creating a new permanent borrow, it's just as
                 // transient as the already existing one. For reborrowing references this is handled
@@ -387,15 +344,16 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
                 // value of the constant.
                 // Note: This is only sound if every local that has a `StorageDead` has a
                 // `StorageDead` in every control flow path leading to a `return` terminator.
-                // The good news is that interning will detect if any unexpected mutable
-                // pointer slips through.
-                if place.is_indirect() || self.local_is_transient(place.local) {
-                    self.check_op(ops::TransientMutBorrow(kind));
-                } else {
-                    self.check_op(ops::MutBorrow(kind));
-                }
+                // If anything slips through, there's no safety net -- safe code can create
+                // references to variants of `!Freeze` enums as long as that variant is `Freeze`, so
+                // interning can't protect us here. (There *is* a safety net for mutable references
+                // though, interning will ICE if we miss something here.)
+                place.is_indirect() || self.local_is_transient(place.local)
             }
-        }
+        };
+        // Transient places cannot possibly escape because the place doesn't exist any more at the
+        // end of evaluation.
+        !is_transient
     }
 }
 
@@ -420,47 +378,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
 
-        // Special-case reborrows to be more like a copy of a reference.
-        // FIXME: this does not actually handle all reborrows. It only detects cases where `*` is the outermost
-        // projection of the borrowed place, it skips deref'ing raw pointers and it skips `static`.
-        // All those cases are handled below with shared/mutable borrows.
-        // Once `const_mut_refs` is stable, we should be able to entirely remove this special case.
-        // (`const_refs_to_cell` is not needed, we already allow all borrows of indirect places anyway.)
-        match *rvalue {
-            Rvalue::Ref(_, kind, place) => {
-                if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) {
-                    let ctx = match kind {
-                        BorrowKind::Shared => {
-                            PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
-                        }
-                        BorrowKind::Fake(_) => {
-                            PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow)
-                        }
-                        BorrowKind::Mut { .. } => {
-                            PlaceContext::MutatingUse(MutatingUseContext::Borrow)
-                        }
-                    };
-                    self.visit_local(reborrowed_place_ref.local, ctx, location);
-                    self.visit_projection(reborrowed_place_ref, ctx, location);
-                    return;
-                }
-            }
-            Rvalue::RawPtr(mutbl, place) => {
-                if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) {
-                    let ctx = match mutbl {
-                        Mutability::Not => {
-                            PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow)
-                        }
-                        Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::RawBorrow),
-                    };
-                    self.visit_local(reborrowed_place_ref.local, ctx, location);
-                    self.visit_projection(reborrowed_place_ref, ctx, location);
-                    return;
-                }
-            }
-            _ => {}
-        }
-
         self.super_rvalue(rvalue, location);
 
         match rvalue {
@@ -494,15 +411,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                 let is_allowed =
                     self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut);
 
-                if !is_allowed {
-                    self.check_mut_borrow(
-                        place,
-                        if matches!(rvalue, Rvalue::Ref(..)) {
-                            hir::BorrowKind::Ref
-                        } else {
-                            hir::BorrowKind::Raw
-                        },
-                    );
+                if !is_allowed && self.place_may_escape(place) {
+                    self.check_op(ops::EscapingMutBorrow(if matches!(rvalue, Rvalue::Ref(..)) {
+                        hir::BorrowKind::Ref
+                    } else {
+                        hir::BorrowKind::Raw
+                    }));
                 }
             }
 
@@ -514,40 +428,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     place.as_ref(),
                 );
 
-                // If the place is indirect, this is basically a reborrow. We have a reborrow
-                // special case above, but for raw pointers and pointers/references to `static` and
-                // when the `*` is not the first projection, `place_as_reborrow` does not recognize
-                // them as such, so we end up here. This should probably be considered a
-                // `TransientCellBorrow` (we consider the equivalent mutable case a
-                // `TransientMutBorrow`), but such reborrows got accidentally stabilized already and
-                // it is too much of a breaking change to take back.
-                if borrowed_place_has_mut_interior && !place.is_indirect() {
-                    match self.const_kind() {
-                        // In a const fn all borrows are transient or point to the places given via
-                        // references in the arguments (so we already checked them with
-                        // TransientCellBorrow/CellBorrow as appropriate).
-                        // The borrow checker guarantees that no new non-transient borrows are created.
-                        // NOTE: Once we have heap allocations during CTFE we need to figure out
-                        // how to prevent `const fn` to create long-lived allocations that point
-                        // to (interior) mutable memory.
-                        hir::ConstContext::ConstFn => self.check_op(ops::TransientCellBorrow),
-                        _ => {
-                            // Locals with StorageDead are definitely not part of the final constant value, and
-                            // it is thus inherently safe to permit such locals to have their
-                            // address taken as we can't end up with a reference to them in the
-                            // final value.
-                            // Note: This is only sound if every local that has a `StorageDead` has a
-                            // `StorageDead` in every control flow path leading to a `return` terminator.
-                            // If anything slips through, there's no safety net -- safe code can create
-                            // references to variants of `!Freeze` enums as long as that variant is `Freeze`,
-                            // so interning can't protect us here.
-                            if self.local_is_transient(place.local) {
-                                self.check_op(ops::TransientCellBorrow);
-                            } else {
-                                self.check_op(ops::CellBorrow);
-                            }
-                        }
-                    }
+                if borrowed_place_has_mut_interior && self.place_may_escape(place) {
+                    self.check_op(ops::EscapingCellBorrow);
                 }
             }
 
@@ -636,58 +518,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
             }
         }
     }
-    fn visit_projection_elem(
-        &mut self,
-        place_ref: PlaceRef<'tcx>,
-        elem: PlaceElem<'tcx>,
-        context: PlaceContext,
-        location: Location,
-    ) {
-        trace!(
-            "visit_projection_elem: place_ref={:?} elem={:?} \
-            context={:?} location={:?}",
-            place_ref, elem, context, location,
-        );
-
-        self.super_projection_elem(place_ref, elem, context, location);
-
-        match elem {
-            ProjectionElem::Deref => {
-                let base_ty = place_ref.ty(self.body, self.tcx).ty;
-                if base_ty.is_unsafe_ptr() {
-                    if place_ref.projection.is_empty() {
-                        let decl = &self.body.local_decls[place_ref.local];
-                        // If this is a static, then this is not really dereferencing a pointer,
-                        // just directly accessing a static. That is not subject to any feature
-                        // gates (except for the one about whether statics can even be used, but
-                        // that is checked already by `visit_operand`).
-                        if let LocalInfo::StaticRef { .. } = *decl.local_info() {
-                            return;
-                        }
-                    }
-
-                    // `*const T` is stable, `*mut T` is not
-                    if !base_ty.is_mutable_ptr() {
-                        return;
-                    }
-
-                    self.check_op(ops::RawMutPtrDeref);
-                }
-
-                if context.is_mutating_use() {
-                    self.check_op(ops::MutDeref);
-                }
-            }
-
-            ProjectionElem::ConstantIndex { .. }
-            | ProjectionElem::Downcast(..)
-            | ProjectionElem::OpaqueCast(..)
-            | ProjectionElem::Subslice { .. }
-            | ProjectionElem::Subtype(..)
-            | ProjectionElem::Field(..)
-            | ProjectionElem::Index(_) => {}
-        }
-    }
 
     fn visit_source_info(&mut self, source_info: &SourceInfo) {
         trace!("visit_source_info: source_info={:?}", source_info);
@@ -984,40 +814,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
     }
 }
 
-fn place_as_reborrow<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    body: &Body<'tcx>,
-    place: Place<'tcx>,
-) -> Option<PlaceRef<'tcx>> {
-    match place.as_ref().last_projection() {
-        Some((place_base, ProjectionElem::Deref)) => {
-            // FIXME: why do statics and raw pointers get excluded here? This makes
-            // some code involving mutable pointers unstable, but it is unclear
-            // why that code is treated differently from mutable references.
-            // Once TransientMutBorrow and TransientCellBorrow are stable,
-            // this can probably be cleaned up without any behavioral changes.
-
-            // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
-            // that points to the allocation for the static. Don't treat these as reborrows.
-            if body.local_decls[place_base.local].is_ref_to_static() {
-                None
-            } else {
-                // Ensure the type being derefed is a reference and not a raw pointer.
-                // This is sufficient to prevent an access to a `static mut` from being marked as a
-                // reborrow, even if the check above were to disappear.
-                let inner_ty = place_base.ty(body, tcx).ty;
-
-                if let ty::Ref(..) = inner_ty.kind() {
-                    return Some(place_base);
-                } else {
-                    return None;
-                }
-            }
-        }
-        _ => None,
-    }
-}
-
 fn is_int_bool_float_or_char(ty: Ty<'_>) -> bool {
     ty.is_bool() || ty.is_integral() || ty.is_char() || ty.is_floating_point()
 }
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index e8f10c88408..a52fc6a077b 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -8,7 +8,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
-use rustc_middle::mir::{self, CallSource};
+use rustc_middle::mir::CallSource;
 use rustc_middle::span_bug;
 use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _};
 use rustc_middle::ty::{
@@ -392,26 +392,11 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
 }
 
 #[derive(Debug)]
-/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
-/// the final value of the constant.
-pub(crate) struct TransientCellBorrow;
-impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
-    fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
-        Status::Unstable(sym::const_refs_to_cell)
-    }
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        ccx.tcx
-            .sess
-            .create_feature_err(errors::InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
-    }
-}
-
-#[derive(Debug)]
 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
 /// the final value of the constant, and thus we cannot allow this (for now). We may allow
 /// it in the future for static items.
-pub(crate) struct CellBorrow;
-impl<'tcx> NonConstOp<'tcx> for CellBorrow {
+pub(crate) struct EscapingCellBorrow;
+impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
     fn importance(&self) -> DiagImportance {
         // Most likely the code will try to do mutation with these borrows, which
         // triggers its own errors. Only show this one if that does not happen.
@@ -431,9 +416,9 @@ impl<'tcx> NonConstOp<'tcx> for CellBorrow {
 /// This op is for `&mut` borrows in the trailing expression of a constant
 /// which uses the "enclosing scopes rule" to leak its locals into anonymous
 /// static or const items.
-pub(crate) struct MutBorrow(pub hir::BorrowKind);
+pub(crate) struct EscapingMutBorrow(pub hir::BorrowKind);
 
-impl<'tcx> NonConstOp<'tcx> for MutBorrow {
+impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
     fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
         Status::Forbidden
     }
@@ -460,49 +445,6 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
     }
 }
 
-#[derive(Debug)]
-pub(crate) struct TransientMutBorrow(pub hir::BorrowKind);
-
-impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
-    fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
-        Status::Unstable(sym::const_mut_refs)
-    }
-
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        let kind = ccx.const_kind();
-        match self.0 {
-            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,
-            ),
-        }
-    }
-}
-
-#[derive(Debug)]
-pub(crate) struct MutDeref;
-impl<'tcx> NonConstOp<'tcx> for MutDeref {
-    fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
-        Status::Unstable(sym::const_mut_refs)
-    }
-
-    fn importance(&self) -> DiagImportance {
-        // Usually a side-effect of a `TransientMutBorrow` somewhere.
-        DiagImportance::Secondary
-    }
-
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        ccx.tcx.sess.create_feature_err(
-            errors::MutDerefErr { span, kind: ccx.const_kind() },
-            sym::const_mut_refs,
-        )
-    }
-}
-
 /// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
 #[derive(Debug)]
 pub(crate) struct PanicNonStr;
@@ -524,24 +466,6 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
     }
 }
 
-#[derive(Debug)]
-pub(crate) struct RawMutPtrDeref;
-impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
-    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
-        Status::Unstable(sym::const_mut_refs)
-    }
-
-    #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        feature_err(
-            &ccx.tcx.sess,
-            sym::const_mut_refs,
-            span,
-            format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
-        )
-    }
-}
-
 /// Casting raw pointer or function pointer to an integer.
 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
 /// allocation base addresses that are not known at compile-time.
@@ -588,33 +512,3 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
         ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
     }
 }
-
-/// Types that cannot appear in the signature or locals of a `const fn`.
-pub(crate) mod mut_ref {
-    use super::*;
-
-    #[derive(Debug)]
-    pub(crate) struct MutRef(pub mir::LocalKind);
-    impl<'tcx> NonConstOp<'tcx> for MutRef {
-        fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
-            Status::Unstable(sym::const_mut_refs)
-        }
-
-        fn importance(&self) -> DiagImportance {
-            match self.0 {
-                mir::LocalKind::Temp => DiagImportance::Secondary,
-                mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => DiagImportance::Primary,
-            }
-        }
-
-        #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
-        fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-            feature_err(
-                &ccx.tcx.sess,
-                sym::const_mut_refs,
-                span,
-                format!("mutable references are not allowed in {}s", ccx.const_kind()),
-            )
-        }
-    }
-}
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 57e52254757..b66b5c0a00a 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -94,30 +94,6 @@ pub(crate) struct PanicNonStrErr {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_mut_deref, code = E0658)]
-pub(crate) struct MutDerefErr {
-    #[primary_span]
-    pub span: Span,
-    pub kind: ConstContext,
-}
-
-#[derive(Diagnostic)]
-#[diag(const_eval_transient_mut_borrow, code = E0658)]
-pub(crate) struct TransientMutBorrowErr {
-    #[primary_span]
-    pub span: Span,
-    pub kind: ConstContext,
-}
-
-#[derive(Diagnostic)]
-#[diag(const_eval_transient_mut_raw, code = E0658)]
-pub(crate) struct TransientMutRawErr {
-    #[primary_span]
-    pub span: Span,
-    pub kind: ConstContext,
-}
-
-#[derive(Diagnostic)]
 #[diag(const_eval_max_num_nodes_in_const)]
 pub(crate) struct MaxNumNodesInConstErr {
     #[primary_span]
@@ -217,13 +193,6 @@ pub(crate) struct InteriorMutableDataRefer {
     pub teach: bool,
 }
 
-#[derive(Diagnostic)]
-#[diag(const_eval_interior_mutability_borrow)]
-pub(crate) struct InteriorMutabilityBorrow {
-    #[primary_span]
-    pub span: Span,
-}
-
 #[derive(LintDiagnostic)]
 #[diag(const_eval_long_running)]
 #[note]
diff --git a/compiler/rustc_error_codes/src/error_codes/E0764.md b/compiler/rustc_error_codes/src/error_codes/E0764.md
index 152627cf654..4d5091cd954 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0764.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0764.md
@@ -3,8 +3,6 @@ A mutable reference was used in a constant.
 Erroneous code example:
 
 ```compile_fail,E0764
-#![feature(const_mut_refs)]
-
 fn main() {
     const OH_NO: &'static mut usize = &mut 1; // error!
 }
@@ -26,8 +24,6 @@ Remember: you cannot use a function call inside a constant or static. However,
 you can totally use it in constant functions:
 
 ```
-#![feature(const_mut_refs)]
-
 const fn foo(x: usize) -> usize {
     let mut y = 1;
     let z = &mut y;
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 468ea81524a..0088a7bbc1e 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -143,10 +143,14 @@ declare_features! (
     (accepted, const_let, "1.33.0", Some(48821)),
     /// Allows the use of `loop` and `while` in constants.
     (accepted, const_loop, "1.46.0", Some(52000)),
+    /// Allows using `&mut` in constant functions.
+    (accepted, const_mut_refs, "CURRENT_RUSTC_VERSION", Some(57349)),
     /// Allows panicking during const eval (producing compile-time errors).
     (accepted, const_panic, "1.57.0", Some(51999)),
     /// Allows dereferencing raw pointers during const eval.
     (accepted, const_raw_ptr_deref, "1.58.0", Some(51911)),
+    /// Allows references to types with interior mutability within constants
+    (accepted, const_refs_to_cell, "CURRENT_RUSTC_VERSION", Some(80384)),
     /// Allows implementing `Copy` for closures where possible (RFC 2132).
     (accepted, copy_closures, "1.26.0", Some(44490)),
     /// Allows `crate` in paths.
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 05856dca491..d0c0460ddfe 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -403,12 +403,8 @@ declare_features! (
     (incomplete, const_closures, "1.68.0", Some(106003)),
     /// Allows `for _ in _` loops in const contexts.
     (unstable, const_for, "1.56.0", Some(87575)),
-    /// Allows using `&mut` in constant functions.
-    (unstable, const_mut_refs, "1.41.0", Some(57349)),
     /// Be more precise when looking for live drops in a const context.
     (unstable, const_precise_live_drops, "1.46.0", Some(73255)),
-    /// Allows references to types with interior mutability within constants
-    (unstable, const_refs_to_cell, "1.51.0", Some(80384)),
     /// Allows creating pointers and references to `static` items in constants.
     (unstable, const_refs_to_static, "1.78.0", Some(119618)),
     /// Allows `impl const Trait for T` syntax.
diff --git a/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs
index 042a9a14071..762084291a6 100644
--- a/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs
@@ -21,16 +21,16 @@ pub(crate) fn target() -> Target {
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
-            // The Cortex-R52 has two variants with respect to floating-point support:
-            // 1. fp-armv8, SP-only, with 16 DP (32 SP) registers
-            // 2. neon-fp-armv8, SP+DP, with 32 DP registers
-            // Use the lesser of these two options as the default, as it will produce code
-            // compatible with either variant.
+            // Armv8-R requires a minimum set of floating-point features equivalent to:
+            // fp-armv8, SP-only, with 16 DP (32 SP) registers
+            // LLVM defines Armv8-R to include these features automatically.
+            //
+            // The Cortex-R52 supports these default features and optionally includes:
+            // neon-fp-armv8, SP+DP, with 32 DP registers
             //
             // Reference:
             // Arm Cortex-R52 Processor Technical Reference Manual
             // - Chapter 15 Advanced SIMD and floating-point support
-            features: "+fp-armv8,-fp64,-d32".into(),
             max_atomic_width: Some(64),
             emit_debug_gdb_scripts: false,
             // GCC defaults to 8 for arm-none here.
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index c3fd771b797..b5ce465a173 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -175,7 +175,7 @@ pub(crate) mod rustc {
     use rustc_middle::ty::{self, AdtDef, AdtKind, List, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
     use rustc_span::ErrorGuaranteed;
     use rustc_target::abi::{
-        FieldIdx, FieldsShape, Layout, Size, TyAndLayout, VariantIdx, Variants,
+        FieldIdx, FieldsShape, Layout, Size, TagEncoding, TyAndLayout, VariantIdx, Variants,
     };
 
     use super::Tree;
@@ -319,11 +319,17 @@ pub(crate) mod rustc {
             assert!(def.is_enum());
 
             // Computes the variant of a given index.
-            let layout_of_variant = |index| {
+            let layout_of_variant = |index, encoding: Option<TagEncoding<VariantIdx>>| {
                 let tag = cx.tcx.tag_for_variant((cx.tcx.erase_regions(ty), index));
                 let variant_def = Def::Variant(def.variant(index));
                 let variant_layout = ty_variant(cx, (ty, layout), index);
-                Self::from_variant(variant_def, tag, (ty, variant_layout), layout.size, cx)
+                Self::from_variant(
+                    variant_def,
+                    tag.map(|tag| (tag, index, encoding.unwrap())),
+                    (ty, variant_layout),
+                    layout.size,
+                    cx,
+                )
             };
 
             // We consider three kinds of enums, each demanding a different
@@ -345,9 +351,9 @@ pub(crate) mod rustc {
                 Variants::Single { index } => {
                     // `Variants::Single` on enums with variants denotes that
                     // the enum delegates its layout to the variant at `index`.
-                    layout_of_variant(*index)
+                    layout_of_variant(*index, None)
                 }
-                Variants::Multiple { tag_field, .. } => {
+                Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
                     // `Variants::Multiple` denotes an enum with multiple
                     // variants. The layout of such an enum is the disjunction
                     // of the layouts of its tagged variants.
@@ -359,7 +365,7 @@ pub(crate) mod rustc {
                     let variants = def.discriminants(cx.tcx()).try_fold(
                         Self::uninhabited(),
                         |variants, (idx, ref discriminant)| {
-                            let variant = layout_of_variant(idx)?;
+                            let variant = layout_of_variant(idx, Some(tag_encoding.clone()))?;
                             Result::<Self, Err>::Ok(variants.or(variant))
                         },
                     )?;
@@ -380,7 +386,7 @@ pub(crate) mod rustc {
         /// `0`.
         fn from_variant(
             def: Def<'tcx>,
-            tag: Option<ScalarInt>,
+            tag: Option<(ScalarInt, VariantIdx, TagEncoding<VariantIdx>)>,
             (ty, layout): (Ty<'tcx>, Layout<'tcx>),
             total_size: Size,
             cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
@@ -400,9 +406,18 @@ pub(crate) mod rustc {
             let mut struct_tree = Self::def(def);
 
             // If a `tag` is provided, place it at the start of the layout.
-            if let Some(tag) = tag {
-                size += tag.size();
-                struct_tree = struct_tree.then(Self::from_tag(tag, cx.tcx));
+            if let Some((tag, index, encoding)) = &tag {
+                match encoding {
+                    TagEncoding::Direct => {
+                        size += tag.size();
+                    }
+                    TagEncoding::Niche { niche_variants, .. } => {
+                        if !niche_variants.contains(index) {
+                            size += tag.size();
+                        }
+                    }
+                }
+                struct_tree = struct_tree.then(Self::from_tag(*tag, cx.tcx));
             }
 
             // Append the fields, in memory order, to the layout.