Support reinitialization of variables

This commit is contained in:
Eric Holk 2021-11-03 16:28:07 -07:00
parent aa029d4bbe
commit 96117701f9
3 changed files with 116 additions and 32 deletions

View File

@ -691,13 +691,20 @@ impl DropRangeVisitor<'tcx> {
self.consumed_places.get_mut(&consumer).map(|places| places.insert(target));
}
fn drop_range(&mut self, hir_id: &HirId) -> &mut DropRange {
if !self.drop_ranges.contains_key(hir_id) {
self.drop_ranges.insert(*hir_id, DropRange::empty());
}
self.drop_ranges.get_mut(hir_id).unwrap()
}
fn record_drop(&mut self, hir_id: HirId) {
let drop_ranges = &mut self.drop_ranges;
if self.borrowed_places.contains(&hir_id) {
debug!("not marking {:?} as dropped because it is borrowed at some point", hir_id);
} else {
debug!("marking {:?} as dropped at {}", hir_id, self.expr_count);
drop_ranges.insert(hir_id, DropRange::new(self.expr_count));
let count = self.expr_count;
self.drop_range(&hir_id).drop(count);
}
}
@ -706,7 +713,6 @@ impl DropRangeVisitor<'tcx> {
other
}
#[allow(dead_code)]
fn fork_drop_ranges(&self) -> HirIdMap<DropRange> {
self.drop_ranges.iter().map(|(k, v)| (*k, v.fork_at(self.expr_count))).collect()
}
@ -720,7 +726,6 @@ impl DropRangeVisitor<'tcx> {
})
}
#[allow(dead_code)]
fn merge_drop_ranges(&mut self, drops: HirIdMap<DropRange>) {
drops.into_iter().for_each(|(k, v)| {
if !self.drop_ranges.contains_key(&k) {
@ -753,6 +758,20 @@ impl DropRangeVisitor<'tcx> {
}
}
}
fn reinit_expr(&mut self, expr: &hir::Expr<'_>) {
if let ExprKind::Path(hir::QPath::Resolved(
_,
hir::Path { res: hir::def::Res::Local(hir_id), .. },
)) = expr.kind
{
let location = self.expr_count;
debug!("reinitializing {:?} at {}", hir_id, location);
self.drop_range(hir_id).reinit(location)
} else {
warn!("reinitializing {:?} is not supported", expr);
}
}
}
fn place_hir_id(place: &Place<'_>) -> Option<HirId> {
@ -814,6 +833,7 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
}
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
let mut reinit = None;
match expr.kind {
ExprKind::AssignOp(_op, lhs, rhs) => {
// These operations are weird because their order of evaluation depends on whether
@ -867,11 +887,20 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
}
}
}
ExprKind::Assign(lhs, rhs, _) => {
self.visit_expr(lhs);
self.visit_expr(rhs);
reinit = Some(lhs);
}
_ => intravisit::walk_expr(self, expr),
}
self.expr_count += 1;
self.consume_expr(expr);
if let Some(expr) = reinit {
self.reinit_expr(expr);
}
}
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
@ -908,8 +937,8 @@ struct DropRange {
}
impl DropRange {
fn new(begin: usize) -> Self {
Self { events: vec![Event::Drop(begin)] }
fn empty() -> Self {
Self { events: vec![] }
}
fn intersect(&self, other: &Self) -> Self {
@ -966,12 +995,10 @@ impl DropRange {
}
}
#[allow(dead_code)]
fn drop(&mut self, location: usize) {
self.events.push(Event::Drop(location))
}
#[allow(dead_code)]
fn reinit(&mut self, location: usize) {
self.events.push(Event::Reinit(location));
}
@ -982,7 +1009,6 @@ impl DropRange {
/// at the end of both self and other.
///
/// Assumes that all locations in each range are less than joinpoint
#[allow(dead_code)]
fn merge_with(&mut self, other: &DropRange, join_point: usize) {
let mut events: Vec<_> =
self.events.iter().merge(other.events.iter()).dedup().cloned().collect();
@ -999,7 +1025,6 @@ impl DropRange {
/// Creates a new DropRange from this one at the split point.
///
/// Used to model branching control flow.
#[allow(dead_code)]
fn fork_at(&self, split_point: usize) -> Self {
Self {
events: vec![if self.is_dropped_at(split_point) {

View File

@ -0,0 +1,81 @@
// build-pass
// A test to ensure generators capture values that were conditionally dropped,
// and also that values that are dropped along all paths to a yield do not get
// included in the generator type.
#![feature(generators, negative_impls)]
#![allow(unused_assignments, dead_code)]
struct Ptr;
impl<'a> Drop for Ptr {
fn drop(&mut self) {}
}
struct NonSend {}
impl !Send for NonSend {}
fn assert_send<T: Send>(_: T) {}
// This test case is reduced from src/test/ui/drop/dynamic-drop-async.rs
fn one_armed_if(arg: bool) {
let _ = || {
let arr = [Ptr];
if arg {
drop(arr);
}
yield;
};
}
fn two_armed_if(arg: bool) {
assert_send(|| {
let arr = [Ptr];
if arg {
drop(arr);
} else {
drop(arr);
}
yield;
})
}
fn if_let(arg: Option<i32>) {
let _ = || {
let arr = [Ptr];
if let Some(_) = arg {
drop(arr);
}
yield;
};
}
fn reinit() {
let _ = || {
let mut arr = [Ptr];
drop(arr);
arr = [Ptr];
yield;
};
}
fn loop_uninit() {
let _ = || {
let mut arr = [Ptr];
let mut count = 0;
drop(arr);
while count < 3 {
yield;
arr = [Ptr];
count += 1;
}
};
}
fn main() {
one_armed_if(true);
if_let(Some(41));
reinit();
// loop_uninit();
}

View File

@ -1,22 +0,0 @@
// build-pass
// This test case is reduced from src/test/ui/drop/dynamic-drop-async.rs
#![feature(generators)]
struct Ptr;
impl<'a> Drop for Ptr {
fn drop(&mut self) {
}
}
fn main() {
let arg = true;
let _ = || {
let arr = [Ptr];
if arg {
drop(arr);
}
yield
};
}