Auto merge of #52046 - cramertj:fix-generator-mir, r=eddyb
Ensure StorageDead is created even if variable initialization fails Rebase and slight cleanup of https://github.com/rust-lang/rust/pull/51109 Fixes https://github.com/rust-lang/rust/issues/49232 r? @eddyb
This commit is contained in:
commit
e92e9ce0d8
|
@ -102,7 +102,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
});
|
});
|
||||||
if let Some(scope) = scope {
|
if let Some(scope) = scope {
|
||||||
// schedule a shallow free of that memory, lest we unwind:
|
// schedule a shallow free of that memory, lest we unwind:
|
||||||
this.schedule_drop(expr_span, scope, &Place::Local(result), value.ty);
|
this.schedule_drop_storage_and_value(
|
||||||
|
expr_span, scope, &Place::Local(result), value.ty,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// malloc some memory of suitable type (thus far, uninitialized):
|
// malloc some memory of suitable type (thus far, uninitialized):
|
||||||
|
|
|
@ -62,7 +62,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
// anything because no values with a destructor can be created in
|
// anything because no values with a destructor can be created in
|
||||||
// a constant at this time, even if the type may need dropping.
|
// a constant at this time, even if the type may need dropping.
|
||||||
if let Some(temp_lifetime) = temp_lifetime {
|
if let Some(temp_lifetime) = temp_lifetime {
|
||||||
this.schedule_drop(expr_span, temp_lifetime, &Place::Local(temp), expr_ty);
|
this.schedule_drop_storage_and_value(
|
||||||
|
expr_span, temp_lifetime, &Place::Local(temp), expr_ty,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
block.and(temp)
|
block.and(temp)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
use build::{BlockAnd, BlockAndExtension, Builder};
|
use build::{BlockAnd, BlockAndExtension, Builder};
|
||||||
use build::{GuardFrame, GuardFrameLocal, LocalsForNode};
|
use build::{GuardFrame, GuardFrameLocal, LocalsForNode};
|
||||||
use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
|
use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
|
||||||
|
use build::scope::{CachedBlock, DropKind};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_data_structures::bitvec::BitVector;
|
use rustc_data_structures::bitvec::BitVector;
|
||||||
use rustc::ty::{self, Ty};
|
use rustc::ty::{self, Ty};
|
||||||
|
@ -367,7 +368,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
source_info,
|
source_info,
|
||||||
kind: StatementKind::StorageLive(local_id)
|
kind: StatementKind::StorageLive(local_id)
|
||||||
});
|
});
|
||||||
Place::Local(local_id)
|
let place = Place::Local(local_id);
|
||||||
|
let var_ty = self.local_decls[local_id].ty;
|
||||||
|
let hir_id = self.hir.tcx().hir.node_to_hir_id(var);
|
||||||
|
let region_scope = self.hir.region_scope_tree.var_scope(hir_id.local_id);
|
||||||
|
self.schedule_drop(
|
||||||
|
span, region_scope, &place, var_ty,
|
||||||
|
DropKind::Storage,
|
||||||
|
);
|
||||||
|
place
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn schedule_drop_for_binding(&mut self,
|
pub fn schedule_drop_for_binding(&mut self,
|
||||||
|
@ -378,7 +387,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
let var_ty = self.local_decls[local_id].ty;
|
let var_ty = self.local_decls[local_id].ty;
|
||||||
let hir_id = self.hir.tcx().hir.node_to_hir_id(var);
|
let hir_id = self.hir.tcx().hir.node_to_hir_id(var);
|
||||||
let region_scope = self.hir.region_scope_tree.var_scope(hir_id.local_id);
|
let region_scope = self.hir.region_scope_tree.var_scope(hir_id.local_id);
|
||||||
self.schedule_drop(span, region_scope, &Place::Local(local_id), var_ty);
|
self.schedule_drop(
|
||||||
|
span, region_scope, &Place::Local(local_id), var_ty,
|
||||||
|
DropKind::Value {
|
||||||
|
cached_block: CachedBlock::default(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visit_bindings<F>(&mut self, pattern: &Pattern<'tcx>, f: &mut F)
|
pub fn visit_bindings<F>(&mut self, pattern: &Pattern<'tcx>, f: &mut F)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
|
|
||||||
use build;
|
use build;
|
||||||
|
use build::scope::{CachedBlock, DropKind};
|
||||||
use hair::cx::Cx;
|
use hair::cx::Cx;
|
||||||
use hair::{LintLevel, BindingMode, PatternKind};
|
use hair::{LintLevel, BindingMode, PatternKind};
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
|
@ -744,9 +745,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we drop (parts of) the argument even when not matched on.
|
// Make sure we drop (parts of) the argument even when not matched on.
|
||||||
self.schedule_drop(pattern.as_ref().map_or(ast_body.span, |pat| pat.span),
|
self.schedule_drop(
|
||||||
argument_scope, &place, ty);
|
pattern.as_ref().map_or(ast_body.span, |pat| pat.span),
|
||||||
|
argument_scope, &place, ty,
|
||||||
|
DropKind::Value { cached_block: CachedBlock::default() },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enter the argument pattern bindings source scope, if it exists.
|
// Enter the argument pattern bindings source scope, if it exists.
|
||||||
|
|
|
@ -144,12 +144,12 @@ struct DropData<'tcx> {
|
||||||
/// place to drop
|
/// place to drop
|
||||||
location: Place<'tcx>,
|
location: Place<'tcx>,
|
||||||
|
|
||||||
/// Whether this is a full value Drop, or just a StorageDead.
|
/// Whether this is a value Drop or a StorageDead.
|
||||||
kind: DropKind
|
kind: DropKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
struct CachedBlock {
|
pub(crate) struct CachedBlock {
|
||||||
/// The cached block for the cleanups-on-diverge path. This block
|
/// The cached block for the cleanups-on-diverge path. This block
|
||||||
/// contains code to run the current drop and all the preceding
|
/// contains code to run the current drop and all the preceding
|
||||||
/// drops (i.e. those having lower index in Drop’s Scope drop
|
/// drops (i.e. those having lower index in Drop’s Scope drop
|
||||||
|
@ -166,7 +166,7 @@ struct CachedBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum DropKind {
|
pub(crate) enum DropKind {
|
||||||
Value {
|
Value {
|
||||||
cached_block: CachedBlock,
|
cached_block: CachedBlock,
|
||||||
},
|
},
|
||||||
|
@ -622,25 +622,58 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
abortblk
|
abortblk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn schedule_drop_storage_and_value(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
region_scope: region::Scope,
|
||||||
|
place: &Place<'tcx>,
|
||||||
|
place_ty: Ty<'tcx>,
|
||||||
|
) {
|
||||||
|
self.schedule_drop(
|
||||||
|
span, region_scope, place, place_ty,
|
||||||
|
DropKind::Storage,
|
||||||
|
);
|
||||||
|
self.schedule_drop(
|
||||||
|
span, region_scope, place, place_ty,
|
||||||
|
DropKind::Value {
|
||||||
|
cached_block: CachedBlock::default(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Scheduling drops
|
// Scheduling drops
|
||||||
// ================
|
// ================
|
||||||
/// Indicates that `place` should be dropped on exit from
|
/// Indicates that `place` should be dropped on exit from
|
||||||
/// `region_scope`.
|
/// `region_scope`.
|
||||||
pub fn schedule_drop(&mut self,
|
///
|
||||||
span: Span,
|
/// When called with `DropKind::Storage`, `place` should be a local
|
||||||
region_scope: region::Scope,
|
/// with an index higher than the current `self.arg_count`.
|
||||||
place: &Place<'tcx>,
|
pub fn schedule_drop(
|
||||||
place_ty: Ty<'tcx>) {
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
region_scope: region::Scope,
|
||||||
|
place: &Place<'tcx>,
|
||||||
|
place_ty: Ty<'tcx>,
|
||||||
|
drop_kind: DropKind,
|
||||||
|
) {
|
||||||
let needs_drop = self.hir.needs_drop(place_ty);
|
let needs_drop = self.hir.needs_drop(place_ty);
|
||||||
let drop_kind = if needs_drop {
|
match drop_kind {
|
||||||
DropKind::Value { cached_block: CachedBlock::default() }
|
DropKind::Value { .. } => if !needs_drop { return },
|
||||||
} else {
|
DropKind::Storage => {
|
||||||
// Only temps and vars need their storage dead.
|
match *place {
|
||||||
match *place {
|
Place::Local(index) => if index.index() <= self.arg_count {
|
||||||
Place::Local(index) if index.index() > self.arg_count => DropKind::Storage,
|
span_bug!(
|
||||||
_ => return
|
span, "`schedule_drop` called with index {} and arg_count {}",
|
||||||
|
index.index(),
|
||||||
|
self.arg_count,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
_ => span_bug!(
|
||||||
|
span, "`schedule_drop` called with non-`Local` place {:?}", place
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
for scope in self.scopes.iter_mut().rev() {
|
for scope in self.scopes.iter_mut().rev() {
|
||||||
let this_scope = scope.region_scope == region_scope;
|
let this_scope = scope.region_scope == region_scope;
|
||||||
|
@ -895,24 +928,24 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
|
||||||
});
|
});
|
||||||
block = next;
|
block = next;
|
||||||
}
|
}
|
||||||
DropKind::Storage => {}
|
DropKind::Storage => {
|
||||||
}
|
// We do not need to emit StorageDead for generator drops
|
||||||
|
if generator_drop {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// We do not need to emit StorageDead for generator drops
|
// Drop the storage for both value and storage drops.
|
||||||
if generator_drop {
|
// Only temps and vars need their storage dead.
|
||||||
continue
|
match drop_data.location {
|
||||||
}
|
Place::Local(index) if index.index() > arg_count => {
|
||||||
|
cfg.push(block, Statement {
|
||||||
// Drop the storage for both value and storage drops.
|
source_info,
|
||||||
// Only temps and vars need their storage dead.
|
kind: StatementKind::StorageDead(index)
|
||||||
match drop_data.location {
|
});
|
||||||
Place::Local(index) if index.index() > arg_count => {
|
}
|
||||||
cfg.push(block, Statement {
|
_ => unreachable!(),
|
||||||
source_info,
|
}
|
||||||
kind: StatementKind::StorageDead(index)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
_ => continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
block.unit()
|
block.unit()
|
||||||
|
|
|
@ -31,11 +31,11 @@ pub fn test() {
|
||||||
// CHECK: [[S__4:%[0-9]+]] = bitcast { i32, i32 }* %_4 to i8*
|
// CHECK: [[S__4:%[0-9]+]] = bitcast { i32, i32 }* %_4 to i8*
|
||||||
// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S__4]])
|
// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S__4]])
|
||||||
|
|
||||||
// CHECK: [[E_b:%[0-9]+]] = bitcast { i32, i32 }** %b to i8*
|
|
||||||
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_b]])
|
|
||||||
|
|
||||||
// CHECK: [[E__4:%[0-9]+]] = bitcast { i32, i32 }* %_4 to i8*
|
// CHECK: [[E__4:%[0-9]+]] = bitcast { i32, i32 }* %_4 to i8*
|
||||||
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E__4]])
|
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E__4]])
|
||||||
|
|
||||||
|
// CHECK: [[E_b:%[0-9]+]] = bitcast { i32, i32 }** %b to i8*
|
||||||
|
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_b]])
|
||||||
}
|
}
|
||||||
|
|
||||||
let c = 1;
|
let c = 1;
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// We must mark a variable whose initialization fails due to an
|
||||||
|
// abort statement as StorageDead.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
loop {
|
||||||
|
let beacon = {
|
||||||
|
match true {
|
||||||
|
false => 4,
|
||||||
|
true => break,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
drop(&beacon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// END RUST SOURCE
|
||||||
|
// START rustc.main.mir_map.0.mir
|
||||||
|
// fn main() -> (){
|
||||||
|
// let mut _0: ();
|
||||||
|
// scope 1 {
|
||||||
|
// }
|
||||||
|
// scope 2 {
|
||||||
|
// let _2: i32;
|
||||||
|
// }
|
||||||
|
// let mut _1: ();
|
||||||
|
// let mut _3: bool;
|
||||||
|
// let mut _4: u8;
|
||||||
|
// let mut _5: !;
|
||||||
|
// let mut _6: ();
|
||||||
|
// let mut _7: &i32;
|
||||||
|
// bb0: {
|
||||||
|
// goto -> bb1;
|
||||||
|
// }
|
||||||
|
// bb1: {
|
||||||
|
// falseUnwind -> [real: bb3, cleanup: bb4];
|
||||||
|
// }
|
||||||
|
// bb2: {
|
||||||
|
// goto -> bb29;
|
||||||
|
// }
|
||||||
|
// bb3: {
|
||||||
|
// StorageLive(_2);
|
||||||
|
// StorageLive(_3);
|
||||||
|
// _3 = const true;
|
||||||
|
// _4 = discriminant(_3);
|
||||||
|
// switchInt(_3) -> [false: bb11, otherwise: bb10];
|
||||||
|
// }
|
||||||
|
// bb4: {
|
||||||
|
// resume;
|
||||||
|
// }
|
||||||
|
// bb5: {
|
||||||
|
// _2 = const 4i32;
|
||||||
|
// goto -> bb14;
|
||||||
|
// }
|
||||||
|
// bb6: {
|
||||||
|
// _0 = ();
|
||||||
|
// goto -> bb15;
|
||||||
|
// }
|
||||||
|
// bb7: {
|
||||||
|
// falseEdges -> [real: bb12, imaginary: bb8];
|
||||||
|
// }
|
||||||
|
// bb8: {
|
||||||
|
// falseEdges -> [real: bb13, imaginary: bb9];
|
||||||
|
// }
|
||||||
|
// bb9: {
|
||||||
|
// unreachable;
|
||||||
|
// }
|
||||||
|
// bb10: {
|
||||||
|
// goto -> bb8;
|
||||||
|
// }
|
||||||
|
// bb11: {
|
||||||
|
// goto -> bb7;
|
||||||
|
// }
|
||||||
|
// bb12: {
|
||||||
|
// goto -> bb5;
|
||||||
|
// }
|
||||||
|
// bb13: {
|
||||||
|
// goto -> bb6;
|
||||||
|
// }
|
||||||
|
// bb14: {
|
||||||
|
// StorageDead(_3);
|
||||||
|
// StorageLive(_7);
|
||||||
|
// _7 = &_2;
|
||||||
|
// _6 = const std::mem::drop(move _7) -> [return: bb28, unwind: bb4];
|
||||||
|
// }
|
||||||
|
// bb15: {
|
||||||
|
// goto -> bb16;
|
||||||
|
// }
|
||||||
|
// bb16: {
|
||||||
|
// goto -> bb17;
|
||||||
|
// }
|
||||||
|
// bb17: {
|
||||||
|
// goto -> bb18;
|
||||||
|
// }
|
||||||
|
// bb18: {
|
||||||
|
// goto -> bb19;
|
||||||
|
// }
|
||||||
|
// bb19: {
|
||||||
|
// goto -> bb20;
|
||||||
|
// }
|
||||||
|
// bb20: {
|
||||||
|
// StorageDead(_3);
|
||||||
|
// goto -> bb21;
|
||||||
|
// }
|
||||||
|
// bb21: {
|
||||||
|
// goto -> bb22;
|
||||||
|
// }
|
||||||
|
// bb22: {
|
||||||
|
// StorageDead(_2);
|
||||||
|
// goto -> bb23;
|
||||||
|
// }
|
||||||
|
// bb23: {
|
||||||
|
// goto -> bb24;
|
||||||
|
// }
|
||||||
|
// bb24: {
|
||||||
|
// goto -> bb25;
|
||||||
|
// }
|
||||||
|
// bb25: {
|
||||||
|
// goto -> bb2;
|
||||||
|
// }
|
||||||
|
// bb26: {
|
||||||
|
// _5 = ();
|
||||||
|
// unreachable;
|
||||||
|
// }
|
||||||
|
// bb27: {
|
||||||
|
// StorageDead(_5);
|
||||||
|
// goto -> bb14;
|
||||||
|
// }
|
||||||
|
// bb28: {
|
||||||
|
// StorageDead(_7);
|
||||||
|
// _1 = ();
|
||||||
|
// StorageDead(_2);
|
||||||
|
// goto -> bb1;
|
||||||
|
// }
|
||||||
|
// bb29: {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// END rustc.main.mir_map.0.mir
|
|
@ -31,8 +31,8 @@ fn main() {
|
||||||
// StorageDead(_5);
|
// StorageDead(_5);
|
||||||
// _3 = &_4;
|
// _3 = &_4;
|
||||||
// _2 = ();
|
// _2 = ();
|
||||||
// StorageDead(_3);
|
|
||||||
// StorageDead(_4);
|
// StorageDead(_4);
|
||||||
|
// StorageDead(_3);
|
||||||
// StorageLive(_6);
|
// StorageLive(_6);
|
||||||
// _6 = const 1i32;
|
// _6 = const 1i32;
|
||||||
// _0 = ();
|
// _0 = ();
|
||||||
|
|
Loading…
Reference in New Issue