Auto merge of #90788 - ecstatic-morse:issue-90752, r=wesleywiser

Mark places as initialized when mutably borrowed

Fixes the example in #90752, but does not handle some corner cases involving raw pointers and unsafe. See [this comment](https://github.com/rust-lang/rust/issues/90752#issuecomment-965822895) for more information, or the second test.

Although I talked about both `MaybeUninitializedPlaces` and `MaybeInitializedPlaces` in #90752, this PR only changes the latter. That's because "maybe uninitialized" is the conservative choice, and marking them as definitely initialized (`!maybe_uninitialized`) when a mutable borrow is created could lead to problems if `addr_of_mut` to an uninitialized local is allowed. Additionally, places cannot become uninitialized via a mutable reference, so if a place is definitely initialized, taking a mutable reference to it should not change that.

I think it's correct to ignore interior mutability as nbdd0121 suggests below. Their analysis doesn't work inside of `core::cell`, which *does* have access to `UnsafeCell`'s field, but that won't be an issue unless we explicitly instantiate one with an `enum` within that module.

r? `@wesleywiser`
This commit is contained in:
bors 2021-11-23 17:44:33 +00:00
commit 7b3cd075bb
5 changed files with 163 additions and 6 deletions

View file

@ -0,0 +1,41 @@
// run-pass
use std::cell::RefCell;
struct S<'a>(i32, &'a RefCell<Vec<i32>>);
impl<'a> Drop for S<'a> {
fn drop(&mut self) {
self.1.borrow_mut().push(self.0);
}
}
fn test(drops: &RefCell<Vec<i32>>) {
let mut foo = None;
let pfoo: *mut _ = &mut foo;
match foo {
None => (),
_ => return,
}
// Both S(0) and S(1) should be dropped, but aren't.
unsafe { *pfoo = Some((S(0, drops), S(1, drops))); }
match foo {
Some((_x, _)) => {}
_ => {}
}
}
fn main() {
let drops = RefCell::new(Vec::new());
test(&drops);
// Ideally, we want this...
//assert_eq!(*drops.borrow(), &[0, 1]);
// But the delayed access through the raw pointer confuses drop elaboration,
// causing S(1) to be leaked.
assert_eq!(*drops.borrow(), &[0]);
}

View file

@ -0,0 +1,32 @@
// run-pass
use std::cell::RefCell;
struct S<'a>(i32, &'a RefCell<Vec<i32>>);
impl<'a> Drop for S<'a> {
fn drop(&mut self) {
self.1.borrow_mut().push(self.0);
}
}
fn test(drops: &RefCell<Vec<i32>>) {
let mut foo = None;
match foo {
None => (),
_ => return,
}
*(&mut foo) = Some((S(0, drops), S(1, drops))); // Both S(0) and S(1) should be dropped
match foo {
Some((_x, _)) => {}
_ => {}
}
}
fn main() {
let drops = RefCell::new(Vec::new());
test(&drops);
assert_eq!(*drops.borrow(), &[0, 1]);
}

View file

@ -0,0 +1,12 @@
// Ensure that taking a mutable raw ptr to an uninitialized variable does not change its
// initializedness.
struct S;
fn main() {
let mut x: S;
std::ptr::addr_of_mut!(x); //~ borrow of
let y = x; // Should error here if `addr_of_mut` is ever allowed on uninitialized variables
drop(y);
}

View file

@ -0,0 +1,11 @@
error[E0381]: borrow of possibly-uninitialized variable: `x`
--> $DIR/move-of-addr-of-mut.rs:8:5
|
LL | std::ptr::addr_of_mut!(x);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `x`
|
= note: this error originates in the macro `std::ptr::addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
For more information about this error, try `rustc --explain E0381`.