mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
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:
commit
7b3cd075bb
@ -4,17 +4,18 @@
|
||||
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::Idx;
|
||||
use rustc_middle::mir::visit::{MirVisitable, Visitor};
|
||||
use rustc_middle::mir::{self, Body, Location};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
|
||||
use crate::drop_flag_effects;
|
||||
use crate::drop_flag_effects_for_function_entry;
|
||||
use crate::drop_flag_effects_for_location;
|
||||
use crate::elaborate_drops::DropFlagState;
|
||||
use crate::framework::SwitchIntEdgeEffects;
|
||||
use crate::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex};
|
||||
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
|
||||
use crate::on_lookup_result_bits;
|
||||
use crate::MoveDataParamEnv;
|
||||
use crate::{drop_flag_effects, on_all_children_bits};
|
||||
use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
|
||||
|
||||
mod borrowed_locals;
|
||||
@ -307,22 +308,45 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
||||
fn statement_effect(
|
||||
&self,
|
||||
trans: &mut impl GenKill<Self::Idx>,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
statement: &mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
||||
Self::update_bits(trans, path, s)
|
||||
});
|
||||
|
||||
if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark all places as "maybe init" if they are mutably borrowed. See #90752.
|
||||
for_each_mut_borrow(statement, location, |place| {
|
||||
let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) else { return };
|
||||
on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| {
|
||||
trans.gen(child);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn terminator_effect(
|
||||
&self,
|
||||
trans: &mut impl GenKill<Self::Idx>,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
terminator: &mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
||||
Self::update_bits(trans, path, s)
|
||||
});
|
||||
|
||||
if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration {
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_mut_borrow(terminator, location, |place| {
|
||||
let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) else { return };
|
||||
on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| {
|
||||
trans.gen(child);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -427,7 +451,10 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
||||
) {
|
||||
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
||||
Self::update_bits(trans, path, s)
|
||||
})
|
||||
});
|
||||
|
||||
// Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a
|
||||
// mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
|
||||
}
|
||||
|
||||
fn terminator_effect(
|
||||
@ -438,7 +465,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
||||
) {
|
||||
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
||||
Self::update_bits(trans, path, s)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn call_return_effect(
|
||||
@ -704,3 +731,37 @@ fn switch_on_enum_discriminant(
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
struct OnMutBorrow<F>(F);
|
||||
|
||||
impl<F> Visitor<'_> for OnMutBorrow<F>
|
||||
where
|
||||
F: FnMut(&mir::Place<'_>),
|
||||
{
|
||||
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'_>, location: Location) {
|
||||
// FIXME: Does `&raw const foo` allow mutation? See #90413.
|
||||
match rvalue {
|
||||
mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place)
|
||||
| mir::Rvalue::AddressOf(_, place) => (self.0)(place),
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.super_rvalue(rvalue, location)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls `f` for each mutable borrow or raw reference in the program.
|
||||
///
|
||||
/// This DOES NOT call `f` for a shared borrow of a type with interior mutability. That's okay for
|
||||
/// initializedness, because we cannot move from an `UnsafeCell` (outside of `core::cell`), but
|
||||
/// other analyses will likely need to check for `!Freeze`.
|
||||
fn for_each_mut_borrow<'tcx>(
|
||||
mir: &impl MirVisitable<'tcx>,
|
||||
location: Location,
|
||||
f: impl FnMut(&mir::Place<'_>),
|
||||
) {
|
||||
let mut vis = OnMutBorrow(f);
|
||||
|
||||
mir.apply(location, &mut vis);
|
||||
}
|
||||
|
41
src/test/ui/drop/issue-90752-raw-ptr-shenanigans.rs
Normal file
41
src/test/ui/drop/issue-90752-raw-ptr-shenanigans.rs
Normal 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]);
|
||||
}
|
32
src/test/ui/drop/issue-90752.rs
Normal file
32
src/test/ui/drop/issue-90752.rs
Normal 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]);
|
||||
}
|
12
src/test/ui/moves/move-of-addr-of-mut.rs
Normal file
12
src/test/ui/moves/move-of-addr-of-mut.rs
Normal 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);
|
||||
}
|
11
src/test/ui/moves/move-of-addr-of-mut.stderr
Normal file
11
src/test/ui/moves/move-of-addr-of-mut.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user