Skip to content

Commit 73c0987

Browse files
committed
Do not mark unitinitialized locals as requiring storage
1 parent 1dfc3e7 commit 73c0987

File tree

5 files changed

+140
-17
lines changed

5 files changed

+140
-17
lines changed

src/librustc_mir/dataflow/impls/storage_liveness.rs

+30-8
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,13 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
109109
assert_eq!(1, self.body.arg_count);
110110
}
111111

112-
fn statement_effect(&self,
113-
sets: &mut GenKillSet<Local>,
114-
loc: Location) {
115-
self.check_for_move(sets, loc);
112+
fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
113+
// If we borrow or assign to a place then it needs storage for that
114+
// statement.
116115
self.check_for_borrow(sets, loc);
117116

118117
let stmt = &self.body[loc.block].statements[loc.statement_index];
119118
match stmt.kind {
120-
StatementKind::StorageLive(l) => sets.gen(l),
121119
StatementKind::StorageDead(l) => sets.kill(l),
122120
StatementKind::Assign(box(ref place, _))
123121
| StatementKind::SetDiscriminant { box ref place, .. } => {
@@ -136,11 +134,35 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
136134
}
137135
}
138136

139-
fn terminator_effect(&self,
140-
sets: &mut GenKillSet<Local>,
141-
loc: Location) {
137+
fn statement_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
138+
// If we move from a place then only stops needing storage *after*
139+
// that statement.
142140
self.check_for_move(sets, loc);
141+
}
142+
143+
fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
143144
self.check_for_borrow(sets, loc);
145+
146+
if let TerminatorKind::Call {
147+
destination: Some((Place { base: PlaceBase::Local(local), .. }, _)),
148+
..
149+
} = self.body[loc.block].terminator().kind {
150+
sets.gen(local);
151+
}
152+
}
153+
154+
fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
155+
// For call terminators the destination requires storage for the call
156+
// and after the call returns successfully, but not after a panic.
157+
// Since `propagate_call_unwind` doesn't exist, we have to kill the
158+
// destination here, and then gen it again in `propagate_call_return`.
159+
if let TerminatorKind::Call {
160+
destination: Some((Place { base: PlaceBase::Local(local), projection: box [] }, _)),
161+
..
162+
} = self.body[loc.block].terminator().kind {
163+
sets.kill(local);
164+
}
165+
self.check_for_move(sets, loc);
144166
}
145167

146168
fn propagate_call_return(

src/librustc_mir/transform/generator.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -508,10 +508,7 @@ fn locals_live_across_suspend_points(
508508
storage_liveness_map.insert(block, storage_liveness.clone());
509509

510510
requires_storage_cursor.seek(loc);
511-
let mut storage_required = requires_storage_cursor.get().clone();
512-
513-
// Mark locals without storage statements as always requiring storage
514-
storage_required.union(&ignored.0);
511+
let storage_required = requires_storage_cursor.get().clone();
515512

516513
// Locals live are live at this point only if they are used across
517514
// suspension points (the `liveness` variable)

src/test/ui/async-await/async-fn-size-moved-locals.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ struct BigFut([u8; BIG_FUT_SIZE]);
2222
impl BigFut {
2323
fn new() -> Self {
2424
BigFut([0; BIG_FUT_SIZE])
25-
} }
25+
}
26+
}
2627

2728
impl Drop for BigFut {
2829
fn drop(&mut self) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Test that we don't store uninitialized locals in futures from `async fn`.
2+
//
3+
// The exact sizes can change by a few bytes (we'd like to know when they do).
4+
// What we don't want to see is the wrong multiple of 1024 (the size of `Big`)
5+
// being reflected in the size.
6+
7+
// ignore-wasm32-bare (sizes don't match)
8+
// run-pass
9+
10+
// edition:2018
11+
12+
#![allow(unused_variables, unused_assignments)]
13+
14+
use std::future::Future;
15+
use std::pin::Pin;
16+
use std::task::{Context, Poll};
17+
18+
const BIG_FUT_SIZE: usize = 1024;
19+
struct Big([u8; BIG_FUT_SIZE]);
20+
21+
impl Big {
22+
fn new() -> Self {
23+
Big([0; BIG_FUT_SIZE])
24+
}
25+
}
26+
27+
impl Drop for Big {
28+
fn drop(&mut self) {}
29+
}
30+
31+
#[allow(dead_code)]
32+
struct Joiner {
33+
a: Option<Big>,
34+
b: Option<Big>,
35+
c: Option<Big>,
36+
}
37+
38+
impl Future for Joiner {
39+
type Output = ();
40+
41+
fn poll(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<Self::Output> {
42+
Poll::Ready(())
43+
}
44+
}
45+
46+
fn noop() {}
47+
async fn fut() {}
48+
49+
async fn single() {
50+
let x;
51+
fut().await;
52+
x = Big::new();
53+
}
54+
55+
async fn single_with_noop() {
56+
let x;
57+
fut().await;
58+
noop();
59+
x = Big::new();
60+
noop();
61+
}
62+
63+
async fn joined() {
64+
let joiner;
65+
let a = Big::new();
66+
let b = Big::new();
67+
let c = Big::new();
68+
69+
fut().await;
70+
noop();
71+
joiner = Joiner { a: Some(a), b: Some(b), c: Some(c) };
72+
noop();
73+
}
74+
75+
async fn joined_with_noop() {
76+
let joiner;
77+
let a = Big::new();
78+
let b = Big::new();
79+
let c = Big::new();
80+
81+
fut().await;
82+
noop();
83+
joiner = Joiner { a: Some(a), b: Some(b), c: Some(c) };
84+
noop();
85+
}
86+
87+
async fn join_retval() -> Joiner {
88+
let a = Big::new();
89+
let b = Big::new();
90+
let c = Big::new();
91+
92+
fut().await;
93+
noop();
94+
Joiner { a: Some(a), b: Some(b), c: Some(c) }
95+
}
96+
97+
fn main() {
98+
assert_eq!(8, std::mem::size_of_val(&single()));
99+
assert_eq!(12, std::mem::size_of_val(&single_with_noop()));
100+
assert_eq!(3084, std::mem::size_of_val(&joined()));
101+
assert_eq!(3084, std::mem::size_of_val(&joined_with_noop()));
102+
assert_eq!(3084, std::mem::size_of_val(&join_retval()));
103+
}

src/test/ui/async-await/async-fn-size.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ fn main() {
8989
assert_eq!(8, std::mem::size_of_val(&await1_level1()));
9090
assert_eq!(12, std::mem::size_of_val(&await2_level1()));
9191
assert_eq!(12, std::mem::size_of_val(&await3_level1()));
92-
assert_eq!(20, std::mem::size_of_val(&await3_level2()));
93-
assert_eq!(28, std::mem::size_of_val(&await3_level3()));
94-
assert_eq!(36, std::mem::size_of_val(&await3_level4()));
95-
assert_eq!(44, std::mem::size_of_val(&await3_level5()));
92+
assert_eq!(24, std::mem::size_of_val(&await3_level2()));
93+
assert_eq!(36, std::mem::size_of_val(&await3_level3()));
94+
assert_eq!(48, std::mem::size_of_val(&await3_level4()));
95+
assert_eq!(60, std::mem::size_of_val(&await3_level5()));
9696

9797
assert_eq!(1, wait(base()));
9898
assert_eq!(1, wait(await1_level1()));

0 commit comments

Comments
 (0)