diff options
Diffstat (limited to 'compiler/rustc_const_eval')
| -rw-r--r-- | compiler/rustc_const_eval/messages.ftl | 10 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/check.rs | 254 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/ops.rs | 116 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 31 |
4 files changed, 30 insertions, 381 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] |
