Rollup merge of #62465 - matthewjasper:never-type-storage, r=pnkfelix

Sometimes generate storage statements for temporaries with type `!`

Closes #62165
cc #42371
This commit is contained in:
Mazdak Farrokhzad 2019-07-10 16:08:21 +02:00 committed by GitHub
commit 5760bc6e98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 29 deletions

View file

@ -73,13 +73,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
let slice = unpack!(block = this.as_place(block, lhs));
// region_scope=None so place indexes live forever. They are scalars so they
// do not need storage annotations, and they are often copied between
// places.
// Making this a *fresh* temporary also means we do not have to worry about
// the index changing later: Nothing will ever change this temporary.
// The "retagging" transformation (for Stacked Borrows) relies on this.
let idx = unpack!(block = this.as_temp(block, None, index, Mutability::Mut));
let idx = unpack!(block = this.as_temp(
block,
expr.temp_lifetime,
index,
Mutability::Not,
));
// bounds check:
let (len, lt) = (

View file

@ -3,6 +3,7 @@
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::scope::DropKind;
use crate::hair::*;
use rustc::hir;
use rustc::middle::region;
use rustc::mir::*;
@ -66,32 +67,46 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
};
let temp_place = &Place::from(temp);
if !expr_ty.is_never() {
this.cfg.push(
block,
Statement {
source_info,
kind: StatementKind::StorageLive(temp),
},
);
// In constants, `temp_lifetime` is `None` for temporaries that live for the
// `'static` lifetime. Thus we do not drop these temporaries and simply leak them.
// This is equivalent to what `let x = &foo();` does in functions. The temporary
// is lifted to their surrounding scope. In a function that means the temporary lives
// until just before the function returns. In constants that means it outlives the
// constant's initialization value computation. Anything outliving a constant
// must have the `'static` lifetime and live forever.
// Anything with a shorter lifetime (e.g the `&foo()` in `bar(&foo())` or anything
// within a block will keep the regular drops just like runtime code.
if let Some(temp_lifetime) = temp_lifetime {
this.schedule_drop(
expr_span,
temp_lifetime,
temp,
expr_ty,
DropKind::Storage,
match expr.kind {
// Don't bother with StorageLive and Dead for these temporaries,
// they are never assigned.
ExprKind::Break { .. } |
ExprKind::Continue { .. } |
ExprKind::Return { .. } => (),
ExprKind::Block {
body: hir::Block { expr: None, targeted_by_break: false, .. }
} if expr_ty.is_never() => (),
_ => {
this.cfg.push(
block,
Statement {
source_info,
kind: StatementKind::StorageLive(temp),
},
);
// In constants, `temp_lifetime` is `None` for temporaries that
// live for the `'static` lifetime. Thus we do not drop these
// temporaries and simply leak them.
// This is equivalent to what `let x = &foo();` does in
// functions. The temporary is lifted to their surrounding
// scope. In a function that means the temporary lives until
// just before the function returns. In constants that means it
// outlives the constant's initialization value computation.
// Anything outliving a constant must have the `'static`
// lifetime and live forever.
// Anything with a shorter lifetime (e.g the `&foo()` in
// `bar(&foo())` or anything within a block will keep the
// regular drops just like runtime code.
if let Some(temp_lifetime) = temp_lifetime {
this.schedule_drop(
expr_span,
temp_lifetime,
temp,
expr_ty,
DropKind::Storage,
);
}
}
}

View file

@ -26,6 +26,7 @@ fn main() {
// _1 = ();
// StorageDead(_2);
// StorageDead(_1);
// StorageLive(_4);
// goto -> bb5;
// }
// ...

View file

@ -0,0 +1,14 @@
// Regression test for issue 62165
// check-pass
#![feature(never_type)]
pub fn main() {
loop {
match None {
None => return,
Some(val) => val,
};
};
}