Skip to content

Commit 011289c

Browse files
authored
Rollup merge of rust-lang#129195 - RalfJung:const-mut-refs, r=fee1-dead
Stabilize `&mut` (and `*mut`) as well as `&Cell` (and `*const Cell`) in const This stabilizes `const_mut_refs` and `const_refs_to_cell`. That allows a bunch of new things in const contexts: - Mentioning `&mut` types - Creating `&mut` and `*mut` values - Creating `&T` and `*const T` values where `T` contains interior mutability - Dereferencing `&mut` and `*mut` values (both for reads and writes) The same rules as at runtime apply: mutating immutable data is UB. This includes mutation through pointers derived from shared references; the following is diagnosed with a hard error: ```rust #[allow(invalid_reference_casting)] const _: () = { let mut val = 15; let ptr = &val as *const i32 as *mut i32; unsafe { *ptr = 16; } }; ``` The main limitation that is enforced is that the final value of a const (or non-`mut` static) may not contain `&mut` values nor interior mutable `&` values. This is necessary because the memory those references point to becomes *read-only* when the constant is done computing, so (interior) mutable references to such memory would be pretty dangerous. We take a multi-layered approach here to ensuring no mutable references escape the initializer expression: - A static analysis rejects (interior) mutable references when the referee looks like it may outlive the current MIR body. - To be extra sure, this static check is complemented by a "safety net" of dynamic checks. ("Dynamic" in the sense of "running during/after const-evaluation, e.g. at runtime of this code" -- in contrast to "static" which works entirely by looking at the MIR without evaluating it.) - After the final value is computed, we do a type-driven traversal of the entire value, and if we find any `&mut` or interior-mutable `&` we error out. - However, the type-driven traversal cannot traverse `union` or raw pointers, so there is a second dynamic check where if the final value of the const contains any pointer that was not derived from a shared reference, we complain. This is currently a future-compat lint, but will become an ICE in rust-lang#128543. On the off-chance that it's actually possible to trigger this lint on stable, I'd prefer if we could make it an ICE before stabilizing const_mut_refs, but it's not a hard blocker. This part of the "safety net" is only active for mutable references since with shared references, it has false positives. Altogether this should prevent people from leaking (interior) mutable references out of the const initializer. While updating the tests I learned that surprisingly, this code gets rejected: ```rust const _: Vec<i32> = { let mut x = Vec::<i32>::new(); //~ ERROR destructor of `Vec<i32>` cannot be evaluated at compile-time let r = &mut x; let y = x; y }; ``` The analysis that rejects destructors in `const` is very conservative when it sees an `&mut` being created to `x`, and then considers `x` to be always live. See [here](rust-lang#65394 (comment)) for a longer explanation. `const_precise_live_drops` will solve this, so I consider this problem to be tracked by rust-lang#73255. Cc `@rust-lang/wg-const-eval` `@rust-lang/lang` Cc rust-lang#57349 Cc rust-lang#80384
2 parents bc486f3 + 49316f8 commit 011289c

File tree

175 files changed

+382
-1693
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

175 files changed

+382
-1693
lines changed

compiler/rustc_const_eval/messages.ftl

-10
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,6 @@ const_eval_incompatible_return_types =
134134
const_eval_incompatible_types =
135135
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
136136
137-
const_eval_interior_mutability_borrow =
138-
cannot borrow here, since the borrowed element may contain interior mutability
139-
140137
const_eval_interior_mutable_data_refer =
141138
{const_eval_const_context}s cannot refer to interior mutable data
142139
.label = this borrow of an interior mutable value may end up in the final value
@@ -230,9 +227,6 @@ const_eval_memory_exhausted =
230227
const_eval_modified_global =
231228
modifying a static's initial value from another static's initializer
232229
233-
const_eval_mut_deref =
234-
mutation through a reference is not allowed in {const_eval_const_context}s
235-
236230
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
237231
238232
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 =
363357
const_eval_too_many_caller_args =
364358
calling a function with more arguments than it expected
365359
366-
const_eval_transient_mut_borrow = mutable references are not allowed in {const_eval_const_context}s
367-
368-
const_eval_transient_mut_raw = raw mutable pointers are not allowed in {const_eval_const_context}s
369-
370360
const_eval_try_block_from_output_non_const =
371361
`try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
372362
const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s

compiler/rustc_const_eval/src/check_consts/check.rs

+25-229
Large diffs are not rendered by default.

compiler/rustc_const_eval/src/check_consts/ops.rs

+5-111
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_hir as hir;
88
use rustc_hir::def_id::DefId;
99
use rustc_infer::infer::TyCtxtInferExt;
1010
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
11-
use rustc_middle::mir::{self, CallSource};
11+
use rustc_middle::mir::CallSource;
1212
use rustc_middle::span_bug;
1313
use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _};
1414
use rustc_middle::ty::{
@@ -391,27 +391,12 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
391391
}
392392
}
393393

394-
#[derive(Debug)]
395-
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
396-
/// the final value of the constant.
397-
pub(crate) struct TransientCellBorrow;
398-
impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
399-
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
400-
Status::Unstable(sym::const_refs_to_cell)
401-
}
402-
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
403-
ccx.tcx
404-
.sess
405-
.create_feature_err(errors::InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
406-
}
407-
}
408-
409394
#[derive(Debug)]
410395
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
411396
/// the final value of the constant, and thus we cannot allow this (for now). We may allow
412397
/// it in the future for static items.
413-
pub(crate) struct CellBorrow;
414-
impl<'tcx> NonConstOp<'tcx> for CellBorrow {
398+
pub(crate) struct EscapingCellBorrow;
399+
impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
415400
fn importance(&self) -> DiagImportance {
416401
// Most likely the code will try to do mutation with these borrows, which
417402
// triggers its own errors. Only show this one if that does not happen.
@@ -431,9 +416,9 @@ impl<'tcx> NonConstOp<'tcx> for CellBorrow {
431416
/// This op is for `&mut` borrows in the trailing expression of a constant
432417
/// which uses the "enclosing scopes rule" to leak its locals into anonymous
433418
/// static or const items.
434-
pub(crate) struct MutBorrow(pub hir::BorrowKind);
419+
pub(crate) struct EscapingMutBorrow(pub hir::BorrowKind);
435420

436-
impl<'tcx> NonConstOp<'tcx> for MutBorrow {
421+
impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
437422
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
438423
Status::Forbidden
439424
}
@@ -460,49 +445,6 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
460445
}
461446
}
462447

463-
#[derive(Debug)]
464-
pub(crate) struct TransientMutBorrow(pub hir::BorrowKind);
465-
466-
impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
467-
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
468-
Status::Unstable(sym::const_mut_refs)
469-
}
470-
471-
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
472-
let kind = ccx.const_kind();
473-
match self.0 {
474-
hir::BorrowKind::Raw => ccx
475-
.tcx
476-
.sess
477-
.create_feature_err(errors::TransientMutRawErr { span, kind }, sym::const_mut_refs),
478-
hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err(
479-
errors::TransientMutBorrowErr { span, kind },
480-
sym::const_mut_refs,
481-
),
482-
}
483-
}
484-
}
485-
486-
#[derive(Debug)]
487-
pub(crate) struct MutDeref;
488-
impl<'tcx> NonConstOp<'tcx> for MutDeref {
489-
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
490-
Status::Unstable(sym::const_mut_refs)
491-
}
492-
493-
fn importance(&self) -> DiagImportance {
494-
// Usually a side-effect of a `TransientMutBorrow` somewhere.
495-
DiagImportance::Secondary
496-
}
497-
498-
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
499-
ccx.tcx.sess.create_feature_err(
500-
errors::MutDerefErr { span, kind: ccx.const_kind() },
501-
sym::const_mut_refs,
502-
)
503-
}
504-
}
505-
506448
/// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
507449
#[derive(Debug)]
508450
pub(crate) struct PanicNonStr;
@@ -524,24 +466,6 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
524466
}
525467
}
526468

527-
#[derive(Debug)]
528-
pub(crate) struct RawMutPtrDeref;
529-
impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
530-
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
531-
Status::Unstable(sym::const_mut_refs)
532-
}
533-
534-
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
535-
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
536-
feature_err(
537-
&ccx.tcx.sess,
538-
sym::const_mut_refs,
539-
span,
540-
format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
541-
)
542-
}
543-
}
544-
545469
/// Casting raw pointer or function pointer to an integer.
546470
/// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
547471
/// allocation base addresses that are not known at compile-time.
@@ -588,33 +512,3 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
588512
ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
589513
}
590514
}
591-
592-
/// Types that cannot appear in the signature or locals of a `const fn`.
593-
pub(crate) mod mut_ref {
594-
use super::*;
595-
596-
#[derive(Debug)]
597-
pub(crate) struct MutRef(pub mir::LocalKind);
598-
impl<'tcx> NonConstOp<'tcx> for MutRef {
599-
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
600-
Status::Unstable(sym::const_mut_refs)
601-
}
602-
603-
fn importance(&self) -> DiagImportance {
604-
match self.0 {
605-
mir::LocalKind::Temp => DiagImportance::Secondary,
606-
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => DiagImportance::Primary,
607-
}
608-
}
609-
610-
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
611-
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
612-
feature_err(
613-
&ccx.tcx.sess,
614-
sym::const_mut_refs,
615-
span,
616-
format!("mutable references are not allowed in {}s", ccx.const_kind()),
617-
)
618-
}
619-
}
620-
}

compiler/rustc_const_eval/src/errors.rs

-31
Original file line numberDiff line numberDiff line change
@@ -93,30 +93,6 @@ pub(crate) struct PanicNonStrErr {
9393
pub span: Span,
9494
}
9595

96-
#[derive(Diagnostic)]
97-
#[diag(const_eval_mut_deref, code = E0658)]
98-
pub(crate) struct MutDerefErr {
99-
#[primary_span]
100-
pub span: Span,
101-
pub kind: ConstContext,
102-
}
103-
104-
#[derive(Diagnostic)]
105-
#[diag(const_eval_transient_mut_borrow, code = E0658)]
106-
pub(crate) struct TransientMutBorrowErr {
107-
#[primary_span]
108-
pub span: Span,
109-
pub kind: ConstContext,
110-
}
111-
112-
#[derive(Diagnostic)]
113-
#[diag(const_eval_transient_mut_raw, code = E0658)]
114-
pub(crate) struct TransientMutRawErr {
115-
#[primary_span]
116-
pub span: Span,
117-
pub kind: ConstContext,
118-
}
119-
12096
#[derive(Diagnostic)]
12197
#[diag(const_eval_max_num_nodes_in_const)]
12298
pub(crate) struct MaxNumNodesInConstErr {
@@ -217,13 +193,6 @@ pub(crate) struct InteriorMutableDataRefer {
217193
pub teach: bool,
218194
}
219195

220-
#[derive(Diagnostic)]
221-
#[diag(const_eval_interior_mutability_borrow)]
222-
pub(crate) struct InteriorMutabilityBorrow {
223-
#[primary_span]
224-
pub span: Span,
225-
}
226-
227196
#[derive(LintDiagnostic)]
228197
#[diag(const_eval_long_running)]
229198
#[note]

compiler/rustc_error_codes/src/error_codes/E0764.md

-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ A mutable reference was used in a constant.
33
Erroneous code example:
44

55
```compile_fail,E0764
6-
#![feature(const_mut_refs)]
7-
86
fn main() {
97
const OH_NO: &'static mut usize = &mut 1; // error!
108
}
@@ -26,8 +24,6 @@ Remember: you cannot use a function call inside a constant or static. However,
2624
you can totally use it in constant functions:
2725

2826
```
29-
#![feature(const_mut_refs)]
30-
3127
const fn foo(x: usize) -> usize {
3228
let mut y = 1;
3329
let z = &mut y;

compiler/rustc_feature/src/accepted.rs

+4
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,14 @@ declare_features! (
143143
(accepted, const_let, "1.33.0", Some(48821)),
144144
/// Allows the use of `loop` and `while` in constants.
145145
(accepted, const_loop, "1.46.0", Some(52000)),
146+
/// Allows using `&mut` in constant functions.
147+
(accepted, const_mut_refs, "CURRENT_RUSTC_VERSION", Some(57349)),
146148
/// Allows panicking during const eval (producing compile-time errors).
147149
(accepted, const_panic, "1.57.0", Some(51999)),
148150
/// Allows dereferencing raw pointers during const eval.
149151
(accepted, const_raw_ptr_deref, "1.58.0", Some(51911)),
152+
/// Allows references to types with interior mutability within constants
153+
(accepted, const_refs_to_cell, "CURRENT_RUSTC_VERSION", Some(80384)),
150154
/// Allows implementing `Copy` for closures where possible (RFC 2132).
151155
(accepted, copy_closures, "1.26.0", Some(44490)),
152156
/// Allows `crate` in paths.

compiler/rustc_feature/src/unstable.rs

-4
Original file line numberDiff line numberDiff line change
@@ -403,12 +403,8 @@ declare_features! (
403403
(incomplete, const_closures, "1.68.0", Some(106003)),
404404
/// Allows `for _ in _` loops in const contexts.
405405
(unstable, const_for, "1.56.0", Some(87575)),
406-
/// Allows using `&mut` in constant functions.
407-
(unstable, const_mut_refs, "1.41.0", Some(57349)),
408406
/// Be more precise when looking for live drops in a const context.
409407
(unstable, const_precise_live_drops, "1.46.0", Some(73255)),
410-
/// Allows references to types with interior mutability within constants
411-
(unstable, const_refs_to_cell, "1.51.0", Some(80384)),
412408
/// Allows creating pointers and references to `static` items in constants.
413409
(unstable, const_refs_to_static, "1.78.0", Some(119618)),
414410
/// Allows `impl const Trait for T` syntax.

library/alloc/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@
114114
#![feature(const_maybe_uninit_write)]
115115
#![feature(const_option)]
116116
#![feature(const_pin)]
117-
#![feature(const_refs_to_cell)]
118117
#![feature(const_size_of_val)]
119118
#![feature(core_intrinsics)]
120119
#![feature(deprecated_suggestion)]
@@ -164,13 +163,14 @@
164163
//
165164
// Language features:
166165
// tidy-alphabetical-start
166+
#![cfg_attr(bootstrap, feature(const_mut_refs))]
167+
#![cfg_attr(bootstrap, feature(const_refs_to_cell))]
167168
#![cfg_attr(not(test), feature(coroutine_trait))]
168169
#![cfg_attr(test, feature(panic_update_hook))]
169170
#![cfg_attr(test, feature(test))]
170171
#![feature(allocator_internals)]
171172
#![feature(allow_internal_unstable)]
172173
#![feature(cfg_sanitize)]
173-
#![feature(const_mut_refs)]
174174
#![feature(const_precise_live_drops)]
175175
#![feature(const_ptr_write)]
176176
#![feature(const_try)]

library/alloc/tests/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#![feature(cow_is_borrowed)]
77
#![feature(const_cow_is_borrowed)]
88
#![feature(const_heap)]
9-
#![feature(const_mut_refs)]
9+
#![cfg_attr(bootstrap, feature(const_mut_refs))]
1010
#![feature(const_slice_from_raw_parts_mut)]
1111
#![feature(const_ptr_write)]
1212
#![feature(const_try)]

library/core/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@
191191
//
192192
// Language features:
193193
// tidy-alphabetical-start
194+
#![cfg_attr(bootstrap, feature(const_mut_refs))]
195+
#![cfg_attr(bootstrap, feature(const_refs_to_cell))]
194196
#![feature(abi_unadjusted)]
195197
#![feature(adt_const_params)]
196198
#![feature(allow_internal_unsafe)]
@@ -201,9 +203,7 @@
201203
#![feature(cfg_target_has_atomic_equal_alignment)]
202204
#![feature(cfg_ub_checks)]
203205
#![feature(const_for)]
204-
#![feature(const_mut_refs)]
205206
#![feature(const_precise_live_drops)]
206-
#![feature(const_refs_to_cell)]
207207
#![feature(decl_macro)]
208208
#![feature(deprecated_suggestion)]
209209
#![feature(doc_cfg)]

library/core/src/ptr/mut_ptr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1569,7 +1569,7 @@ impl<T: ?Sized> *mut T {
15691569
///
15701570
/// ```
15711571
/// #![feature(const_pointer_is_aligned)]
1572-
/// #![feature(const_mut_refs)]
1572+
/// # #![cfg_attr(bootstrap, feature(const_mut_refs))]
15731573
///
15741574
/// // On some platforms, the alignment of primitives is less than their size.
15751575
/// #[repr(align(4))]
@@ -1695,7 +1695,7 @@ impl<T: ?Sized> *mut T {
16951695
/// ```
16961696
/// #![feature(pointer_is_aligned_to)]
16971697
/// #![feature(const_pointer_is_aligned)]
1698-
/// #![feature(const_mut_refs)]
1698+
/// # #![cfg_attr(bootstrap, feature(const_mut_refs))]
16991699
///
17001700
/// // On some platforms, the alignment of i32 is less than 4.
17011701
/// #[repr(align(4))]

library/core/src/slice/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ impl<T> [T] {
846846
/// [`as_mut_ptr`]: slice::as_mut_ptr
847847
#[stable(feature = "slice_ptr_range", since = "1.48.0")]
848848
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
849-
#[rustc_allow_const_fn_unstable(const_mut_refs)]
849+
#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs, const_refs_to_cell))]
850850
#[inline]
851851
#[must_use]
852852
pub const fn as_mut_ptr_range(&mut self) -> Range<*mut T> {

library/core/tests/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// tidy-alphabetical-start
2+
#![cfg_attr(bootstrap, feature(const_mut_refs))]
23
#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
34
#![cfg_attr(test, feature(cfg_match))]
45
#![feature(alloc_layout_extra)]
@@ -26,7 +27,6 @@
2627
#![feature(const_ipv6)]
2728
#![feature(const_likely)]
2829
#![feature(const_maybe_uninit_as_mut_ptr)]
29-
#![feature(const_mut_refs)]
3030
#![feature(const_nonnull_new)]
3131
#![feature(const_option)]
3232
#![feature(const_option_ext)]

library/std/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@
272272
//
273273
// Language features:
274274
// tidy-alphabetical-start
275+
#![cfg_attr(bootstrap, feature(const_mut_refs))]
275276
#![feature(alloc_error_handler)]
276277
#![feature(allocator_internals)]
277278
#![feature(allow_internal_unsafe)]
@@ -281,7 +282,6 @@
281282
#![feature(cfg_target_thread_local)]
282283
#![feature(cfi_encoding)]
283284
#![feature(concat_idents)]
284-
#![feature(const_mut_refs)]
285285
#![feature(decl_macro)]
286286
#![feature(deprecated_suggestion)]
287287
#![feature(doc_cfg)]

0 commit comments

Comments
 (0)