Do not mark unitinitialized locals as requiring storage
This commit is contained in:
parent
1dfc3e7962
commit
73c09870b5
5 changed files with 140 additions and 17 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
103
src/test/ui/async-await/async-fn-size-uninit-locals.rs
Normal file
103
src/test/ui/async-await/async-fn-size-uninit-locals.rs
Normal file
|
|
@ -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()));
|
||||
}
|
||||
|
|
@ -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()));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue