diff options
| author | Ralf Jung <post@ralfj.de> | 2024-08-26 14:51:16 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2024-09-15 10:20:47 +0200 |
| commit | 49316f871cf8731c3bdf921932659a69199034b4 (patch) | |
| tree | 946820be88e07daa1fb177d73cc8bf69481bd1f2 /compiler/rustc_const_eval | |
| parent | 544a6a7df382fbe6ae13c05f4b2cfa251e448050 (diff) | |
also stabilize const_refs_to_cell
Diffstat (limited to 'compiler/rustc_const_eval')
| -rw-r--r-- | compiler/rustc_const_eval/messages.ftl | 3 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/check.rs | 79 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/ops.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 7 |
4 files changed, 21 insertions, 87 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 381569b37a2..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 diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index da51ecb359f..6d09ed5b4bf 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -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 @@ -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 } } @@ -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 + })); } } @@ -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); } } diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index bd492244d95..a52fc6a077b 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -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. diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 4e2b727cf9f..b66b5c0a00a 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -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] |
