about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthew Jasper <mjjasper1@gmail.com>2019-09-29 21:34:22 +0100
committerMatthew Jasper <mjjasper1@gmail.com>2019-10-02 20:55:38 +0100
commit73c09870b5da81c16209844fe8ca650afd4e7ade (patch)
tree1ae2ef5c3c81b25acc5d2ed5550fbd9c88a35081
parent1dfc3e79628e8faa67617adc02928af809602cd9 (diff)
downloadrust-73c09870b5da81c16209844fe8ca650afd4e7ade.tar.gz
rust-73c09870b5da81c16209844fe8ca650afd4e7ade.zip
Do not mark unitinitialized locals as requiring storage
-rw-r--r--src/librustc_mir/dataflow/impls/storage_liveness.rs38
-rw-r--r--src/librustc_mir/transform/generator.rs5
-rw-r--r--src/test/ui/async-await/async-fn-size-moved-locals.rs3
-rw-r--r--src/test/ui/async-await/async-fn-size-uninit-locals.rs103
-rw-r--r--src/test/ui/async-await/async-fn-size.rs8
5 files changed, 140 insertions, 17 deletions
diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs
index 0f66b13fdc5..c1695ba66d0 100644
--- a/src/librustc_mir/dataflow/impls/storage_liveness.rs
+++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs
@@ -109,15 +109,13 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
         assert_eq!(1, self.body.arg_count);
     }
 
-    fn statement_effect(&self,
-                        sets: &mut GenKillSet<Local>,
-                        loc: Location) {
-        self.check_for_move(sets, loc);
+    fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
+        // If we borrow or assign to a place then it needs storage for that
+        // statement.
         self.check_for_borrow(sets, loc);
 
         let stmt = &self.body[loc.block].statements[loc.statement_index];
         match stmt.kind {
-            StatementKind::StorageLive(l) => sets.gen(l),
             StatementKind::StorageDead(l) => sets.kill(l),
             StatementKind::Assign(box(ref place, _))
             | StatementKind::SetDiscriminant { box ref place, .. } => {
@@ -136,11 +134,35 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
         }
     }
 
-    fn terminator_effect(&self,
-                         sets: &mut GenKillSet<Local>,
-                         loc: Location) {
+    fn statement_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
+        // If we move from a place then only stops needing storage *after*
+        // that statement.
         self.check_for_move(sets, loc);
+    }
+
+    fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
         self.check_for_borrow(sets, loc);
+
+        if let TerminatorKind::Call {
+            destination: Some((Place { base: PlaceBase::Local(local), .. }, _)),
+            ..
+        } = self.body[loc.block].terminator().kind {
+            sets.gen(local);
+        }
+    }
+
+    fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
+        // For call terminators the destination requires storage for the call
+        // and after the call returns successfully, but not after a panic.
+        // Since `propagate_call_unwind` doesn't exist, we have to kill the
+        // destination here, and then gen it again in `propagate_call_return`.
+        if let TerminatorKind::Call {
+            destination: Some((Place { base: PlaceBase::Local(local), projection: box [] }, _)),
+            ..
+        } = self.body[loc.block].terminator().kind {
+            sets.kill(local);
+        }
+        self.check_for_move(sets, loc);
     }
 
     fn propagate_call_return(
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 2b66e602370..f304ac4e9e1 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -508,10 +508,7 @@ fn locals_live_across_suspend_points(
             storage_liveness_map.insert(block, storage_liveness.clone());
 
             requires_storage_cursor.seek(loc);
-            let mut storage_required = requires_storage_cursor.get().clone();
-
-            // Mark locals without storage statements as always requiring storage
-            storage_required.union(&ignored.0);
+            let storage_required = requires_storage_cursor.get().clone();
 
             // Locals live are live at this point only if they are used across
             // suspension points (the `liveness` variable)
diff --git a/src/test/ui/async-await/async-fn-size-moved-locals.rs b/src/test/ui/async-await/async-fn-size-moved-locals.rs
index 3ffcbb58595..c266644fd70 100644
--- a/src/test/ui/async-await/async-fn-size-moved-locals.rs
+++ b/src/test/ui/async-await/async-fn-size-moved-locals.rs
@@ -22,7 +22,8 @@ struct BigFut([u8; BIG_FUT_SIZE]);
 impl BigFut {
     fn new() -> Self {
         BigFut([0; BIG_FUT_SIZE])
-    } }
+    }
+}
 
 impl Drop for BigFut {
     fn drop(&mut self) {}
diff --git a/src/test/ui/async-await/async-fn-size-uninit-locals.rs b/src/test/ui/async-await/async-fn-size-uninit-locals.rs
new file mode 100644
index 00000000000..a489fb11630
--- /dev/null
+++ b/src/test/ui/async-await/async-fn-size-uninit-locals.rs
@@ -0,0 +1,103 @@
+// Test that we don't store uninitialized locals in futures from `async fn`.
+//
+// The exact sizes can change by a few bytes (we'd like to know when they do).
+// What we don't want to see is the wrong multiple of 1024 (the size of `Big`)
+// being reflected in the size.
+
+// ignore-wasm32-bare (sizes don't match)
+// run-pass
+
+// edition:2018
+
+#![allow(unused_variables, unused_assignments)]
+
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+const BIG_FUT_SIZE: usize = 1024;
+struct Big([u8; BIG_FUT_SIZE]);
+
+impl Big {
+    fn new() -> Self {
+        Big([0; BIG_FUT_SIZE])
+    }
+}
+
+impl Drop for Big {
+    fn drop(&mut self) {}
+}
+
+#[allow(dead_code)]
+struct Joiner {
+    a: Option<Big>,
+    b: Option<Big>,
+    c: Option<Big>,
+}
+
+impl Future for Joiner {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<Self::Output> {
+        Poll::Ready(())
+    }
+}
+
+fn noop() {}
+async fn fut() {}
+
+async fn single() {
+    let x;
+    fut().await;
+    x = Big::new();
+}
+
+async fn single_with_noop() {
+    let x;
+    fut().await;
+    noop();
+    x = Big::new();
+    noop();
+}
+
+async fn joined() {
+    let joiner;
+    let a = Big::new();
+    let b = Big::new();
+    let c = Big::new();
+
+    fut().await;
+    noop();
+    joiner = Joiner { a: Some(a), b: Some(b), c: Some(c) };
+    noop();
+}
+
+async fn joined_with_noop() {
+    let joiner;
+    let a = Big::new();
+    let b = Big::new();
+    let c = Big::new();
+
+    fut().await;
+    noop();
+    joiner = Joiner { a: Some(a), b: Some(b), c: Some(c) };
+    noop();
+}
+
+async fn join_retval() -> Joiner {
+    let a = Big::new();
+    let b = Big::new();
+    let c = Big::new();
+
+    fut().await;
+    noop();
+    Joiner { a: Some(a), b: Some(b), c: Some(c) }
+}
+
+fn main() {
+    assert_eq!(8, std::mem::size_of_val(&single()));
+    assert_eq!(12, std::mem::size_of_val(&single_with_noop()));
+    assert_eq!(3084, std::mem::size_of_val(&joined()));
+    assert_eq!(3084, std::mem::size_of_val(&joined_with_noop()));
+    assert_eq!(3084, std::mem::size_of_val(&join_retval()));
+}
diff --git a/src/test/ui/async-await/async-fn-size.rs b/src/test/ui/async-await/async-fn-size.rs
index b5c94ecb716..b313992db4e 100644
--- a/src/test/ui/async-await/async-fn-size.rs
+++ b/src/test/ui/async-await/async-fn-size.rs
@@ -89,10 +89,10 @@ fn main() {
     assert_eq!(8, std::mem::size_of_val(&await1_level1()));
     assert_eq!(12, std::mem::size_of_val(&await2_level1()));
     assert_eq!(12, std::mem::size_of_val(&await3_level1()));
-    assert_eq!(20, std::mem::size_of_val(&await3_level2()));
-    assert_eq!(28, std::mem::size_of_val(&await3_level3()));
-    assert_eq!(36, std::mem::size_of_val(&await3_level4()));
-    assert_eq!(44, std::mem::size_of_val(&await3_level5()));
+    assert_eq!(24, std::mem::size_of_val(&await3_level2()));
+    assert_eq!(36, std::mem::size_of_val(&await3_level3()));
+    assert_eq!(48, std::mem::size_of_val(&await3_level4()));
+    assert_eq!(60, std::mem::size_of_val(&await3_level5()));
 
     assert_eq!(1,   wait(base()));
     assert_eq!(1,   wait(await1_level1()));