about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <ariel.byd@gmail.com>2017-10-17 21:37:18 +0300
committerAriel Ben-Yehuda <ariel.byd@gmail.com>2017-10-17 21:37:18 +0300
commita6ca84a38340bdbf3c02ba4c7970c9289e5bc946 (patch)
tree0d4e873da04b3692422d9bdc72e8a1f36ce68f5f
parenta151d37401b20d235eac1d862579dbbaa4ccea13 (diff)
downloadrust-a6ca84a38340bdbf3c02ba4c7970c9289e5bc946.tar.gz
rust-a6ca84a38340bdbf3c02ba4c7970c9289e5bc946.zip
look past the next drop for the drop panic target
The previous code would leak data on a drop panic if the immediate next
drop was a StorageDead that was followed by another drop.
-rw-r--r--src/librustc_mir/build/scope.rs16
-rw-r--r--src/test/run-pass/dynamic-drop.rs22
2 files changed, 24 insertions, 14 deletions
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index b88b61f1818..772438d5252 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -836,24 +836,22 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
                            generator_drop: bool)
                            -> BlockAnd<()> {
     debug!("build_scope_drops({:?} -> {:?})", block, scope);
-    let mut iter = scope.drops.iter().rev().peekable();
+    let mut iter = scope.drops.iter().rev();
     while let Some(drop_data) = iter.next() {
         let source_info = scope.source_info(drop_data.span);
         match drop_data.kind {
             DropKind::Value { .. } => {
                 // Try to find the next block with its cached block
                 // for us to diverge into in case the drop panics.
-                let on_diverge = iter.peek().iter().filter_map(|dd| {
+                let on_diverge = iter.clone().filter_map(|dd| {
                     match dd.kind {
-                        DropKind::Value { cached_block } => {
-                            let result = cached_block.get(generator_drop);
-                            if result.is_none() {
-                                span_bug!(drop_data.span, "cached block not present?")
-                            }
-                            result
-                        },
+                        DropKind::Value { cached_block } => Some(cached_block),
                         DropKind::Storage => None
                     }
+                }).map(|cached_block| {
+                    cached_block
+                        .get(generator_drop)
+                        .unwrap_or_else(|| span_bug!(drop_data.span, "cached block not present?"))
                 }).next();
                 // If there’s no `cached_block`s within current scope,
                 // we must look for one in the enclosing scope.
diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs
index d8b6dbe48f1..483dbb356ce 100644
--- a/src/test/run-pass/dynamic-drop.rs
+++ b/src/test/run-pass/dynamic-drop.rs
@@ -177,6 +177,17 @@ fn generator(a: &Allocator, run_count: usize) {
     }
 }
 
+fn mixed_drop_and_nondrop(a: &Allocator) {
+    // check that destructor panics handle drop
+    // and non-drop blocks in the same scope correctly.
+    //
+    // Surprisingly enough, this used to not work.
+    let (x, y, z);
+    x = a.alloc();
+    y = 5;
+    z = a.alloc();
+}
+
 #[allow(unreachable_code)]
 fn vec_unreachable(a: &Allocator) {
     let _x = vec![a.alloc(), a.alloc(), a.alloc(), return];
@@ -244,11 +255,12 @@ fn main() {
     run_test(|a| field_assignment(a, false));
     run_test(|a| field_assignment(a, true));
 
-    // FIXME: fix leaks on panics
-    run_test_nopanic(|a| generator(a, 0));
-    run_test_nopanic(|a| generator(a, 1));
-    run_test_nopanic(|a| generator(a, 2));
-    run_test_nopanic(|a| generator(a, 3));
+    run_test(|a| generator(a, 0));
+    run_test(|a| generator(a, 1));
+    run_test(|a| generator(a, 2));
+    run_test(|a| generator(a, 3));
+
+    run_test(|a| mixed_drop_and_nondrop(a));
 
     run_test_nopanic(|a| union1(a));
 }