From 544a6a7df382fbe6ae13c05f4b2cfa251e448050 Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Wed, 21 Aug 2024 22:30:16 +0200
Subject: [PATCH] const_refs_to_cell: dont let mutable references sneak past
 the interior mutability check

---
 .../src/check_consts/check.rs                 | 13 +++++++-
 library/core/src/slice/mod.rs                 |  2 +-
 .../feature-gate-const_refs_to_cell.rs        | 20 ++++++-----
 .../feature-gate-const_refs_to_cell.stderr    | 33 +++++++++++++++++++
 tests/ui/statics/mutable_memory_validation.rs |  2 +-
 5 files changed, 59 insertions(+), 11 deletions(-)
 create mode 100644 tests/ui/feature-gates/feature-gate-const_refs_to_cell.stderr

diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 88c8c6475e8..da51ecb359f 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -433,7 +433,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                 // `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() {
+                // However, we only want to consider places that are obtained by dereferencing
+                // a *shared* reference. Mutable references to interior mutable data are stable,
+                // and we don't want `&*&mut interior_mut` to be accepted.
+                let is_indirect = place.iter_projections().any(|(base, proj)| {
+                    matches!(proj, ProjectionElem::Deref)
+                        && matches!(
+                            base.ty(self.body, self.tcx).ty.kind(),
+                            ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not)
+                        )
+                });
+
+                if borrowed_place_has_mut_interior && !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
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 8b661d1202c..c74a7666919 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -846,7 +846,7 @@ impl<T> [T] {
     /// [`as_mut_ptr`]: slice::as_mut_ptr
     #[stable(feature = "slice_ptr_range", since = "1.48.0")]
     #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
-    #[rustc_allow_const_fn_unstable(const_mut_refs)]
+    #[rustc_allow_const_fn_unstable(const_mut_refs, const_refs_to_cell)]
     #[inline]
     #[must_use]
     pub const fn as_mut_ptr_range(&mut self) -> Range<*mut T> {
diff --git a/tests/ui/feature-gates/feature-gate-const_refs_to_cell.rs b/tests/ui/feature-gates/feature-gate-const_refs_to_cell.rs
index bd908676c8b..d8628cb41e5 100644
--- a/tests/ui/feature-gates/feature-gate-const_refs_to_cell.rs
+++ b/tests/ui/feature-gates/feature-gate-const_refs_to_cell.rs
@@ -1,12 +1,16 @@
-//@ check-pass
-
-#![feature(const_refs_to_cell)]
-
 const FOO: () = {
     let x = std::cell::Cell::new(42);
-    let y = &x;
+    let y = &x; //~ERROR: cannot borrow here
 };
 
-fn main() {
-    FOO;
-}
+const FOO2: () = {
+    let mut x = std::cell::Cell::new(42);
+    let y = &*&mut x; //~ERROR: cannot borrow here
+};
+
+const FOO3: () = unsafe {
+    let mut x = std::cell::Cell::new(42);
+    let y = &*(&mut x as *mut _); //~ERROR: cannot borrow here
+};
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-const_refs_to_cell.stderr b/tests/ui/feature-gates/feature-gate-const_refs_to_cell.stderr
new file mode 100644
index 00000000000..553140750b7
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-const_refs_to_cell.stderr
@@ -0,0 +1,33 @@
+error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
+  --> $DIR/feature-gate-const_refs_to_cell.rs:3:13
+   |
+LL |     let y = &x;
+   |             ^^
+   |
+   = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
+   = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
+  --> $DIR/feature-gate-const_refs_to_cell.rs:8:13
+   |
+LL |     let y = &*&mut x;
+   |             ^^^^^^^^
+   |
+   = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
+   = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
+  --> $DIR/feature-gate-const_refs_to_cell.rs:13:13
+   |
+LL |     let y = &*(&mut x as *mut _);
+   |             ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
+   = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/statics/mutable_memory_validation.rs b/tests/ui/statics/mutable_memory_validation.rs
index d16b787fef8..0dc42c37bed 100644
--- a/tests/ui/statics/mutable_memory_validation.rs
+++ b/tests/ui/statics/mutable_memory_validation.rs
@@ -4,7 +4,7 @@
 //@ normalize-stderr-test: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
 //@ normalize-stderr-test: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> "HEX_DUMP"
 
-#![feature(const_refs_to_static)]
+#![feature(const_refs_to_static, const_refs_to_cell)]
 
 use std::cell::UnsafeCell;