-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tracking Issue for const_refs_to_cell
#80384
Comments
…e, r=RalfJung Allow references to interior mutable data behind a feature gate supercedes rust-lang#80373 by simply not checking for interior mutability on borrows of locals that have `StorageDead` and thus can never be leaked to the final value of the constant tracking issue: rust-lang#80384 r? `@RalfJung`
Does this need to apply to raw references/ #![feature(const_refs_to_cell)]
use core::cell::UnsafeCell;
use core::ptr;
fn main() {
const _: i32 = {
let x = UnsafeCell::new(5i32);
let p: *const UnsafeCell<i32> = ptr::addr_of!(x);
unsafe {
// No stable way to mutate `p`
// Requires const_mut_refs
// *(p as *mut i32) = 10
// Requires const_ptr_write
// (p as *mut i32).write(10)
// Requires const_intrinsic_copy, which just left FCP
// ptr::copy(&10, p as *mut i32, 1);
*(p as *const i32)
}
};
} |
I would be rather nervous about relying on roundabout arguments like this -- that's the kind of reasoning which quickly leads to accidental stabilization. For some motivation why we care about this feature, see Gilnaa/memoffset#4 (comment). |
Do you suspect This limitation would be much less frustrating if it didn't trigger when taking the address of any generic I'm trying to read a generic integer's bytes without mutation and am prevented from doing so on the impossible possibility that an interior mutable type is used. Fortunately it can be gotten around for now by passing in a reference to the |
Implementation of `memoffset::offset_of` (from the `memoffset` crate) ends up taking a reference to a struct. In `const` contexts (such as the context of the assertion) this runs into rust-lang/rust#80384. This CL works around this by opting the generated code into `#![feature(const_refs_to_cell)]`. After this CL bindings generated by `cc_bindings_from_rs` will require a "nightly" version of the Rust compiler. This is unfortunate, but this dependency already exists on `rs_bindings_from_cc` side (e.g. requiring `impl_trait_in_assoc_type` and/or `type_alias_impl_trait` unstable features). This CL unblocks implementing `Drop` support. `Drop` support adds bindings for additional types, some of which run into this bug. PiperOrigin-RevId: 546337289 Change-Id: I1684b30a1ac096cc5115aabbe6e5c6504286947c
Status update: const_mut_refs will hopefully to be stabilized soon. However, I think I'd prefer to wait a bit whether there are any issues showing up there before we also allow references to interior mutable types. In particular, the "safety net" described in #129195 doesn't work as well for shared references due to #121610 and rust-lang/unsafe-code-guidelines#493. Therefore, I'd like to increase our confidence in the static checks before stabilizing something without a solid safety net. See here for a Zulip discussion about what it would take to make sure that when we are allowing references to interior mutable data, that data is indeed "transient" and does not escape the initializer expression. |
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
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
This is a tracking issue for taking references to values of types that (may) contain
UnsafeCell
within const contexts.The feature gate for the issue is
#![feature(const_refs_to_cell)]
.About tracking issues
Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Steps
&mut
(and*mut
) as well as&Cell
(and*const Cell
) in const #129195Unresolved questions
const BAR: AtomicUsize = ..; static FOO: &AtomicUsize = &BAR;
and only preventsconst FOO: &AtomicUsize = &BAR;
. This decision was made because the implementation is simpler this way and we would already allow thestatic FOO
case ifBAR
were a static itself.static FOO
The soundness of this feature depends on the fact that if aStorageDead
exists for a local, then for every assignment to the local, on each pass from that assignment to anyreturn
, there will be aStorageDead
for this local.const fn foo() -> &'static Cell<i32>
, which could then be used to initialize aconst FOO: &'static Cell<i32>
which is unsound (and only caught by dynamic checks). The tracking issue for const heap is Tracking Issue forconst_heap
#79597Implementation history
The text was updated successfully, but these errors were encountered: