You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
136
136
137
-
const_eval_interior_mutability_borrow =
138
-
cannot borrow here, since the borrowed element may contain interior mutability
139
-
140
137
const_eval_interior_mutable_data_refer =
141
138
{const_eval_const_context}s cannot refer to interior mutable data
142
139
.label = this borrow of an interior mutable value may end up in the final value
@@ -230,9 +227,6 @@ const_eval_memory_exhausted =
230
227
const_eval_modified_global =
231
228
modifying a static's initial value from another static's initializer
232
229
233
-
const_eval_mut_deref =
234
-
mutation through a reference is not allowed in {const_eval_const_context}s
235
-
236
230
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
237
231
238
232
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 =
363
357
const_eval_too_many_caller_args =
364
358
calling a function with more arguments than it expected
365
359
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
-
370
360
const_eval_try_block_from_output_non_const =
371
361
`try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
372
362
const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s
0 commit comments