Skip to content

Commit 036469c

Browse files
committed
Auto merge of #46320 - arielb1:always-resume, r=nikomatsakis
Always unwind through a Resume and other fixes Should fix most of the small MIR borrowck issues. r? @nikomatsakis
2 parents 16ba459 + 6594799 commit 036469c

35 files changed

+542
-311
lines changed

src/librustc/mir/mod.rs

+25
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,10 @@ impl<'tcx> Terminator<'tcx> {
733733
pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> {
734734
self.kind.successors_mut()
735735
}
736+
737+
pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
738+
self.kind.unwind_mut()
739+
}
736740
}
737741

738742
impl<'tcx> TerminatorKind<'tcx> {
@@ -811,6 +815,27 @@ impl<'tcx> TerminatorKind<'tcx> {
811815
}
812816
}
813817
}
818+
819+
pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
820+
match *self {
821+
TerminatorKind::Goto { .. } |
822+
TerminatorKind::Resume |
823+
TerminatorKind::Return |
824+
TerminatorKind::Unreachable |
825+
TerminatorKind::GeneratorDrop |
826+
TerminatorKind::Yield { .. } |
827+
TerminatorKind::SwitchInt { .. } |
828+
TerminatorKind::FalseEdges { .. } => {
829+
None
830+
},
831+
TerminatorKind::Call { cleanup: ref mut unwind, .. } |
832+
TerminatorKind::Assert { cleanup: ref mut unwind, .. } |
833+
TerminatorKind::DropAndReplace { ref mut unwind, .. } |
834+
TerminatorKind::Drop { ref mut unwind, .. } => {
835+
Some(unwind)
836+
}
837+
}
838+
}
814839
}
815840

816841
impl<'tcx> BasicBlockData<'tcx> {

src/librustc_mir/borrow_check.rs

+72-30
Original file line numberDiff line numberDiff line change
@@ -384,33 +384,23 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
384384
// StorageDead, but we don't always emit those (notably on unwind paths),
385385
// so this "extra check" serves as a kind of backup.
386386
let domain = flow_state.borrows.base_results.operator();
387-
for borrow in domain.borrows() {
388-
let root_place = self.prefixes(
389-
&borrow.place,
390-
PrefixSet::All
391-
).last().unwrap();
392-
match root_place {
393-
Place::Static(_) => {
394-
self.access_place(
395-
ContextKind::StorageDead.new(loc),
396-
(&root_place, self.mir.source_info(borrow.location).span),
397-
(Deep, Write(WriteKind::StorageDeadOrDrop)),
398-
LocalMutationIsAllowed::Yes,
399-
flow_state
400-
);
401-
}
402-
Place::Local(_) => {
403-
self.access_place(
404-
ContextKind::StorageDead.new(loc),
405-
(&root_place, self.mir.source_info(borrow.location).span),
406-
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
407-
LocalMutationIsAllowed::Yes,
408-
flow_state
409-
);
410-
}
411-
Place::Projection(_) => ()
387+
let data = domain.borrows();
388+
flow_state.borrows.with_elems_outgoing(|borrows| for i in borrows {
389+
let borrow = &data[i];
390+
391+
if self.place_is_invalidated_at_exit(&borrow.place) {
392+
debug!("borrow conflicts at exit {:?}", borrow);
393+
let borrow_span = self.mir.source_info(borrow.location).span;
394+
// FIXME: should be talking about the region lifetime instead
395+
// of just a span here.
396+
let end_span = domain.opt_region_end_span(&borrow.region);
397+
398+
self.report_borrowed_value_does_not_live_long_enough(
399+
ContextKind::StorageDead.new(loc),
400+
(&borrow.place, borrow_span),
401+
end_span)
412402
}
413-
}
403+
});
414404
}
415405
TerminatorKind::Goto { target: _ } |
416406
TerminatorKind::Unreachable |
@@ -594,7 +584,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
594584
context, common_prefix, place_span, bk,
595585
&borrow, end_issued_loan_span)
596586
}
597-
WriteKind::StorageDeadOrDrop => {
587+
WriteKind::StorageDeadOrDrop => {
598588
let end_span =
599589
flow_state.borrows.base_results.operator().opt_region_end_span(
600590
&borrow.region);
@@ -751,6 +741,50 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
751741
Operand::Constant(_) => {}
752742
}
753743
}
744+
745+
/// Returns whether a borrow of this place is invalidated when the function
746+
/// exits
747+
fn place_is_invalidated_at_exit(&self, place: &Place<'tcx>) -> bool {
748+
debug!("place_is_invalidated_at_exit({:?})", place);
749+
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
750+
751+
// FIXME(nll-rfc#40): do more precise destructor tracking here. For now
752+
// we just know that all locals are dropped at function exit (otherwise
753+
// we'll have a memory leak) and assume that all statics have a destructor.
754+
let (might_be_alive, will_be_dropped) = match root_place {
755+
Place::Static(statik) => {
756+
// Thread-locals might be dropped after the function exits, but
757+
// "true" statics will never be.
758+
let is_thread_local = self.tcx.get_attrs(statik.def_id).iter().any(|attr| {
759+
attr.check_name("thread_local")
760+
});
761+
762+
(true, is_thread_local)
763+
}
764+
Place::Local(_) => {
765+
// Locals are always dropped at function exit, and if they
766+
// have a destructor it would've been called already.
767+
(false, true)
768+
}
769+
Place::Projection(..) => bug!("root of {:?} is a projection ({:?})?",
770+
place, root_place)
771+
};
772+
773+
if !will_be_dropped {
774+
debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place);
775+
return false;
776+
}
777+
778+
// FIXME: replace this with a proper borrow_conflicts_with_place when
779+
// that is merged.
780+
let prefix_set = if might_be_alive {
781+
PrefixSet::Supporting
782+
} else {
783+
PrefixSet::Shallow
784+
};
785+
786+
self.prefixes(place, prefix_set).any(|prefix| prefix == root_place)
787+
}
754788
}
755789

756790
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
@@ -1667,13 +1701,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16671701

16681702
fn report_borrowed_value_does_not_live_long_enough(&mut self,
16691703
_: Context,
1670-
(place, span): (&Place, Span),
1704+
(place, span): (&Place<'tcx>, Span),
16711705
end_span: Option<Span>) {
1672-
let proper_span = match *place {
1706+
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
1707+
let proper_span = match *root_place {
16731708
Place::Local(local) => self.mir.local_decls[local].source_info.span,
16741709
_ => span
16751710
};
1676-
16771711
let mut err = self.tcx.path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
16781712
err.span_label(proper_span, "temporary value created here");
16791713
err.span_label(span, "temporary value dropped here while still borrowed");
@@ -2162,4 +2196,12 @@ impl<BD> FlowInProgress<BD> where BD: BitDenotation {
21622196
let univ = self.base_results.sets().bits_per_block();
21632197
self.curr_state.elems(univ)
21642198
}
2199+
2200+
fn with_elems_outgoing<F>(&self, f: F) where F: FnOnce(indexed_set::Elems<BD::Idx>) {
2201+
let mut curr_state = self.curr_state.clone();
2202+
curr_state.union(&self.stmt_gen);
2203+
curr_state.subtract(&self.stmt_kill);
2204+
let univ = self.base_results.sets().bits_per_block();
2205+
f(curr_state.elems(univ));
2206+
}
21652207
}

src/librustc_mir/build/expr/into.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
255255
this.cfg.terminate(block, source_info, TerminatorKind::Call {
256256
func: fun,
257257
args,
258-
cleanup,
258+
cleanup: Some(cleanup),
259259
destination: if diverges {
260260
None
261261
} else {
@@ -273,7 +273,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
273273
ExprKind::Break { .. } |
274274
ExprKind::InlineAsm { .. } |
275275
ExprKind::Return {.. } => {
276-
this.stmt_expr(block, expr)
276+
unpack!(block = this.stmt_expr(block, expr));
277+
this.cfg.push_assign_unit(block, source_info, destination);
278+
block.unit()
277279
}
278280

279281
// these are the cases that are more naturally handled by some other mode

src/librustc_mir/build/matches/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
315315
}),
316316
args: vec![val, expect],
317317
destination: Some((eq_result.clone(), eq_block)),
318-
cleanup,
318+
cleanup: Some(cleanup),
319319
});
320320

321321
// check the result

src/librustc_mir/build/scope.rs

+38-27
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
383383
assert_eq!(scope.region_scope, region_scope.0);
384384

385385
self.cfg.push_end_region(self.hir.tcx(), block, region_scope.1, scope.region_scope);
386+
let resume_block = self.resume_block();
386387
unpack!(block = build_scope_drops(&mut self.cfg,
388+
resume_block,
387389
&scope,
388390
&self.scopes,
389391
block,
@@ -422,6 +424,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
422424
}
423425

424426
{
427+
let resume_block = self.resume_block();
425428
let mut rest = &mut self.scopes[(len - scope_count)..];
426429
while let Some((scope, rest_)) = {rest}.split_last_mut() {
427430
rest = rest_;
@@ -441,6 +444,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
441444
self.cfg.push_end_region(self.hir.tcx(), block, region_scope.1, scope.region_scope);
442445

443446
unpack!(block = build_scope_drops(&mut self.cfg,
447+
resume_block,
444448
scope,
445449
rest,
446450
block,
@@ -468,6 +472,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
468472
let src_info = self.scopes[0].source_info(self.fn_span);
469473
let mut block = self.cfg.start_new_block();
470474
let result = block;
475+
let resume_block = self.resume_block();
471476
let mut rest = &mut self.scopes[..];
472477

473478
while let Some((scope, rest_)) = {rest}.split_last_mut() {
@@ -491,6 +496,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
491496
self.cfg.push_end_region(self.hir.tcx(), block, src_info, scope.region_scope);
492497

493498
unpack!(block = build_scope_drops(&mut self.cfg,
499+
resume_block,
494500
scope,
495501
rest,
496502
block,
@@ -701,18 +707,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
701707
/// This path terminates in Resume. Returns the start of the path.
702708
/// See module comment for more details. None indicates there’s no
703709
/// cleanup to do at this point.
704-
pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
710+
pub fn diverge_cleanup(&mut self) -> BasicBlock {
705711
self.diverge_cleanup_gen(false)
706712
}
707713

708-
fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> Option<BasicBlock> {
709-
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
710-
return None;
714+
fn resume_block(&mut self) -> BasicBlock {
715+
if let Some(target) = self.cached_resume_block {
716+
target
717+
} else {
718+
let resumeblk = self.cfg.start_new_cleanup_block();
719+
self.cfg.terminate(resumeblk,
720+
SourceInfo {
721+
scope: ARGUMENT_VISIBILITY_SCOPE,
722+
span: self.fn_span
723+
},
724+
TerminatorKind::Resume);
725+
self.cached_resume_block = Some(resumeblk);
726+
resumeblk
711727
}
712-
assert!(!self.scopes.is_empty()); // or `any` above would be false
728+
}
729+
730+
fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock {
731+
// To start, create the resume terminator.
732+
let mut target = self.resume_block();
713733

714-
let Builder { ref mut cfg, ref mut scopes,
715-
ref mut cached_resume_block, .. } = *self;
734+
let Builder { ref mut cfg, ref mut scopes, .. } = *self;
716735

717736
// Build up the drops in **reverse** order. The end result will
718737
// look like:
@@ -725,23 +744,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
725744
// store caches. If everything is cached, we'll just walk right
726745
// to left reading the cached results but never created anything.
727746

728-
// To start, create the resume terminator.
729-
let mut target = if let Some(target) = *cached_resume_block {
730-
target
731-
} else {
732-
let resumeblk = cfg.start_new_cleanup_block();
733-
cfg.terminate(resumeblk,
734-
scopes[0].source_info(self.fn_span),
735-
TerminatorKind::Resume);
736-
*cached_resume_block = Some(resumeblk);
737-
resumeblk
738-
};
739-
740-
for scope in scopes.iter_mut() {
741-
target = build_diverge_scope(self.hir.tcx(), cfg, scope.region_scope_span,
742-
scope, target, generator_drop);
747+
if scopes.iter().any(|scope| scope.needs_cleanup) {
748+
for scope in scopes.iter_mut() {
749+
target = build_diverge_scope(self.hir.tcx(), cfg, scope.region_scope_span,
750+
scope, target, generator_drop);
751+
}
743752
}
744-
Some(target)
753+
754+
target
745755
}
746756

747757
/// Utility function for *non*-scope code to build their own drops
@@ -760,7 +770,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
760770
TerminatorKind::Drop {
761771
location,
762772
target: next_target,
763-
unwind: diverge_target,
773+
unwind: Some(diverge_target),
764774
});
765775
next_target.unit()
766776
}
@@ -779,7 +789,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
779789
location,
780790
value,
781791
target: next_target,
782-
unwind: diverge_target,
792+
unwind: Some(diverge_target),
783793
});
784794
next_target.unit()
785795
}
@@ -804,7 +814,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
804814
expected,
805815
msg,
806816
target: success_block,
807-
cleanup,
817+
cleanup: Some(cleanup),
808818
});
809819

810820
success_block
@@ -813,6 +823,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
813823

814824
/// Builds drops for pop_scope and exit_scope.
815825
fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
826+
resume_block: BasicBlock,
816827
scope: &Scope<'tcx>,
817828
earlier_scopes: &[Scope<'tcx>],
818829
mut block: BasicBlock,
@@ -868,7 +879,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
868879
cfg.terminate(block, source_info, TerminatorKind::Drop {
869880
location: drop_data.location.clone(),
870881
target: next,
871-
unwind: on_diverge
882+
unwind: Some(on_diverge.unwrap_or(resume_block))
872883
});
873884
block = next;
874885
}

src/librustc_mir/dataflow/impls/borrows.rs

+10
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
132132
&self.borrows[idx].location
133133
}
134134

135+
pub fn nonlexical_regioncx(&self) -> Option<&'a RegionInferenceContext<'tcx>> {
136+
self.nonlexical_regioncx
137+
}
138+
135139
/// Returns the span for the "end point" given region. This will
136140
/// return `None` if NLL is enabled, since that concept has no
137141
/// meaning there. Otherwise, return region span if it exists and
@@ -208,6 +212,12 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
208212
mir::StatementKind::Assign(_, ref rhs) => {
209213
if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
210214
if is_unsafe_place(self.tcx, self.mir, place) { return; }
215+
if let RegionKind::ReEmpty = region {
216+
// If the borrowed value is dead, the region for it
217+
// can be empty. Don't track the borrow in that case.
218+
return
219+
}
220+
211221
let index = self.location_map.get(&location).unwrap_or_else(|| {
212222
panic!("could not find BorrowIndex for location {:?}", location);
213223
});

0 commit comments

Comments
 (0)