Skip to content

Commit

Permalink
stabilize const_mut_refs
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Aug 17, 2024
1 parent a2a238f commit 7ab0df0
Show file tree
Hide file tree
Showing 151 changed files with 259 additions and 1,338 deletions.
7 changes: 0 additions & 7 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,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
Expand Down Expand Up @@ -363,10 +360,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
Expand Down
111 changes: 6 additions & 105 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ 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};
Expand Down Expand Up @@ -163,24 +162,6 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
}
}

struct LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
kind: LocalKind,
checker: &'ck mut Checker<'mir, 'tcx>,
}

impl<'ck, 'mir, 'tcx> TypeVisitor<TyCtxt<'tcx>> for LocalReturnTyVisitor<'ck, '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>,
Expand Down Expand Up @@ -226,25 +207,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);
}
Expand Down Expand Up @@ -344,24 +306,16 @@ 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() {
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).
// 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 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
Expand All @@ -375,12 +329,11 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
// `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_has_storage_dead(place.local) {
self.check_op(ops::TransientMutBorrow(kind));
} else {
self.check_op(ops::MutBorrow(kind));
}
place.is_indirect() || self.local_has_storage_dead(place.local)
}
};
if !is_transient {
self.check_op(ops::EscapingMutBorrow(kind));
}
}
}
Expand Down Expand Up @@ -626,58 +579,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);
Expand Down
97 changes: 3 additions & 94 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -463,9 +463,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 struct MutBorrow(pub hir::BorrowKind);
pub 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
}
Expand All @@ -492,49 +492,6 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
}
}

#[derive(Debug)]
pub 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 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 struct PanicNonStr;
Expand All @@ -556,24 +513,6 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
}
}

#[derive(Debug)]
pub 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.
Expand Down Expand Up @@ -620,33 +559,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 mod mut_ref {
use super::*;

#[derive(Debug)]
pub 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()),
)
}
}
}
24 changes: 0 additions & 24 deletions compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,30 +93,6 @@ pub(crate) struct PanicNonStrErr {
pub span: Span,
}

#[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 {
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_error_codes/src/error_codes/E0764.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!
}
Expand All @@ -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;
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 @@ -139,6 +139,8 @@ 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.
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 @@ -404,8 +404,6 @@ declare_features! (
(unstable, const_fn_floating_point_arithmetic, "1.48.0", Some(57241)),
/// 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
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4657,7 +4657,6 @@ declare_lint! {
/// ### Example
///
/// ```rust,compile_fail
/// #![feature(const_mut_refs)]
/// const WRITE_AFTER_CAST: () = unsafe {
/// let mut x = 0;
/// let ptr = &x as *const i32 as *mut i32;
Expand Down
Loading

0 comments on commit 7ab0df0

Please sign in to comment.