Skip to content

Commit ac4f612

Browse files
committed
Emit StorageDead for all locals
Some errors in tests now get suppressed as a side-effect of this change. We should discuss in review whether we are okay with this.
1 parent b054710 commit ac4f612

File tree

5 files changed

+85
-161
lines changed

5 files changed

+85
-161
lines changed

Diff for: src/librustc_mir/build/scope.rs

+81-57
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ use rustc::middle::region;
8383
use rustc::ty::Ty;
8484
use rustc::hir;
8585
use rustc::mir::*;
86-
use syntax_pos::{Span};
86+
use syntax_pos::{Span, DUMMY_SP};
8787
use rustc_data_structures::fx::FxHashMap;
8888
use std::collections::hash_map::Entry;
89+
use std::mem;
8990

9091
#[derive(Debug)]
9192
pub struct Scope<'tcx> {
@@ -98,16 +99,10 @@ pub struct Scope<'tcx> {
9899
/// the span of that region_scope
99100
region_scope_span: Span,
100101

101-
/// Whether there's anything to do for the cleanup path, that is,
102+
/// Whether we need landing pads for the cleanup path, that is,
102103
/// when unwinding through this scope. This includes destructors,
103-
/// but not StorageDead statements, which don't get emitted at all
104-
/// for unwinding, for several reasons:
105-
/// * clang doesn't emit llvm.lifetime.end for C++ unwinding
106-
/// * LLVM's memory dependency analysis can't handle it atm
107-
/// * polluting the cleanup MIR with StorageDead creates
108-
/// landing pads even though there's no actual destructors
109-
/// * freeing up stack space has no effect during unwinding
110-
needs_cleanup: bool,
104+
/// but not StorageDead statements.
105+
cleanup_may_panic: bool,
111106

112107
/// set of places to drop when exiting this scope. This starts
113108
/// out empty but grows as variables are declared during the
@@ -348,7 +343,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
348343
source_scope: vis_scope,
349344
region_scope: region_scope.0,
350345
region_scope_span: region_scope.1.span,
351-
needs_cleanup: false,
346+
cleanup_may_panic: false,
352347
drops: vec![],
353348
cached_generator_drop: None,
354349
cached_exits: Default::default(),
@@ -411,7 +406,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
411406

412407
// If we are emitting a `drop` statement, we need to have the cached
413408
// diverge cleanup pads ready in case that drop panics.
414-
let may_panic = self.scopes[(len - scope_count)..].iter().any(|s| s.needs_cleanup);
409+
let may_panic = self.scopes[(len - scope_count)..].iter().any(|s| s.cleanup_may_panic);
415410
if may_panic {
416411
self.diverge_cleanup();
417412
}
@@ -466,10 +461,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
466461
/// This path terminates in GeneratorDrop. Returns the start of the path.
467462
/// None indicates there’s no cleanup to do at this point.
468463
pub fn generator_drop_cleanup(&mut self) -> Option<BasicBlock> {
469-
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
470-
return None;
471-
}
472-
473464
// Fill in the cache for unwinds
474465
self.diverge_cleanup_gen(true);
475466

@@ -480,10 +471,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
480471
let result = block;
481472

482473
while let Some(scope) = scopes.next() {
483-
if !scope.needs_cleanup {
484-
continue;
485-
}
486-
487474
block = if let Some(b) = scope.cached_generator_drop {
488475
self.cfg.terminate(block, src_info,
489476
TerminatorKind::Goto { target: b });
@@ -623,7 +610,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
623610

624611
// Schedule an abort block - this is used for some ABIs that cannot unwind
625612
pub fn schedule_abort(&mut self) -> BasicBlock {
626-
self.scopes[0].needs_cleanup = true;
613+
self.scopes[0].cleanup_may_panic = true;
627614
let abortblk = self.cfg.start_new_cleanup_block();
628615
let source_info = self.scopes[0].source_info(self.fn_span);
629616
self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort);
@@ -735,7 +722,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
735722
scope.invalidate_cache(!needs_drop, this_scope);
736723
if this_scope {
737724
if let DropKind::Value { .. } = drop_kind {
738-
scope.needs_cleanup = true;
725+
scope.cleanup_may_panic = true;
739726
}
740727

741728
let region_scope_span = region_scope.span(self.hir.tcx(),
@@ -900,12 +887,6 @@ fn build_scope_drops<'tcx>(
900887
// drops panic (panicking while unwinding will abort, so there's no need for
901888
// another set of arrows). The drops for the unwind path should have already
902889
// been generated by `diverge_cleanup_gen`.
903-
//
904-
// The code in this function reads from right to left.
905-
// Storage dead drops have to be done left to right (since we can only push
906-
// to the end of a Vec). So, we find the next drop and then call
907-
// push_storage_deads which will iterate backwards through them so that
908-
// they are added in the correct order.
909890

910891
let mut unwind_blocks = scope.drops.iter().rev().filter_map(|drop_data| {
911892
if let DropKind::Value { cached_block } = drop_data.kind {
@@ -936,11 +917,6 @@ fn build_scope_drops<'tcx>(
936917
block = next;
937918
}
938919
DropKind::Storage => {
939-
// We do not need to emit StorageDead for generator drops
940-
if generator_drop {
941-
continue
942-
}
943-
944920
// Drop the storage for both value and storage drops.
945921
// Only temps and vars need their storage dead.
946922
match drop_data.location {
@@ -958,12 +934,12 @@ fn build_scope_drops<'tcx>(
958934
block.unit()
959935
}
960936

961-
fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
962-
span: Span,
963-
scope: &mut Scope<'tcx>,
964-
mut target: BasicBlock,
965-
generator_drop: bool)
966-
-> BasicBlock
937+
fn build_diverge_scope(cfg: &mut CFG<'tcx>,
938+
span: Span,
939+
scope: &mut Scope<'tcx>,
940+
mut target: BasicBlock,
941+
generator_drop: bool)
942+
-> BasicBlock
967943
{
968944
// Build up the drops in **reverse** order. The end result will
969945
// look like:
@@ -981,7 +957,13 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
981957
scope: source_scope
982958
};
983959

984-
// Next, build up the drops. Here we iterate the vector in
960+
// We keep track of StorageDead statements to prepend to our current block
961+
// and store them here, in reverse order.
962+
let mut storage_deads = vec![];
963+
964+
let mut target_built_by_us = false;
965+
966+
// Build up the drops. Here we iterate the vector in
985967
// *forward* order, so that we generate drops[0] first (right to
986968
// left in diagram above).
987969
for (j, drop_data) in scope.drops.iter_mut().enumerate() {
@@ -994,28 +976,70 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
994976
// frozen stack)? In particular, the intent may have been to
995977
// match the behavior of clang, but on inspection eddyb says
996978
// this is not what clang does.
997-
let cached_block = match drop_data.kind {
998-
DropKind::Value { ref mut cached_block } => cached_block.ref_mut(generator_drop),
999-
DropKind::Storage => continue
1000-
};
1001-
target = if let Some(cached_block) = *cached_block {
1002-
cached_block
1003-
} else {
1004-
let block = cfg.start_new_cleanup_block();
1005-
cfg.terminate(block, source_info(drop_data.span),
1006-
TerminatorKind::Drop {
1007-
location: drop_data.location.clone(),
1008-
target,
1009-
unwind: None
1010-
});
1011-
*cached_block = Some(block);
1012-
block
979+
match drop_data.kind {
980+
DropKind::Storage => {
981+
// Only temps and vars need their storage dead.
982+
match drop_data.location {
983+
Place::Base(PlaceBase::Local(index)) => {
984+
storage_deads.push(Statement {
985+
source_info: source_info(drop_data.span),
986+
kind: StatementKind::StorageDead(index)
987+
});
988+
}
989+
_ => unreachable!(),
990+
};
991+
}
992+
DropKind::Value { ref mut cached_block } => {
993+
let cached_block = cached_block.ref_mut(generator_drop);
994+
target = if let Some(cached_block) = *cached_block {
995+
storage_deads.clear();
996+
target_built_by_us = false;
997+
cached_block
998+
} else {
999+
push_storage_deads(
1000+
cfg, &mut target, &mut storage_deads, target_built_by_us, source_scope);
1001+
let block = cfg.start_new_cleanup_block();
1002+
cfg.terminate(block, source_info(drop_data.span),
1003+
TerminatorKind::Drop {
1004+
location: drop_data.location.clone(),
1005+
target,
1006+
unwind: None
1007+
});
1008+
*cached_block = Some(block);
1009+
target_built_by_us = true;
1010+
block
1011+
};
1012+
}
10131013
};
10141014
}
1015-
1015+
push_storage_deads(cfg, &mut target, &mut storage_deads, target_built_by_us, source_scope);
10161016
*scope.cached_unwind.ref_mut(generator_drop) = Some(target);
10171017

1018+
assert!(storage_deads.is_empty());
10181019
debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target);
10191020

10201021
target
10211022
}
1023+
1024+
fn push_storage_deads(cfg: &mut CFG<'tcx>,
1025+
target: &mut BasicBlock,
1026+
storage_deads: &mut Vec<Statement<'tcx>>,
1027+
target_built_by_us: bool,
1028+
source_scope: SourceScope) {
1029+
if storage_deads.is_empty() { return; }
1030+
if !target_built_by_us {
1031+
// We cannot add statements to an existing block, so we create a new
1032+
// block for our StorageDead statements.
1033+
let block = cfg.start_new_cleanup_block();
1034+
let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope };
1035+
cfg.terminate(block, source_info, TerminatorKind::Goto { target: *target });
1036+
*target = block;
1037+
}
1038+
let statements = &mut cfg.block_data_mut(*target).statements;
1039+
storage_deads.reverse();
1040+
debug!("push_storage_deads({:?}), storage_deads={:?}, statements={:?}",
1041+
*target, storage_deads, statements);
1042+
storage_deads.append(statements);
1043+
mem::swap(statements, storage_deads);
1044+
assert!(storage_deads.is_empty());
1045+
}

Diff for: src/test/ui/dropck/dropck_trait_cycle_checked.stderr

+1-36
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,6 @@ LL | o1.set1(&o3);
2121
LL | }
2222
| - `o3` dropped here while still borrowed
2323

24-
error[E0597]: `o2` does not live long enough
25-
--> $DIR/dropck_trait_cycle_checked.rs:113:13
26-
|
27-
LL | let (o1, o2, o3): (Box<Obj>, Box<Obj>, Box<Obj>) = (O::new(), O::new(), O::new());
28-
| -------- cast requires that `o2` is borrowed for `'static`
29-
...
30-
LL | o2.set0(&o2);
31-
| ^^^ borrowed value does not live long enough
32-
...
33-
LL | }
34-
| - `o2` dropped here while still borrowed
35-
36-
error[E0597]: `o3` does not live long enough
37-
--> $DIR/dropck_trait_cycle_checked.rs:114:13
38-
|
39-
LL | let (o1, o2, o3): (Box<Obj>, Box<Obj>, Box<Obj>) = (O::new(), O::new(), O::new());
40-
| -------- cast requires that `o3` is borrowed for `'static`
41-
...
42-
LL | o2.set1(&o3);
43-
| ^^^ borrowed value does not live long enough
44-
...
45-
LL | }
46-
| - `o3` dropped here while still borrowed
47-
4824
error[E0597]: `o1` does not live long enough
4925
--> $DIR/dropck_trait_cycle_checked.rs:115:13
5026
|
@@ -57,17 +33,6 @@ LL | o3.set1(&o2);
5733
LL | }
5834
| - `o1` dropped here while still borrowed
5935

60-
error[E0597]: `o2` does not live long enough
61-
--> $DIR/dropck_trait_cycle_checked.rs:116:13
62-
|
63-
LL | let (o1, o2, o3): (Box<Obj>, Box<Obj>, Box<Obj>) = (O::new(), O::new(), O::new());
64-
| -------- cast requires that `o2` is borrowed for `'static`
65-
...
66-
LL | o3.set1(&o2);
67-
| ^^^ borrowed value does not live long enough
68-
LL | }
69-
| - `o2` dropped here while still borrowed
70-
71-
error: aborting due to 6 previous errors
36+
error: aborting due to 3 previous errors
7237

7338
For more information about this error, try `rustc --explain E0597`.

Diff for: src/test/ui/nll/user-annotations/constant-in-expr-inherent-2.stderr

+1-36
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,6 @@ LL | FUN(&x);
1010
LL | }
1111
| - `x` dropped here while still borrowed
1212

13-
error[E0597]: `x` does not live long enough
14-
--> $DIR/constant-in-expr-inherent-2.rs:26:23
15-
|
16-
LL | A::ASSOCIATED_FUN(&x);
17-
| ------------------^^-
18-
| | |
19-
| | borrowed value does not live long enough
20-
| argument requires that `x` is borrowed for `'static`
21-
...
22-
LL | }
23-
| - `x` dropped here while still borrowed
24-
25-
error[E0597]: `x` does not live long enough
26-
--> $DIR/constant-in-expr-inherent-2.rs:27:28
27-
|
28-
LL | B::ALSO_ASSOCIATED_FUN(&x);
29-
| -----------------------^^-
30-
| | |
31-
| | borrowed value does not live long enough
32-
| argument requires that `x` is borrowed for `'static`
33-
LL | <_>::TRAIT_ASSOCIATED_FUN(&x);
34-
LL | }
35-
| - `x` dropped here while still borrowed
36-
37-
error[E0597]: `x` does not live long enough
38-
--> $DIR/constant-in-expr-inherent-2.rs:28:31
39-
|
40-
LL | <_>::TRAIT_ASSOCIATED_FUN(&x);
41-
| --------------------------^^-
42-
| | |
43-
| | borrowed value does not live long enough
44-
| argument requires that `x` is borrowed for `'static`
45-
LL | }
46-
| - `x` dropped here while still borrowed
47-
48-
error: aborting due to 4 previous errors
13+
error: aborting due to previous error
4914

5015
For more information about this error, try `rustc --explain E0597`.

Diff for: src/test/ui/nll/user-annotations/method-ufcs-inherent-2.stderr

+1-16
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,6 @@ LL | let x = A::<'a>::new::<&'a u32>(&v, &v);
1313
LL | }
1414
| - `v` dropped here while still borrowed
1515

16-
error[E0597]: `v` does not live long enough
17-
--> $DIR/method-ufcs-inherent-2.rs:16:41
18-
|
19-
LL | fn foo<'a>() {
20-
| -- lifetime `'a` defined here
21-
LL | let v = 22;
22-
LL | let x = A::<'a>::new::<&'a u32>(&v, &v);
23-
| ----------------------------^^-
24-
| | |
25-
| | borrowed value does not live long enough
26-
| argument requires that `v` is borrowed for `'a`
27-
...
28-
LL | }
29-
| - `v` dropped here while still borrowed
30-
31-
error: aborting due to 2 previous errors
16+
error: aborting due to previous error
3217

3318
For more information about this error, try `rustc --explain E0597`.

Diff for: src/test/ui/nll/user-annotations/method-ufcs-inherent-4.stderr

+1-16
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,6 @@ LL | let x = <A<'a>>::new::<&'a u32>(&v, &v);
1313
LL | }
1414
| - `v` dropped here while still borrowed
1515

16-
error[E0597]: `v` does not live long enough
17-
--> $DIR/method-ufcs-inherent-4.rs:17:41
18-
|
19-
LL | fn foo<'a>() {
20-
| -- lifetime `'a` defined here
21-
LL | let v = 22;
22-
LL | let x = <A<'a>>::new::<&'a u32>(&v, &v);
23-
| ----------------------------^^-
24-
| | |
25-
| | borrowed value does not live long enough
26-
| argument requires that `v` is borrowed for `'a`
27-
...
28-
LL | }
29-
| - `v` dropped here while still borrowed
30-
31-
error: aborting due to 2 previous errors
16+
error: aborting due to previous error
3217

3318
For more information about this error, try `rustc --explain E0597`.

0 commit comments

Comments
 (0)