Skip to content

Commit 7b3cd07

Browse files
committed
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](#90752 (comment)) 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`
2 parents 311fa1f + 22d937d commit 7b3cd07

File tree

5 files changed

+163
-6
lines changed

5 files changed

+163
-6
lines changed

compiler/rustc_mir_dataflow/src/impls/mod.rs

+67-6
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44
55
use rustc_index::bit_set::BitSet;
66
use rustc_index::vec::Idx;
7+
use rustc_middle::mir::visit::{MirVisitable, Visitor};
78
use rustc_middle::mir::{self, Body, Location};
89
use rustc_middle::ty::{self, TyCtxt};
910

10-
use crate::drop_flag_effects;
1111
use crate::drop_flag_effects_for_function_entry;
1212
use crate::drop_flag_effects_for_location;
1313
use crate::elaborate_drops::DropFlagState;
1414
use crate::framework::SwitchIntEdgeEffects;
15-
use crate::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex};
15+
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
1616
use crate::on_lookup_result_bits;
1717
use crate::MoveDataParamEnv;
18+
use crate::{drop_flag_effects, on_all_children_bits};
1819
use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
1920

2021
mod borrowed_locals;
@@ -307,22 +308,45 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
307308
fn statement_effect(
308309
&self,
309310
trans: &mut impl GenKill<Self::Idx>,
310-
_statement: &mir::Statement<'tcx>,
311+
statement: &mir::Statement<'tcx>,
311312
location: Location,
312313
) {
313314
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
314315
Self::update_bits(trans, path, s)
316+
});
317+
318+
if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration {
319+
return;
320+
}
321+
322+
// Mark all places as "maybe init" if they are mutably borrowed. See #90752.
323+
for_each_mut_borrow(statement, location, |place| {
324+
let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) else { return };
325+
on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| {
326+
trans.gen(child);
327+
})
315328
})
316329
}
317330

318331
fn terminator_effect(
319332
&self,
320333
trans: &mut impl GenKill<Self::Idx>,
321-
_terminator: &mir::Terminator<'tcx>,
334+
terminator: &mir::Terminator<'tcx>,
322335
location: Location,
323336
) {
324337
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
325338
Self::update_bits(trans, path, s)
339+
});
340+
341+
if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration {
342+
return;
343+
}
344+
345+
for_each_mut_borrow(terminator, location, |place| {
346+
let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) else { return };
347+
on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| {
348+
trans.gen(child);
349+
})
326350
})
327351
}
328352

@@ -427,7 +451,10 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
427451
) {
428452
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
429453
Self::update_bits(trans, path, s)
430-
})
454+
});
455+
456+
// Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a
457+
// mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
431458
}
432459

433460
fn terminator_effect(
@@ -438,7 +465,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
438465
) {
439466
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
440467
Self::update_bits(trans, path, s)
441-
})
468+
});
442469
}
443470

444471
fn call_return_effect(
@@ -704,3 +731,37 @@ fn switch_on_enum_discriminant(
704731
_ => None,
705732
}
706733
}
734+
735+
struct OnMutBorrow<F>(F);
736+
737+
impl<F> Visitor<'_> for OnMutBorrow<F>
738+
where
739+
F: FnMut(&mir::Place<'_>),
740+
{
741+
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'_>, location: Location) {
742+
// FIXME: Does `&raw const foo` allow mutation? See #90413.
743+
match rvalue {
744+
mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place)
745+
| mir::Rvalue::AddressOf(_, place) => (self.0)(place),
746+
747+
_ => {}
748+
}
749+
750+
self.super_rvalue(rvalue, location)
751+
}
752+
}
753+
754+
/// Calls `f` for each mutable borrow or raw reference in the program.
755+
///
756+
/// This DOES NOT call `f` for a shared borrow of a type with interior mutability. That's okay for
757+
/// initializedness, because we cannot move from an `UnsafeCell` (outside of `core::cell`), but
758+
/// other analyses will likely need to check for `!Freeze`.
759+
fn for_each_mut_borrow<'tcx>(
760+
mir: &impl MirVisitable<'tcx>,
761+
location: Location,
762+
f: impl FnMut(&mir::Place<'_>),
763+
) {
764+
let mut vis = OnMutBorrow(f);
765+
766+
mir.apply(location, &mut vis);
767+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// run-pass
2+
3+
use std::cell::RefCell;
4+
5+
struct S<'a>(i32, &'a RefCell<Vec<i32>>);
6+
7+
impl<'a> Drop for S<'a> {
8+
fn drop(&mut self) {
9+
self.1.borrow_mut().push(self.0);
10+
}
11+
}
12+
13+
fn test(drops: &RefCell<Vec<i32>>) {
14+
let mut foo = None;
15+
let pfoo: *mut _ = &mut foo;
16+
17+
match foo {
18+
None => (),
19+
_ => return,
20+
}
21+
22+
// Both S(0) and S(1) should be dropped, but aren't.
23+
unsafe { *pfoo = Some((S(0, drops), S(1, drops))); }
24+
25+
match foo {
26+
Some((_x, _)) => {}
27+
_ => {}
28+
}
29+
}
30+
31+
fn main() {
32+
let drops = RefCell::new(Vec::new());
33+
test(&drops);
34+
35+
// Ideally, we want this...
36+
//assert_eq!(*drops.borrow(), &[0, 1]);
37+
38+
// But the delayed access through the raw pointer confuses drop elaboration,
39+
// causing S(1) to be leaked.
40+
assert_eq!(*drops.borrow(), &[0]);
41+
}

src/test/ui/drop/issue-90752.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// run-pass
2+
3+
use std::cell::RefCell;
4+
5+
struct S<'a>(i32, &'a RefCell<Vec<i32>>);
6+
7+
impl<'a> Drop for S<'a> {
8+
fn drop(&mut self) {
9+
self.1.borrow_mut().push(self.0);
10+
}
11+
}
12+
13+
fn test(drops: &RefCell<Vec<i32>>) {
14+
let mut foo = None;
15+
match foo {
16+
None => (),
17+
_ => return,
18+
}
19+
20+
*(&mut foo) = Some((S(0, drops), S(1, drops))); // Both S(0) and S(1) should be dropped
21+
22+
match foo {
23+
Some((_x, _)) => {}
24+
_ => {}
25+
}
26+
}
27+
28+
fn main() {
29+
let drops = RefCell::new(Vec::new());
30+
test(&drops);
31+
assert_eq!(*drops.borrow(), &[0, 1]);
32+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Ensure that taking a mutable raw ptr to an uninitialized variable does not change its
2+
// initializedness.
3+
4+
struct S;
5+
6+
fn main() {
7+
let mut x: S;
8+
std::ptr::addr_of_mut!(x); //~ borrow of
9+
10+
let y = x; // Should error here if `addr_of_mut` is ever allowed on uninitialized variables
11+
drop(y);
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0381]: borrow of possibly-uninitialized variable: `x`
2+
--> $DIR/move-of-addr-of-mut.rs:8:5
3+
|
4+
LL | std::ptr::addr_of_mut!(x);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `x`
6+
|
7+
= note: this error originates in the macro `std::ptr::addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0381`.

0 commit comments

Comments
 (0)