Skip to content

Commit 3bcda48

Browse files
committed
Auto merge of #47802 - bobtwinkles:loop_false_edge, r=nikomatsakis
[NLL] Add false edges out of infinite loops Resolves #46036 by adding a `cleanup` member to the `FalseEdges` terminator kind. There's also a small doc fix to one of the other comments in `into.rs` which I can pull out in to another PR if desired =) This PR should pass CI but the test suite has been relatively unstable on my system so I'm not 100% sure. r? @nikomatsakis
2 parents 02537fb + 85dfa9d commit 3bcda48

File tree

29 files changed

+261
-74
lines changed

29 files changed

+261
-74
lines changed

src/librustc/ich/impls_mir.rs

+4
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ for mir::TerminatorKind<'gcx> {
219219
target.hash_stable(hcx, hasher);
220220
}
221221
}
222+
mir::TerminatorKind::FalseUnwind { ref real_target, ref unwind } => {
223+
real_target.hash_stable(hcx, hasher);
224+
unwind.hash_stable(hcx, hasher);
225+
}
222226
}
223227
}
224228
}

src/librustc/mir/mod.rs

+34-5
Original file line numberDiff line numberDiff line change
@@ -816,9 +816,28 @@ pub enum TerminatorKind<'tcx> {
816816
/// Indicates the end of the dropping of a generator
817817
GeneratorDrop,
818818

819+
/// A block where control flow only ever takes one real path, but borrowck
820+
/// needs to be more conservative.
819821
FalseEdges {
822+
/// The target normal control flow will take
820823
real_target: BasicBlock,
821-
imaginary_targets: Vec<BasicBlock>
824+
/// The list of blocks control flow could conceptually take, but won't
825+
/// in practice
826+
imaginary_targets: Vec<BasicBlock>,
827+
},
828+
/// A terminator for blocks that only take one path in reality, but where we
829+
/// reserve the right to unwind in borrowck, even if it won't happen in practice.
830+
/// This can arise in infinite loops with no function calls for example.
831+
FalseUnwind {
832+
/// The target normal control flow will take
833+
real_target: BasicBlock,
834+
/// The imaginary cleanup block link. This particular path will never be taken
835+
/// in practice, but in order to avoid fragility we want to always
836+
/// consider it in borrowck. We don't want to accept programs which
837+
/// pass borrowck only when panic=abort or some assertions are disabled
838+
/// due to release vs. debug mode builds. This needs to be an Option because
839+
/// of the remove_noop_landing_pads and no_landing_pads passes
840+
unwind: Option<BasicBlock>,
822841
},
823842
}
824843

@@ -878,6 +897,8 @@ impl<'tcx> TerminatorKind<'tcx> {
878897
s.extend_from_slice(imaginary_targets);
879898
s.into_cow()
880899
}
900+
FalseUnwind { real_target: t, unwind: Some(u) } => vec![t, u].into_cow(),
901+
FalseUnwind { real_target: ref t, unwind: None } => slice::from_ref(t).into_cow(),
881902
}
882903
}
883904

@@ -910,6 +931,8 @@ impl<'tcx> TerminatorKind<'tcx> {
910931
s.extend(imaginary_targets.iter_mut());
911932
s
912933
}
934+
FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => vec![t, u],
935+
FalseUnwind { ref mut real_target, unwind: None } => vec![real_target],
913936
}
914937
}
915938

@@ -929,7 +952,8 @@ impl<'tcx> TerminatorKind<'tcx> {
929952
TerminatorKind::Call { cleanup: ref mut unwind, .. } |
930953
TerminatorKind::Assert { cleanup: ref mut unwind, .. } |
931954
TerminatorKind::DropAndReplace { ref mut unwind, .. } |
932-
TerminatorKind::Drop { ref mut unwind, .. } => {
955+
TerminatorKind::Drop { ref mut unwind, .. } |
956+
TerminatorKind::FalseUnwind { ref mut unwind, .. } => {
933957
Some(unwind)
934958
}
935959
}
@@ -1058,7 +1082,8 @@ impl<'tcx> TerminatorKind<'tcx> {
10581082

10591083
write!(fmt, ")")
10601084
},
1061-
FalseEdges { .. } => write!(fmt, "falseEdges")
1085+
FalseEdges { .. } => write!(fmt, "falseEdges"),
1086+
FalseUnwind { .. } => write!(fmt, "falseUnwind"),
10621087
}
10631088
}
10641089

@@ -1100,6 +1125,8 @@ impl<'tcx> TerminatorKind<'tcx> {
11001125
l.resize(imaginary_targets.len() + 1, "imaginary".into());
11011126
l
11021127
}
1128+
FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
1129+
FalseUnwind { unwind: None, .. } => vec!["real".into()],
11031130
}
11041131
}
11051132
}
@@ -2202,7 +2229,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
22022229
Return => Return,
22032230
Unreachable => Unreachable,
22042231
FalseEdges { real_target, ref imaginary_targets } =>
2205-
FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() }
2232+
FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() },
2233+
FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
22062234
};
22072235
Terminator {
22082236
source_info: self.source_info,
@@ -2244,7 +2272,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
22442272
Return |
22452273
GeneratorDrop |
22462274
Unreachable |
2247-
FalseEdges { .. } => false
2275+
FalseEdges { .. } |
2276+
FalseUnwind { .. } => false
22482277
}
22492278
}
22502279
}

src/librustc/mir/visit.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -495,15 +495,21 @@ macro_rules! make_mir_visitor {
495495
self.visit_operand(value, source_location);
496496
self.visit_branch(block, resume);
497497
drop.map(|t| self.visit_branch(block, t));
498-
499498
}
500499

501-
TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => {
500+
TerminatorKind::FalseEdges { real_target, ref imaginary_targets} => {
502501
self.visit_branch(block, real_target);
503502
for target in imaginary_targets {
504503
self.visit_branch(block, *target);
505504
}
506505
}
506+
507+
TerminatorKind::FalseUnwind { real_target, unwind } => {
508+
self.visit_branch(block, real_target);
509+
if let Some(unwind) = unwind {
510+
self.visit_branch(block, unwind);
511+
}
512+
}
507513
}
508514
}
509515

src/librustc_mir/borrow_check/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,8 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
575575
TerminatorKind::Goto { target: _ }
576576
| TerminatorKind::Abort
577577
| TerminatorKind::Unreachable
578-
| TerminatorKind::FalseEdges { .. } => {
578+
| TerminatorKind::FalseEdges { real_target: _, imaginary_targets: _ }
579+
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
579580
// no data used, thus irrelevant to borrowck
580581
}
581582
}

src/librustc_mir/borrow_check/nll/type_check/mod.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
796796
| TerminatorKind::GeneratorDrop
797797
| TerminatorKind::Unreachable
798798
| TerminatorKind::Drop { .. }
799-
| TerminatorKind::FalseEdges { .. } => {
799+
| TerminatorKind::FalseEdges { .. }
800+
| TerminatorKind::FalseUnwind { .. } => {
800801
// no checks needed for these
801802
}
802803

@@ -1152,6 +1153,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
11521153
self.assert_iscleanup(mir, block_data, *target, is_cleanup);
11531154
}
11541155
}
1156+
TerminatorKind::FalseUnwind {
1157+
real_target,
1158+
unwind
1159+
} => {
1160+
self.assert_iscleanup(mir, block_data, real_target, is_cleanup);
1161+
if let Some(unwind) = unwind {
1162+
if is_cleanup {
1163+
span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind");
1164+
}
1165+
self.assert_iscleanup(mir, block_data, unwind, true);
1166+
}
1167+
}
11551168
}
11561169
}
11571170

src/librustc_mir/build/expr/into.rs

+24-14
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
104104
// Or:
105105
//
106106
// [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
107-
// | | (false)
108-
// +----------true------------+-------------------> [false_block]
107+
// | (true) | (false)
108+
// [true_block] [false_block]
109109

110110
let (true_block, false_block, mut else_block, join_block) =
111111
(this.cfg.start_new_block(), this.cfg.start_new_block(),
@@ -147,20 +147,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
147147
join_block.unit()
148148
}
149149
ExprKind::Loop { condition: opt_cond_expr, body } => {
150-
// [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
151-
// ^ |
152-
// | 0
153-
// | |
154-
// | v
155-
// [body_block_end] <~~~ [body_block]
150+
// [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
151+
// ^ |
152+
// | 0
153+
// | |
154+
// | v
155+
// [body_block_end] <-/eval. body/-- [body_block]
156156
//
157157
// If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
158158
//
159-
// [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block]
160-
// ^ |
161-
// | |
162-
// +--------------------------+
163-
//
159+
// [block]
160+
// |
161+
// [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
162+
// | ^ |
163+
// false link | |
164+
// | +-----------------------------------------+
165+
// +-> [diverge_cleanup]
166+
// The false link is required to make sure borrowck considers unwinds through the
167+
// body, even when the exact code in the body cannot unwind
164168

165169
let loop_block = this.cfg.start_new_block();
166170
let exit_block = this.cfg.start_new_block();
@@ -188,7 +192,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
188192
// always `()` anyway
189193
this.cfg.push_assign_unit(exit_block, source_info, destination);
190194
} else {
191-
body_block = loop_block;
195+
body_block = this.cfg.start_new_block();
196+
let diverge_cleanup = this.diverge_cleanup();
197+
this.cfg.terminate(loop_block, source_info,
198+
TerminatorKind::FalseUnwind {
199+
real_target: body_block,
200+
unwind: Some(diverge_cleanup)
201+
})
192202
}
193203

194204
// The “return” value of the loop body must always be an unit. We therefore

src/librustc_mir/build/matches/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
728728
TerminatorKind::FalseEdges {
729729
real_target: block,
730730
imaginary_targets:
731-
vec![candidate.next_candidate_pre_binding_block]});
731+
vec![candidate.next_candidate_pre_binding_block],
732+
});
732733

733734
self.bind_matched_candidate(block, candidate.bindings);
734735

@@ -749,7 +750,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
749750
TerminatorKind::FalseEdges {
750751
real_target: otherwise,
751752
imaginary_targets:
752-
vec![candidate.next_candidate_pre_binding_block] });
753+
vec![candidate.next_candidate_pre_binding_block],
754+
});
753755
Some(otherwise)
754756
} else {
755757
self.cfg.terminate(block, candidate_source_info,

src/librustc_mir/dataflow/impls/borrows.rs

+1
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
517517
mir::TerminatorKind::Yield {..} |
518518
mir::TerminatorKind::Goto {..} |
519519
mir::TerminatorKind::FalseEdges {..} |
520+
mir::TerminatorKind::FalseUnwind {..} |
520521
mir::TerminatorKind::Unreachable => {}
521522
}
522523
}

src/librustc_mir/dataflow/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,14 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
864864
self.propagate_bits_into_entry_set_for(in_out, changed, target);
865865
}
866866
}
867+
mir::TerminatorKind::FalseUnwind { ref real_target, unwind } => {
868+
self.propagate_bits_into_entry_set_for(in_out, changed, real_target);
869+
if let Some(ref unwind) = unwind {
870+
if !self.dead_unwinds.contains(&bb) {
871+
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
872+
}
873+
}
874+
}
867875
}
868876
}
869877

src/librustc_mir/dataflow/move_paths/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
346346
TerminatorKind::Abort |
347347
TerminatorKind::GeneratorDrop |
348348
TerminatorKind::FalseEdges { .. } |
349+
TerminatorKind::FalseUnwind { .. } |
349350
TerminatorKind::Unreachable => { }
350351

351352
TerminatorKind::Return => {

src/librustc_mir/interpret/terminator/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
165165
Resume => unimplemented!(),
166166
Abort => unimplemented!(),
167167
FalseEdges { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"),
168+
FalseUnwind { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"),
168169
Unreachable => return err!(Unreachable),
169170
}
170171

src/librustc_mir/monomorphize/collector.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
636636
mir::TerminatorKind::Assert { .. } => {}
637637
mir::TerminatorKind::GeneratorDrop |
638638
mir::TerminatorKind::Yield { .. } |
639-
mir::TerminatorKind::FalseEdges { .. } => bug!(),
639+
mir::TerminatorKind::FalseEdges { .. } |
640+
mir::TerminatorKind::FalseUnwind { .. } => bug!(),
640641
}
641642

642643
self.super_terminator_kind(block, kind, location);

src/librustc_mir/transform/check_unsafety.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
7676
TerminatorKind::Abort |
7777
TerminatorKind::Return |
7878
TerminatorKind::Unreachable |
79-
TerminatorKind::FalseEdges { .. } => {
79+
TerminatorKind::FalseEdges { .. } |
80+
TerminatorKind::FalseUnwind { .. } => {
8081
// safe (at least as emitted during MIR construction)
8182
}
8283

src/librustc_mir/transform/inline.rs

+3
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
813813
*target = self.update_target(*target);
814814
}
815815
}
816+
TerminatorKind::FalseUnwind { real_target: _ , unwind: _ } =>
817+
// see the ordering of passes in the optimized_mir query.
818+
bug!("False unwinds should have been removed before inlining")
816819
}
817820
}
818821

src/librustc_mir/transform/qualify_consts.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
329329
TerminatorKind::GeneratorDrop |
330330
TerminatorKind::Yield { .. } |
331331
TerminatorKind::Unreachable |
332-
TerminatorKind::FalseEdges { .. } => None,
332+
TerminatorKind::FalseEdges { .. } |
333+
TerminatorKind::FalseUnwind { .. } => None,
333334

334335
TerminatorKind::Return => {
335336
// Check for unused values. This usually means

src/librustc_mir/transform/remove_noop_landing_pads.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ impl RemoveNoopLandingPads {
7575
TerminatorKind::Goto { .. } |
7676
TerminatorKind::Resume |
7777
TerminatorKind::SwitchInt { .. } |
78-
TerminatorKind::FalseEdges { .. } => {
78+
TerminatorKind::FalseEdges { .. } |
79+
TerminatorKind::FalseUnwind { .. } => {
7980
terminator.successors().iter().all(|succ| {
8081
nop_landing_pads.contains(succ.index())
8182
})

src/librustc_mir/transform/simplify_branches.rs

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ impl MirPass for SimplifyBranches {
6464
TerminatorKind::FalseEdges { real_target, .. } => {
6565
TerminatorKind::Goto { target: real_target }
6666
},
67+
TerminatorKind::FalseUnwind { real_target, .. } => {
68+
TerminatorKind::Goto { target: real_target }
69+
},
6770
_ => continue
6871
};
6972
}

src/librustc_passes/mir_stats.rs

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
123123
TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop",
124124
TerminatorKind::Yield { .. } => "TerminatorKind::Yield",
125125
TerminatorKind::FalseEdges { .. } => "TerminatorKind::FalseEdges",
126+
TerminatorKind::FalseUnwind { .. } => "TerminatorKind::FalseUnwind",
126127
}, kind);
127128
self.super_terminator_kind(block, kind, location);
128129
}

src/librustc_trans/mir/analyze.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock
242242
TerminatorKind::Unreachable |
243243
TerminatorKind::SwitchInt { .. } |
244244
TerminatorKind::Yield { .. } |
245-
TerminatorKind::FalseEdges { .. } => {
245+
TerminatorKind::FalseEdges { .. } |
246+
TerminatorKind::FalseUnwind { .. } => {
246247
/* nothing to do */
247248
}
248249
TerminatorKind::Call { cleanup: unwind, .. } |

src/librustc_trans/mir/block.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -608,8 +608,9 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
608608
cleanup);
609609
}
610610
mir::TerminatorKind::GeneratorDrop |
611-
mir::TerminatorKind::Yield { .. } |
612-
mir::TerminatorKind::FalseEdges { .. } => bug!("generator ops in trans"),
611+
mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"),
612+
mir::TerminatorKind::FalseEdges { .. } |
613+
mir::TerminatorKind::FalseUnwind { .. } => bug!("borrowck false edges in trans"),
613614
}
614615
}
615616

0 commit comments

Comments
 (0)