Skip to content

Commit

Permalink
also stabilize const_refs_to_cell
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Sep 15, 2024
1 parent 544a6a7 commit 49316f8
Show file tree
Hide file tree
Showing 38 changed files with 130 additions and 324 deletions.
3 changes: 0 additions & 3 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
79 changes: 19 additions & 60 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,10 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
self.check_op_spanned(ops::StaticAccess, span)
}

fn check_mut_borrow(&mut self, place: &Place<'_>, kind: hir::BorrowKind) {
/// 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
Expand All @@ -341,14 +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 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)
}
};
if !is_transient {
self.check_op(ops::EscapingMutBorrow(kind));
}
// Transient places cannot possibly escape because the place doesn't exist any more at the
// end of evaluation.
!is_transient
}
}

Expand Down Expand Up @@ -406,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
}));
}
}

Expand All @@ -426,51 +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.
// 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
// 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);
}
}

Expand Down
19 changes: 2 additions & 17 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,27 +391,12 @@ 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.
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,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]
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ declare_features! (
(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.
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,6 @@ declare_features! (
(unstable, const_for, "1.56.0", Some(87575)),
/// 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.
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@
#![feature(const_maybe_uninit_write)]
#![feature(const_option)]
#![feature(const_pin)]
#![feature(const_refs_to_cell)]
#![feature(const_size_of_val)]
#![feature(core_intrinsics)]
#![feature(deprecated_suggestion)]
Expand Down Expand Up @@ -165,6 +164,7 @@
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(const_mut_refs))]
#![cfg_attr(bootstrap, feature(const_refs_to_cell))]
#![cfg_attr(not(test), feature(coroutine_trait))]
#![cfg_attr(test, feature(panic_update_hook))]
#![cfg_attr(test, feature(test))]
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(const_mut_refs))]
#![cfg_attr(bootstrap, feature(const_refs_to_cell))]
#![feature(abi_unadjusted)]
#![feature(adt_const_params)]
#![feature(allow_internal_unsafe)]
Expand All @@ -203,7 +204,6 @@
#![feature(cfg_ub_checks)]
#![feature(const_for)]
#![feature(const_precise_live_drops)]
#![feature(const_refs_to_cell)]
#![feature(decl_macro)]
#![feature(deprecated_suggestion)]
#![feature(doc_cfg)]
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, const_refs_to_cell)]
#[cfg_attr(bootstrap, 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> {
Expand Down
9 changes: 5 additions & 4 deletions tests/ui/consts/const-address-of-interior-mut.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//@check-pass
use std::cell::Cell;

const A: () = { let x = Cell::new(2); &raw const x; }; //~ ERROR interior mutability
const A: () = { let x = Cell::new(2); &raw const x; };

static B: () = { let x = Cell::new(2); &raw const x; }; //~ ERROR interior mutability
static B: () = { let x = Cell::new(2); &raw const x; };

static mut C: () = { let x = Cell::new(2); &raw const x; }; //~ ERROR interior mutability
static mut C: () = { let x = Cell::new(2); &raw const x; };

const fn foo() {
let x = Cell::new(0);
let y = &raw const x; //~ ERROR interior mutability
let y = &raw const x;
}

fn main() {}
43 changes: 0 additions & 43 deletions tests/ui/consts/const-address-of-interior-mut.stderr

This file was deleted.

1 change: 0 additions & 1 deletion tests/ui/consts/const-eval/ub-write-through-immutable.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Ensure we catch UB due to writing through a shared reference.
#![feature(const_refs_to_cell)]
#![allow(invalid_reference_casting)]

use std::mem;
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/consts/const-eval/ub-write-through-immutable.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error[E0080]: evaluation of constant value failed
--> $DIR/ub-write-through-immutable.rs:11:5
--> $DIR/ub-write-through-immutable.rs:10:5
|
LL | *ptr = 0;
| ^^^^^^^^ writing through a pointer that was derived from a shared (immutable) reference

error[E0080]: evaluation of constant value failed
--> $DIR/ub-write-through-immutable.rs:18:5
--> $DIR/ub-write-through-immutable.rs:17:5
|
LL | *ptr = 0;
| ^^^^^^^^ writing through a pointer that was derived from a shared (immutable) reference
Expand Down
20 changes: 5 additions & 15 deletions tests/ui/consts/const-promoted-opaque.atomic.stderr
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
--> $DIR/const-promoted-opaque.rs:28:25
|
LL | let _: &'static _ = &FOO;
| ^^^^
|
= 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[E0493]: destructor of `helper::Foo` cannot be evaluated at compile-time
--> $DIR/const-promoted-opaque.rs:28:26
|
LL | let _: &'static _ = &FOO;
| ^^^ the destructor for this type cannot be evaluated in constants
...
LL |
LL | };
| - value is dropped here

error[E0492]: constants cannot refer to interior mutable data
--> $DIR/const-promoted-opaque.rs:33:19
--> $DIR/const-promoted-opaque.rs:32:19
|
LL | const BAZ: &Foo = &FOO;
| ^^^^ this borrow of an interior mutable value may end up in the final value

error[E0716]: temporary value dropped while borrowed
--> $DIR/const-promoted-opaque.rs:37:26
--> $DIR/const-promoted-opaque.rs:36:26
|
LL | let _: &'static _ = &FOO;
| ---------- ^^^ creates a temporary value which is freed while still in use
Expand All @@ -34,7 +24,7 @@ LL |
LL | }
| - temporary value is freed at the end of this statement

error: aborting due to 4 previous errors
error: aborting due to 3 previous errors

Some errors have detailed explanations: E0492, E0493, E0658, E0716.
Some errors have detailed explanations: E0492, E0493, E0716.
For more information about an error, try `rustc --explain E0492`.
1 change: 0 additions & 1 deletion tests/ui/consts/const-promoted-opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use helper::*;
const BAR: () = {
let _: &'static _ = &FOO;
//[string,atomic]~^ ERROR: destructor of `helper::Foo` cannot be evaluated at compile-time
//[atomic]~| ERROR: cannot borrow here
};

const BAZ: &Foo = &FOO;
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/consts/const-promoted-opaque.string.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ error[E0493]: destructor of `helper::Foo` cannot be evaluated at compile-time
|
LL | let _: &'static _ = &FOO;
| ^^^ the destructor for this type cannot be evaluated in constants
...
LL |
LL | };
| - value is dropped here

error[E0716]: temporary value dropped while borrowed
--> $DIR/const-promoted-opaque.rs:37:26
--> $DIR/const-promoted-opaque.rs:36:26
|
LL | let _: &'static _ = &FOO;
| ---------- ^^^ creates a temporary value which is freed while still in use
Expand Down
8 changes: 4 additions & 4 deletions tests/ui/consts/const-suggest-feature.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//@compile-flags: --edition 2018
use std::cell::Cell;

const WRITE: () = unsafe {
let x = Cell::new(0);
let y = &x;
//~^ ERROR interior mutability
//~| HELP add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
let x = async { 13 };
//~^ ERROR `async` blocks
//~| HELP add `#![feature(const_async_blocks)]` to the crate attributes to enable
};

fn main() {}
Loading

0 comments on commit 49316f8

Please sign in to comment.