about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ich/impls_mir.rs4
-rw-r--r--src/librustc/mir/mod.rs39
-rw-r--r--src/librustc/mir/visit.rs10
-rw-r--r--src/librustc_mir/borrow_check/mod.rs3
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs15
-rw-r--r--src/librustc_mir/build/expr/into.rs38
-rw-r--r--src/librustc_mir/build/matches/mod.rs6
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs1
-rw-r--r--src/librustc_mir/dataflow/mod.rs8
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs1
-rw-r--r--src/librustc_mir/interpret/terminator/mod.rs1
-rw-r--r--src/librustc_mir/monomorphize/collector.rs3
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs3
-rw-r--r--src/librustc_mir/transform/inline.rs3
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs3
-rw-r--r--src/librustc_mir/transform/remove_noop_landing_pads.rs3
-rw-r--r--src/librustc_mir/transform/simplify_branches.rs3
-rw-r--r--src/librustc_passes/mir_stats.rs1
-rw-r--r--src/librustc_trans/mir/analyze.rs3
-rw-r--r--src/librustc_trans/mir/block.rs5
-rw-r--r--src/test/compile-fail/issue-46036.rs23
-rw-r--r--src/test/incremental/hashes/loop_expressions.rs2
-rw-r--r--src/test/mir-opt/end_region_2.rs12
-rw-r--r--src/test/mir-opt/end_region_3.rs12
-rw-r--r--src/test/mir-opt/end_region_9.rs22
-rw-r--r--src/test/mir-opt/end_region_cyclic.rs27
-rw-r--r--src/test/mir-opt/issue-38669.rs19
-rw-r--r--src/test/mir-opt/loop_test.rs49
-rw-r--r--src/test/mir-opt/nll/liveness-drop-intra-block.rs16
29 files changed, 261 insertions, 74 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 03a369577a3..67b4cfb6fa7 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -219,6 +219,10 @@ for mir::TerminatorKind<'gcx> {
                     target.hash_stable(hcx, hasher);
                 }
             }
+            mir::TerminatorKind::FalseUnwind { ref real_target, ref unwind } => {
+                real_target.hash_stable(hcx, hasher);
+                unwind.hash_stable(hcx, hasher);
+            }
         }
     }
 }
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index c035d02a612..439be667861 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -816,9 +816,28 @@ pub enum TerminatorKind<'tcx> {
     /// Indicates the end of the dropping of a generator
     GeneratorDrop,
 
+    /// A block where control flow only ever takes one real path, but borrowck
+    /// needs to be more conservative.
     FalseEdges {
+        /// The target normal control flow will take
         real_target: BasicBlock,
-        imaginary_targets: Vec<BasicBlock>
+        /// The list of blocks control flow could conceptually take, but won't
+        /// in practice
+        imaginary_targets: Vec<BasicBlock>,
+    },
+    /// A terminator for blocks that only take one path in reality, but where we
+    /// reserve the right to unwind in borrowck, even if it won't happen in practice.
+    /// This can arise in infinite loops with no function calls for example.
+    FalseUnwind {
+        /// The target normal control flow will take
+        real_target: BasicBlock,
+        /// The imaginary cleanup block link. This particular path will never be taken
+        /// in practice, but in order to avoid fragility we want to always
+        /// consider it in borrowck. We don't want to accept programs which
+        /// pass borrowck only when panic=abort or some assertions are disabled
+        /// due to release vs. debug mode builds. This needs to be an Option because
+        /// of the remove_noop_landing_pads and no_landing_pads passes
+        unwind: Option<BasicBlock>,
     },
 }
 
@@ -878,6 +897,8 @@ impl<'tcx> TerminatorKind<'tcx> {
                 s.extend_from_slice(imaginary_targets);
                 s.into_cow()
             }
+            FalseUnwind { real_target: t, unwind: Some(u) } => vec![t, u].into_cow(),
+            FalseUnwind { real_target: ref t, unwind: None } => slice::from_ref(t).into_cow(),
         }
     }
 
@@ -910,6 +931,8 @@ impl<'tcx> TerminatorKind<'tcx> {
                 s.extend(imaginary_targets.iter_mut());
                 s
             }
+            FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => vec![t, u],
+            FalseUnwind { ref mut real_target, unwind: None } => vec![real_target],
         }
     }
 
@@ -929,7 +952,8 @@ impl<'tcx> TerminatorKind<'tcx> {
             TerminatorKind::Call { cleanup: ref mut unwind, .. } |
             TerminatorKind::Assert { cleanup: ref mut unwind, .. } |
             TerminatorKind::DropAndReplace { ref mut unwind, .. } |
-            TerminatorKind::Drop { ref mut unwind, .. } => {
+            TerminatorKind::Drop { ref mut unwind, .. } |
+            TerminatorKind::FalseUnwind { ref mut unwind, .. } => {
                 Some(unwind)
             }
         }
@@ -1058,7 +1082,8 @@ impl<'tcx> TerminatorKind<'tcx> {
 
                 write!(fmt, ")")
             },
-            FalseEdges { .. } => write!(fmt, "falseEdges")
+            FalseEdges { .. } => write!(fmt, "falseEdges"),
+            FalseUnwind { .. } => write!(fmt, "falseUnwind"),
         }
     }
 
@@ -1100,6 +1125,8 @@ impl<'tcx> TerminatorKind<'tcx> {
                 l.resize(imaginary_targets.len() + 1, "imaginary".into());
                 l
             }
+            FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
+            FalseUnwind { unwind: None, .. } => vec!["real".into()],
         }
     }
 }
@@ -2202,7 +2229,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
             Return => Return,
             Unreachable => Unreachable,
             FalseEdges { real_target, ref imaginary_targets } =>
-                FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() }
+                FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() },
+            FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
         };
         Terminator {
             source_info: self.source_info,
@@ -2244,7 +2272,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
             Return |
             GeneratorDrop |
             Unreachable |
-            FalseEdges { .. } => false
+            FalseEdges { .. } |
+            FalseUnwind { .. } => false
         }
     }
 }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index afaf7d41e92..0b6f1275bdb 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -495,15 +495,21 @@ macro_rules! make_mir_visitor {
                         self.visit_operand(value, source_location);
                         self.visit_branch(block, resume);
                         drop.map(|t| self.visit_branch(block, t));
-
                     }
 
-                    TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => {
+                    TerminatorKind::FalseEdges { real_target, ref imaginary_targets} => {
                         self.visit_branch(block, real_target);
                         for target in imaginary_targets {
                             self.visit_branch(block, *target);
                         }
                     }
+
+                    TerminatorKind::FalseUnwind { real_target, unwind } => {
+                        self.visit_branch(block, real_target);
+                        if let Some(unwind) = unwind {
+                            self.visit_branch(block, unwind);
+                        }
+                    }
                 }
             }
 
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 217dfdf8d41..c4df7349391 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -575,7 +575,8 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
             TerminatorKind::Goto { target: _ }
             | TerminatorKind::Abort
             | TerminatorKind::Unreachable
-            | TerminatorKind::FalseEdges { .. } => {
+            | TerminatorKind::FalseEdges { real_target: _, imaginary_targets: _ }
+            | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
                 // no data used, thus irrelevant to borrowck
             }
         }
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index c0c680a4ddc..7ca8d0bdd50 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -796,7 +796,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::Unreachable
             | TerminatorKind::Drop { .. }
-            | TerminatorKind::FalseEdges { .. } => {
+            | TerminatorKind::FalseEdges { .. }
+            | TerminatorKind::FalseUnwind { .. } => {
                 // no checks needed for these
             }
 
@@ -1152,6 +1153,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     self.assert_iscleanup(mir, block_data, *target, is_cleanup);
                 }
             }
+            TerminatorKind::FalseUnwind {
+                real_target,
+                unwind
+            } => {
+                self.assert_iscleanup(mir, block_data, real_target, is_cleanup);
+                if let Some(unwind) = unwind {
+                    if is_cleanup {
+                        span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind");
+                    }
+                    self.assert_iscleanup(mir, block_data, unwind, true);
+                }
+            }
         }
     }
 
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index 68b23d1ae17..28dc329e4fe 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -104,8 +104,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // Or:
                 //
                 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
-                //        |                          | (false)
-                //        +----------true------------+-------------------> [false_block]
+                //        | (true)                   | (false)
+                //  [true_block]               [false_block]
 
                 let (true_block, false_block, mut else_block, join_block) =
                     (this.cfg.start_new_block(), this.cfg.start_new_block(),
@@ -147,20 +147,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 join_block.unit()
             }
             ExprKind::Loop { condition: opt_cond_expr, body } => {
-                // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
-                //                  ^                  |
-                //                  |                  0
-                //                  |                  |
-                //                  |                  v
-                //           [body_block_end] <~~~ [body_block]
+                // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
+                //                  ^                               |
+                //                  |                               0
+                //                  |                               |
+                //                  |                               v
+                //           [body_block_end] <-/eval. body/-- [body_block]
                 //
                 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
                 //
-                // [block] --> [loop_block / body_block ] ~~> [body_block_end]    [exit_block]
-                //                         ^                          |
-                //                         |                          |
-                //                         +--------------------------+
-                //
+                // [block]
+                //    |
+                //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
+                //    |        ^                                         |
+                // false link  |                                         |
+                //    |        +-----------------------------------------+
+                //    +-> [diverge_cleanup]
+                // The false link is required to make sure borrowck considers unwinds through the
+                // body, even when the exact code in the body cannot unwind
 
                 let loop_block = this.cfg.start_new_block();
                 let exit_block = this.cfg.start_new_block();
@@ -188,7 +192,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             // always `()` anyway
                             this.cfg.push_assign_unit(exit_block, source_info, destination);
                         } else {
-                            body_block = loop_block;
+                            body_block = this.cfg.start_new_block();
+                            let diverge_cleanup = this.diverge_cleanup();
+                            this.cfg.terminate(loop_block, source_info,
+                                               TerminatorKind::FalseUnwind {
+                                                   real_target: body_block,
+                                                   unwind: Some(diverge_cleanup)
+                                               })
                         }
 
                         // The “return” value of the loop body must always be an unit. We therefore
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 6cb92177766..e2096bf5356 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -728,7 +728,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                TerminatorKind::FalseEdges {
                                    real_target: block,
                                    imaginary_targets:
-                                       vec![candidate.next_candidate_pre_binding_block]});
+                                       vec![candidate.next_candidate_pre_binding_block],
+                               });
 
         self.bind_matched_candidate(block, candidate.bindings);
 
@@ -749,7 +750,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                TerminatorKind::FalseEdges {
                                    real_target: otherwise,
                                    imaginary_targets:
-                                       vec![candidate.next_candidate_pre_binding_block] });
+                                       vec![candidate.next_candidate_pre_binding_block],
+                               });
             Some(otherwise)
         } else {
             self.cfg.terminate(block, candidate_source_info,
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index fe9b0b86bef..e798cc93cb0 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -517,6 +517,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
             mir::TerminatorKind::Yield {..} |
             mir::TerminatorKind::Goto {..} |
             mir::TerminatorKind::FalseEdges {..} |
+            mir::TerminatorKind::FalseUnwind {..} |
             mir::TerminatorKind::Unreachable => {}
         }
     }
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 291c22b5e1e..9c7d9b398cc 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -864,6 +864,14 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
                     self.propagate_bits_into_entry_set_for(in_out, changed, target);
                 }
             }
+            mir::TerminatorKind::FalseUnwind { ref real_target, unwind } => {
+                self.propagate_bits_into_entry_set_for(in_out, changed, real_target);
+                if let Some(ref unwind) = unwind {
+                    if !self.dead_unwinds.contains(&bb) {
+                        self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
+                    }
+                }
+            }
         }
     }
 
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index cd36282eca0..635d99e7737 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -346,6 +346,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
             TerminatorKind::Abort |
             TerminatorKind::GeneratorDrop |
             TerminatorKind::FalseEdges { .. } |
+            TerminatorKind::FalseUnwind { .. } |
             TerminatorKind::Unreachable => { }
 
             TerminatorKind::Return => {
diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs
index c8a0dbdd903..606bda51edb 100644
--- a/src/librustc_mir/interpret/terminator/mod.rs
+++ b/src/librustc_mir/interpret/terminator/mod.rs
@@ -165,6 +165,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
             Resume => unimplemented!(),
             Abort => unimplemented!(),
             FalseEdges { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"),
+            FalseUnwind { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"),
             Unreachable => return err!(Unreachable),
         }
 
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index f16187797d4..a80dfaef0da 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -636,7 +636,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
             mir::TerminatorKind::Assert { .. } => {}
             mir::TerminatorKind::GeneratorDrop |
             mir::TerminatorKind::Yield { .. } |
-            mir::TerminatorKind::FalseEdges { .. } => bug!(),
+            mir::TerminatorKind::FalseEdges { .. } |
+            mir::TerminatorKind::FalseUnwind { .. } => bug!(),
         }
 
         self.super_terminator_kind(block, kind, location);
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index ae27f54e618..bbc7803b84d 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -76,7 +76,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
             TerminatorKind::Abort |
             TerminatorKind::Return |
             TerminatorKind::Unreachable |
-            TerminatorKind::FalseEdges { .. } => {
+            TerminatorKind::FalseEdges { .. } |
+            TerminatorKind::FalseUnwind { .. } => {
                 // safe (at least as emitted during MIR construction)
             }
 
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index 15bbcad7325..08a9757fb32 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -813,6 +813,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
                     *target = self.update_target(*target);
                 }
             }
+            TerminatorKind::FalseUnwind { real_target: _ , unwind: _ } =>
+                // see the ordering of passes in the optimized_mir query.
+                bug!("False unwinds should have been removed before inlining")
         }
     }
 
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index e7bb03eefc4..11dc34717a9 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -329,7 +329,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                 TerminatorKind::GeneratorDrop |
                 TerminatorKind::Yield { .. } |
                 TerminatorKind::Unreachable |
-                TerminatorKind::FalseEdges { .. } => None,
+                TerminatorKind::FalseEdges { .. } |
+                TerminatorKind::FalseUnwind { .. } => None,
 
                 TerminatorKind::Return => {
                     // Check for unused values. This usually means
diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs
index e7cab469bc2..cd80d25c410 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -75,7 +75,8 @@ impl RemoveNoopLandingPads {
             TerminatorKind::Goto { .. } |
             TerminatorKind::Resume |
             TerminatorKind::SwitchInt { .. } |
-            TerminatorKind::FalseEdges { .. } => {
+            TerminatorKind::FalseEdges { .. } |
+            TerminatorKind::FalseUnwind { .. } => {
                 terminator.successors().iter().all(|succ| {
                     nop_landing_pads.contains(succ.index())
                 })
diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs
index 20c33bab1aa..41089f567bd 100644
--- a/src/librustc_mir/transform/simplify_branches.rs
+++ b/src/librustc_mir/transform/simplify_branches.rs
@@ -64,6 +64,9 @@ impl MirPass for SimplifyBranches {
                 TerminatorKind::FalseEdges { real_target, .. } => {
                     TerminatorKind::Goto { target: real_target }
                 },
+                TerminatorKind::FalseUnwind { real_target, .. } => {
+                    TerminatorKind::Goto { target: real_target }
+                },
                 _ => continue
             };
         }
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index b379a174b23..e4705674e22 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -123,6 +123,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
             TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop",
             TerminatorKind::Yield { .. } => "TerminatorKind::Yield",
             TerminatorKind::FalseEdges { .. } => "TerminatorKind::FalseEdges",
+            TerminatorKind::FalseUnwind { .. } => "TerminatorKind::FalseUnwind",
         }, kind);
         self.super_terminator_kind(block, kind, location);
     }
diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs
index bf82e1d50c4..f683703ce6d 100644
--- a/src/librustc_trans/mir/analyze.rs
+++ b/src/librustc_trans/mir/analyze.rs
@@ -242,7 +242,8 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock
                 TerminatorKind::Unreachable |
                 TerminatorKind::SwitchInt { .. } |
                 TerminatorKind::Yield { .. } |
-                TerminatorKind::FalseEdges { .. } => {
+                TerminatorKind::FalseEdges { .. } |
+                TerminatorKind::FalseUnwind { .. } => {
                     /* nothing to do */
                 }
                 TerminatorKind::Call { cleanup: unwind, .. } |
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index af1e30a4b19..bb2a7840fae 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -608,8 +608,9 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
                         cleanup);
             }
             mir::TerminatorKind::GeneratorDrop |
-            mir::TerminatorKind::Yield { .. } |
-            mir::TerminatorKind::FalseEdges { .. } => bug!("generator ops in trans"),
+            mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"),
+            mir::TerminatorKind::FalseEdges { .. } |
+            mir::TerminatorKind::FalseUnwind { .. } => bug!("borrowck false edges in trans"),
         }
     }
 
diff --git a/src/test/compile-fail/issue-46036.rs b/src/test/compile-fail/issue-46036.rs
new file mode 100644
index 00000000000..b5cdded4d30
--- /dev/null
+++ b/src/test/compile-fail/issue-46036.rs
@@ -0,0 +1,23 @@
+// Copyright 2017 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.
+
+// Issue 46036: [NLL] false edges on infinite loops
+// Infinite loops should create false edges to the cleanup block.
+#![feature(nll)]
+
+struct Foo { x: &'static u32 }
+
+fn foo() {
+    let a = 3;
+    let foo = Foo { x: &a }; //~ ERROR E0597
+    loop { }
+}
+
+fn main() { }
diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs
index dcb937fd867..8599f8d7f9a 100644
--- a/src/test/incremental/hashes/loop_expressions.rs
+++ b/src/test/incremental/hashes/loop_expressions.rs
@@ -179,7 +179,7 @@ pub fn change_continue_label() {
 }
 
 #[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="HirBody, TypeckTables")]
+#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, TypeckTables")]
 #[rustc_clean(cfg="cfail3")]
 pub fn change_continue_label() {
     let mut _x = 0;
diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs
index 56c3e2a38a0..d6084d5a6da 100644
--- a/src/test/mir-opt/end_region_2.rs
+++ b/src/test/mir-opt/end_region_2.rs
@@ -40,15 +40,21 @@ fn main() {
 //         goto -> bb1;
 //     }
 //     bb1: {
+//          falseUnwind -> [real: bb2, cleanup: bb3];
+//     }
+//     bb2: {
 //         StorageLive(_2);
 //         _2 = const true;
 //         StorageLive(_3);
 //         _3 = &'23_1rs _2;
 //         StorageLive(_5);
 //         _5 = _2;
-//         switchInt(move _5) -> [0u8: bb3, otherwise: bb2];
+//         switchInt(move _5) -> [0u8: bb5, otherwise: bb4];
 //     }
-//     bb2: {
+//     bb3: {
+//         ...
+//     }
+//     bb4: {
 //         _0 = ();
 //         StorageDead(_5);
 //         EndRegion('23_1rs);
@@ -56,7 +62,7 @@ fn main() {
 //         StorageDead(_2);
 //         return;
 //     }
-//     bb3: {
+//     bb5: {
 //         _4 = ();
 //         StorageDead(_5);
 //         StorageLive(_7);
diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs
index 8c0d56eba78..46548f1cce9 100644
--- a/src/test/mir-opt/end_region_3.rs
+++ b/src/test/mir-opt/end_region_3.rs
@@ -43,14 +43,20 @@ fn main() {
 //         goto -> bb1;
 //     }
 //     bb1: {
+//         falseUnwind -> [real: bb2, cleanup: bb3];
+//     }
+//     bb2: {
 //         _1 = const true;
 //         StorageLive(_3);
 //         _3 = &'26_1rs _1;
 //         StorageLive(_5);
 //         _5 = _1;
-//         switchInt(move _5) -> [0u8: bb3, otherwise: bb2];
+//         switchInt(move _5) -> [0u8: bb5, otherwise: bb4];
 //     }
-//     bb2: {
+//     bb3: {
+//         ...
+//     }
+//     bb4: {
 //         _0 = ();
 //         StorageDead(_5);
 //         EndRegion('26_1rs);
@@ -58,7 +64,7 @@ fn main() {
 //         StorageDead(_1);
 //         return;
 //     }
-//     bb3: {
+//     bb5: {
 //         _4 = ();
 //         StorageDead(_5);
 //         StorageLive(_7);
diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs
index b313e296ac9..0f1d714cc6f 100644
--- a/src/test/mir-opt/end_region_9.rs
+++ b/src/test/mir-opt/end_region_9.rs
@@ -57,16 +57,24 @@ fn main() {
 //        _1 = const false;
 //        StorageLive(_2);
 //        _2 = const 3i32;
-//        StorageLive(_4);
-//        goto -> bb1;
+//        falseUnwind -> [real: bb2, cleanup: bb1];
 //    }
-//
 //    bb1: {
+//        ...
+//    }
+//    bb2: {
+//        StorageLive(_4);
+//        goto -> bb3;
+//    }
+//    bb3: {
+//        falseUnwind -> [real: bb4, cleanup: bb1];
+//    }
+//    bb4: {
 //        StorageLive(_7);
 //        _7 = _1;
-//        switchInt(move _7) -> [0u8: bb3, otherwise: bb2];
+//        switchInt(move _7) -> [0u8: bb6, otherwise: bb5];
 //    }
-//    bb2: {
+//    bb5: {
 //        _0 = ();
 //        StorageDead(_7);
 //        EndRegion('33_0rs);
@@ -75,13 +83,13 @@ fn main() {
 //        StorageDead(_1);
 //        return;
 //    }
-//    bb3: {
+//    bb6: {
 //        _4 = &'33_0rs _2;
 //        _6 = ();
 //        StorageDead(_7);
 //        _1 = const true;
 //        _3 = ();
-//        goto -> bb1;
+//        goto -> bb3;
 //    }
 // }
 // END rustc.main.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs
index 37a6229feba..2a82e2675b6 100644
--- a/src/test/mir-opt/end_region_cyclic.rs
+++ b/src/test/mir-opt/end_region_cyclic.rs
@@ -67,16 +67,19 @@ fn query() -> bool { true }
 //         goto -> bb1;
 //     }
 //     bb1: {
+//         falseUnwind -> [real: bb2, cleanup: bb3];
+//     }
+//     bb2: {
 //         StorageLive(_2);
 //         StorageLive(_3);
 //         StorageLive(_4);
 //         _4 = std::option::Option<&'35_0rs S<'35_0rs>>::None;
-//         _3 = const <std::cell::Cell<T>>::new(move _4) -> [return: bb3, unwind: bb2];
+//         _3 = const <std::cell::Cell<T>>::new(move _4) -> [return: bb4, unwind: bb3];
 //     }
-//     bb2: {
+//     bb3: {
 //         resume;
 //     }
-//     bb3: {
+//     bb4: {
 //         StorageDead(_4);
 //         _2 = S<'35_0rs> { r: move _3 };
 //         StorageDead(_3);
@@ -89,27 +92,27 @@ fn query() -> bool { true }
 //         _8 = &'35_0rs (*_9);
 //         _7 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(move _8,);
 //         StorageDead(_8);
-//         _5 = const <std::cell::Cell<T>>::set(move _6, move _7) -> [return: bb4, unwind: bb2];
+//         _5 = const <std::cell::Cell<T>>::set(move _6, move _7) -> [return: bb5, unwind: bb3];
 //     }
-//     bb4: {
+//     bb5: {
 //         EndRegion('16s);
 //         StorageDead(_7);
 //         StorageDead(_6);
 //         StorageDead(_9);
 //         StorageLive(_11);
-//         _11 = const query() -> [return: bb5, unwind: bb2];
-//     }
-//     bb5: {
-//         switchInt(move _11) -> [0u8: bb7, otherwise: bb6];
+//         _11 = const query() -> [return: bb6, unwind: bb3];
 //     }
 //     bb6: {
+//         switchInt(move _11) -> [0u8: bb8, otherwise: bb7];
+//     }
+//     bb7: {
 //         _0 = ();
 //         StorageDead(_11);
 //         EndRegion('35_0rs);
 //         StorageDead(_2);
 //         return;
 //     }
-//     bb7: {
+//     bb8: {
 //         _10 = ();
 //         StorageDead(_11);
 //         StorageLive(_14);
@@ -121,9 +124,9 @@ fn query() -> bool { true }
 //         _16 = &'35_0rs (*_17);
 //         _15 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(move _16,);
 //         StorageDead(_16);
-//         _13 = const <std::cell::Cell<T>>::set(move _14, move _15) -> [return: bb8, unwind: bb2];
+//         _13 = const <std::cell::Cell<T>>::set(move _14, move _15) -> [return: bb9, unwind: bb3];
 //     }
-//     bb8: {
+//     bb9: {
 //         EndRegion('33s);
 //         StorageDead(_15);
 //         StorageDead(_14);
diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs
index b5c188cf834..3151c064307 100644
--- a/src/test/mir-opt/issue-38669.rs
+++ b/src/test/mir-opt/issue-38669.rs
@@ -25,27 +25,30 @@ fn main() {
 //     bb0: {
 //         StorageLive(_1);
 //         _1 = const false;
-//         goto -> bb1;
+//         goto -> bb2;
 //     }
-//
 //     bb1: {
+//         resume;
+//     }
+//     bb2: {
+//         falseUnwind -> [real: bb3, cleanup: bb1];
+//     }
+//     bb3: {
 //         StorageLive(_4);
 //         _4 = _1;
-//         switchInt(move _4) -> [0u8: bb3, otherwise: bb2];
+//         switchInt(move _4) -> [0u8: bb5, otherwise: bb4];
 //     }
-//
-//     bb2: {
+//     bb4: {
 //         _0 = ();
 //         StorageDead(_4);
 //         StorageDead(_1);
 //         return;
 //     }
-//
-//     bb3: {
+//     bb5: {
 //         _3 = ();
 //         StorageDead(_4);
 //         _1 = const true;
 //         _2 = ();
-//         goto -> bb1;
+//         goto -> bb2;
 //     }
 // END rustc.main.SimplifyCfg-initial.after.mir
diff --git a/src/test/mir-opt/loop_test.rs b/src/test/mir-opt/loop_test.rs
new file mode 100644
index 00000000000..d36d8908094
--- /dev/null
+++ b/src/test/mir-opt/loop_test.rs
@@ -0,0 +1,49 @@
+// 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.
+
+// compile-flags: -Z identify_regions -Z emit-end-regions
+
+// Tests to make sure we correctly generate falseUnwind edges in loops
+
+fn main() {
+    // Exit early at runtime. Since only care about the generated MIR
+    // and not the runtime behavior (which is exercised by other tests)
+    // we just bail early. Without this the test just loops infinitely.
+    if true {
+        return;
+    }
+    loop {
+        let x = 1;
+        continue;
+    }
+}
+
+// END RUST SOURCE
+// START rustc.main.SimplifyCfg-qualify-consts.after.mir
+//    ...
+//    bb1: { // The cleanup block
+//        resume;
+//    }
+//    ...
+//    bb3: { // Entry into the loop
+//        _1 = ();
+//        goto -> bb4;
+//    }
+//    bb4: { // The loop_block
+//        falseUnwind -> [real: bb5, cleanup: bb1];
+//    }
+//    bb5: { // The loop body (body_block)
+//        StorageLive(_5);
+//        _5 = const 1i32;
+//        StorageDead(_5);
+//        goto -> bb4;
+//    }
+//    ...
+// END rustc.main.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs
index b060222a95f..64ffc744606 100644
--- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs
+++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs
@@ -25,17 +25,17 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-//    | Live variables on entry to bb2: []
-//    bb2: {
-//            | Live variables on entry to bb2[0]: []
+//    | Live variables on entry to bb3: []
+//    bb3: {
+//            | Live variables on entry to bb3[0]: []
 //        _1 = const 55usize;
-//            | Live variables on entry to bb2[1]: [_1]
+//            | Live variables on entry to bb3[1]: [_1]
 //        StorageLive(_3);
-//            | Live variables on entry to bb2[2]: [_1]
+//            | Live variables on entry to bb3[2]: [_1]
 //        StorageLive(_4);
-//            | Live variables on entry to bb2[3]: [_1]
+//            | Live variables on entry to bb3[3]: [_1]
 //        _4 = _1;
-//            | Live variables on entry to bb2[4]: [_4]
-//        _3 = const use_x(move _4) -> [return: bb3, unwind: bb1];
+//            | Live variables on entry to bb3[4]: [_4]
+//        _3 = const use_x(move _4) -> [return: bb4, unwind: bb1];
 //    }
 // END rustc.main.nll.0.mir