about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-03-30 14:20:20 +0000
committerbors <bors@rust-lang.org>2017-03-30 14:20:20 +0000
commit7ae95e548929f4924905b2e8f87f2dd57d8bc887 (patch)
treeaceb2641992bb0d995e317bb38e119a6ad13710d
parentfe151194e9a919ab23c2bb356767a5cbfa3811e7 (diff)
parent2414222b17f45e33d68582dd5ebe0083ed27b8cc (diff)
downloadrust-7ae95e548929f4924905b2e8f87f2dd57d8bc887.tar.gz
rust-7ae95e548929f4924905b2e8f87f2dd57d8bc887.zip
Auto merge of #40224 - nikomatsakis:issue-39808, r=eddyb
change the strategy for diverging types

The new strategy is as follows. First, the `!` type is assigned
in two cases:

- a block with a diverging statement and no tail expression (e.g.,
  `{return;}`);
- any expression with the type `!` is considered diverging.

Second, we track when we are in a diverging state, and we permit a value
of any type to be coerced **into** `!` if the expression that produced
it is diverging. This means that `fn foo() -> ! { panic!(); 22 }`
type-checks, even though the block has a type of `usize`.

Finally, coercions **from** the `!` type to any other are always
permitted.

Fixes #39808.
Fixes #39984.
-rw-r--r--src/librustc/cfg/construct.rs8
-rw-r--r--src/librustc/hir/intravisit.rs2
-rw-r--r--src/librustc/hir/lowering.rs39
-rw-r--r--src/librustc/hir/mod.rs12
-rw-r--r--src/librustc/hir/print.rs6
-rw-r--r--src/librustc/infer/type_variable.rs5
-rw-r--r--src/librustc/middle/expr_use_visitor.rs4
-rw-r--r--src/librustc/middle/liveness.rs6
-rw-r--r--src/librustc/traits/error_reporting.rs1
-rw-r--r--src/librustc/traits/mod.rs3
-rw-r--r--src/librustc/traits/structural_impls.rs3
-rw-r--r--src/librustc_mir/build/block.rs170
-rw-r--r--src/librustc_mir/build/expr/into.rs14
-rw-r--r--src/librustc_mir/hair/cx/block.rs1
-rw-r--r--src/librustc_mir/hair/cx/expr.rs2
-rw-r--r--src/librustc_mir/hair/mod.rs1
-rw-r--r--src/librustc_trans/abi.rs2
-rw-r--r--src/librustc_typeck/check/_match.rs90
-rw-r--r--src/librustc_typeck/check/autoderef.rs12
-rw-r--r--src/librustc_typeck/check/callee.rs2
-rw-r--r--src/librustc_typeck/check/cast.rs12
-rw-r--r--src/librustc_typeck/check/coercion.rs448
-rw-r--r--src/librustc_typeck/check/compare_method.rs2
-rw-r--r--src/librustc_typeck/check/demand.rs12
-rw-r--r--src/librustc_typeck/check/method/confirm.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs797
-rw-r--r--src/librustc_typeck/check/wfcheck.rs2
-rw-r--r--src/librustc_typeck/lib.rs1
-rw-r--r--src/libsyntax/parse/obsolete.rs1
-rw-r--r--src/test/compile-fail/coerce-to-bang-cast.rs23
-rw-r--r--src/test/compile-fail/coerce-to-bang.rs90
-rw-r--r--src/test/compile-fail/defaulted-unit-warning.rs12
-rw-r--r--src/test/compile-fail/diverging-tuple-parts-39485.rs (renamed from src/test/run-make/graphviz-flowgraph/f23.rs)30
-rw-r--r--src/test/compile-fail/index-bot.rs2
-rw-r--r--src/test/compile-fail/issue-10176.rs2
-rw-r--r--src/test/compile-fail/issue-13847.rs2
-rw-r--r--src/test/compile-fail/issue-15207.rs2
-rw-r--r--src/test/compile-fail/issue-17373.rs2
-rw-r--r--src/test/compile-fail/issue-18532.rs3
-rw-r--r--src/test/compile-fail/issue-2149.rs2
-rw-r--r--src/test/compile-fail/issue-27042.rs8
-rw-r--r--src/test/compile-fail/loop-break-value.rs23
-rw-r--r--src/test/compile-fail/match-no-arms-unreachable-after.rs (renamed from src/test/run-make/graphviz-flowgraph/f01.rs)15
-rw-r--r--src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs (renamed from src/test/run-make/graphviz-flowgraph/f07.rs)11
-rw-r--r--src/test/compile-fail/match-unresolved-one-arm.rs17
-rw-r--r--src/test/compile-fail/never-assign-dead-code.rs1
-rw-r--r--src/test/compile-fail/never-assign-wrong-type.rs1
-rw-r--r--src/test/compile-fail/never-fallback.rs41
-rw-r--r--src/test/compile-fail/region-invariant-static-error-reporting.rs3
-rw-r--r--src/test/compile-fail/regions-bounds.rs10
-rw-r--r--src/test/run-make/graphviz-flowgraph/Makefile38
-rw-r--r--src/test/run-make/graphviz-flowgraph/f00.dot-expected.dot9
-rw-r--r--src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot13
-rw-r--r--src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot13
-rw-r--r--src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot17
-rw-r--r--src/test/run-make/graphviz-flowgraph/f03.rs13
-rw-r--r--src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot15
-rw-r--r--src/test/run-make/graphviz-flowgraph/f04.rs13
-rw-r--r--src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot23
-rw-r--r--src/test/run-make/graphviz-flowgraph/f05.rs13
-rw-r--r--src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot19
-rw-r--r--src/test/run-make/graphviz-flowgraph/f06.rs14
-rw-r--r--src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot39
-rw-r--r--src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot38
-rw-r--r--src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot54
-rw-r--r--src/test/run-make/graphviz-flowgraph/f09.rs18
-rw-r--r--src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot36
-rw-r--r--src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot35
-rw-r--r--src/test/run-make/graphviz-flowgraph/f11.rs18
-rw-r--r--src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot50
-rw-r--r--src/test/run-make/graphviz-flowgraph/f12.rs18
-rw-r--r--src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot54
-rw-r--r--src/test/run-make/graphviz-flowgraph/f13.rs18
-rw-r--r--src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot36
-rw-r--r--src/test/run-make/graphviz-flowgraph/f14.rs18
-rw-r--r--src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot105
-rw-r--r--src/test/run-make/graphviz-flowgraph/f15.rs30
-rw-r--r--src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot111
-rw-r--r--src/test/run-make/graphviz-flowgraph/f16.rs31
-rw-r--r--src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot21
-rw-r--r--src/test/run-make/graphviz-flowgraph/f17.rs13
-rw-r--r--src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot23
-rw-r--r--src/test/run-make/graphviz-flowgraph/f18.rs14
-rw-r--r--src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot29
-rw-r--r--src/test/run-make/graphviz-flowgraph/f19.rs16
-rw-r--r--src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot29
-rw-r--r--src/test/run-make/graphviz-flowgraph/f20.rs14
-rw-r--r--src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot101
-rw-r--r--src/test/run-make/graphviz-flowgraph/f21.rs30
-rw-r--r--src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot107
-rw-r--r--src/test/run-make/graphviz-flowgraph/f22.rs31
-rw-r--r--src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot113
-rw-r--r--src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot161
-rw-r--r--src/test/run-make/graphviz-flowgraph/f24.rs36
-rw-r--r--src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot161
-rw-r--r--src/test/run-make/graphviz-flowgraph/f25.rs36
-rw-r--r--src/test/run-pass/diverging-fallback-control-flow.rs106
-rw-r--r--src/test/run-pass/diverging-fallback-method-chain.rs (renamed from src/test/run-pass/unit-fallback.rs)33
-rw-r--r--src/test/run-pass/diverging-fallback-option.rs22
-rw-r--r--src/test/run-pass/issue-39808.rs26
-rw-r--r--src/test/ui/loop-break-value-no-repeat.rs (renamed from src/test/run-make/graphviz-flowgraph/f08.rs)19
-rw-r--r--src/test/ui/loop-break-value-no-repeat.stderr8
-rw-r--r--src/test/ui/reachable/README.md7
-rw-r--r--src/test/ui/reachable/expr_add.rs (renamed from src/test/run-make/graphviz-flowgraph/f10.rs)22
-rw-r--r--src/test/ui/reachable/expr_add.stderr14
-rw-r--r--src/test/ui/reachable/expr_again.rs (renamed from src/test/run-make/graphviz-flowgraph/f02.rs)13
-rw-r--r--src/test/ui/reachable/expr_again.stderr15
-rw-r--r--src/test/ui/reachable/expr_andand.rs21
-rw-r--r--src/test/ui/reachable/expr_andand.stderr0
-rw-r--r--src/test/ui/reachable/expr_array.rs28
-rw-r--r--src/test/ui/reachable/expr_array.stderr20
-rw-r--r--src/test/ui/reachable/expr_assign.rs39
-rw-r--r--src/test/ui/reachable/expr_assign.stderr26
-rw-r--r--src/test/ui/reachable/expr_block.rs41
-rw-r--r--src/test/ui/reachable/expr_block.stderr22
-rw-r--r--src/test/ui/reachable/expr_box.rs (renamed from src/test/run-make/graphviz-flowgraph/f00.rs)9
-rw-r--r--src/test/ui/reachable/expr_box.stderr14
-rw-r--r--src/test/ui/reachable/expr_call.rs31
-rw-r--r--src/test/ui/reachable/expr_call.stderr20
-rw-r--r--src/test/ui/reachable/expr_cast.rs23
-rw-r--r--src/test/ui/reachable/expr_cast.stderr14
-rw-r--r--src/test/ui/reachable/expr_if.rs41
-rw-r--r--src/test/ui/reachable/expr_if.stderr15
-rw-r--r--src/test/ui/reachable/expr_loop.rs44
-rw-r--r--src/test/ui/reachable/expr_loop.stderr31
-rw-r--r--src/test/ui/reachable/expr_match.rs54
-rw-r--r--src/test/ui/reachable/expr_match.stderr30
-rw-r--r--src/test/ui/reachable/expr_method.rs34
-rw-r--r--src/test/ui/reachable/expr_method.stderr20
-rw-r--r--src/test/ui/reachable/expr_oror.rs20
-rw-r--r--src/test/ui/reachable/expr_oror.stderr0
-rw-r--r--src/test/ui/reachable/expr_repeat.rs23
-rw-r--r--src/test/ui/reachable/expr_repeat.stderr14
-rw-r--r--src/test/ui/reachable/expr_return.rs24
-rw-r--r--src/test/ui/reachable/expr_return.stderr14
-rw-r--r--src/test/ui/reachable/expr_struct.rs43
-rw-r--r--src/test/ui/reachable/expr_struct.stderr32
-rw-r--r--src/test/ui/reachable/expr_tup.rs28
-rw-r--r--src/test/ui/reachable/expr_tup.stderr20
-rw-r--r--src/test/ui/reachable/expr_type.rs23
-rw-r--r--src/test/ui/reachable/expr_type.stderr14
-rw-r--r--src/test/ui/reachable/expr_unary.rs21
-rw-r--r--src/test/ui/reachable/expr_unary.stderr8
-rw-r--r--src/test/ui/reachable/expr_while.rs38
-rw-r--r--src/test/ui/reachable/expr_while.stderr31
-rw-r--r--src/test/ui/resolve/token-error-correct-3.stderr4
146 files changed, 2433 insertions, 2592 deletions
diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs
index 189a7344c31..20b322ec189 100644
--- a/src/librustc/cfg/construct.rs
+++ b/src/librustc/cfg/construct.rs
@@ -74,11 +74,11 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
 impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
     fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex {
-        if let Some(break_to_expr_id) = blk.break_to_expr_id {
+        if blk.targeted_by_break {
             let expr_exit = self.add_ast_node(blk.id, &[]);
 
             self.breakable_block_scopes.push(BlockScope {
-                block_expr_id: break_to_expr_id,
+                block_expr_id: blk.id,
                 break_index: expr_exit,
             });
 
@@ -195,7 +195,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
                 //   [..expr..]
                 //
                 let cond_exit = self.expr(&cond, pred);                // 1
-                let then_exit = self.block(&then, cond_exit);          // 2
+                let then_exit = self.expr(&then, cond_exit);          // 2
                 self.add_ast_node(expr.id, &[cond_exit, then_exit])      // 3,4
             }
 
@@ -215,7 +215,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
                 //   [..expr..]
                 //
                 let cond_exit = self.expr(&cond, pred);                // 1
-                let then_exit = self.block(&then, cond_exit);          // 2
+                let then_exit = self.expr(&then, cond_exit);          // 2
                 let else_exit = self.expr(&otherwise, cond_exit);      // 3
                 self.add_ast_node(expr.id, &[then_exit, else_exit])      // 4, 5
             }
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index f59b8b757f5..c7ad143c949 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -960,7 +960,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
         }
         ExprIf(ref head_expression, ref if_block, ref optional_else) => {
             visitor.visit_expr(head_expression);
-            visitor.visit_block(if_block);
+            visitor.visit_expr(if_block);
             walk_list!(visitor, visit_expr, optional_else);
         }
         ExprWhile(ref subexpression, ref block, ref opt_sp_name) => {
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 029b27b40f0..17185a6ab69 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -1156,7 +1156,7 @@ impl<'a> LoweringContext<'a> {
         bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect()
     }
 
-    fn lower_block(&mut self, b: &Block, break_to: Option<NodeId>) -> P<hir::Block> {
+    fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P<hir::Block> {
         let mut expr = None;
 
         let mut stmts = vec![];
@@ -1179,7 +1179,7 @@ impl<'a> LoweringContext<'a> {
             expr: expr,
             rules: self.lower_block_check_mode(&b.rules),
             span: b.span,
-            break_to_expr_id: break_to,
+            targeted_by_break: targeted_by_break,
         })
     }
 
@@ -1274,7 +1274,7 @@ impl<'a> LoweringContext<'a> {
             }
             ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
                 self.with_new_scopes(|this| {
-                    let body = this.lower_block(body, None);
+                    let body = this.lower_block(body, false);
                     let body = this.expr_block(body, ThinVec::new());
                     let body_id = this.record_body(body, Some(decl));
                     hir::ItemFn(this.lower_fn_decl(decl),
@@ -1368,7 +1368,7 @@ impl<'a> LoweringContext<'a> {
                                                    hir::TraitMethod::Required(names))
                     }
                     TraitItemKind::Method(ref sig, Some(ref body)) => {
-                        let body = this.lower_block(body, None);
+                        let body = this.lower_block(body, false);
                         let expr = this.expr_block(body, ThinVec::new());
                         let body_id = this.record_body(expr, Some(&sig.decl));
                         hir::TraitItemKind::Method(this.lower_method_sig(sig),
@@ -1424,7 +1424,7 @@ impl<'a> LoweringContext<'a> {
                         hir::ImplItemKind::Const(this.lower_ty(ty), body_id)
                     }
                     ImplItemKind::Method(ref sig, ref body) => {
-                        let body = this.lower_block(body, None);
+                        let body = this.lower_block(body, false);
                         let expr = this.expr_block(body, ThinVec::new());
                         let body_id = this.record_body(expr, Some(&sig.decl));
                         hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id)
@@ -1848,7 +1848,7 @@ impl<'a> LoweringContext<'a> {
                                 id: id,
                                 rules: hir::DefaultBlock,
                                 span: span,
-                                break_to_expr_id: None,
+                                targeted_by_break: false,
                             });
                             P(self.expr_block(blk, ThinVec::new()))
                         }
@@ -1856,24 +1856,27 @@ impl<'a> LoweringContext<'a> {
                     }
                 });
 
-                hir::ExprIf(P(self.lower_expr(cond)), self.lower_block(blk, None), else_opt)
+                let then_blk = self.lower_block(blk, false);
+                let then_expr = self.expr_block(then_blk, ThinVec::new());
+
+                hir::ExprIf(P(self.lower_expr(cond)), P(then_expr), else_opt)
             }
             ExprKind::While(ref cond, ref body, opt_ident) => {
                 self.with_loop_scope(e.id, |this|
                     hir::ExprWhile(
                         this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
-                        this.lower_block(body, None),
+                        this.lower_block(body, false),
                         this.lower_opt_sp_ident(opt_ident)))
             }
             ExprKind::Loop(ref body, opt_ident) => {
                 self.with_loop_scope(e.id, |this|
-                    hir::ExprLoop(this.lower_block(body, None),
+                    hir::ExprLoop(this.lower_block(body, false),
                                   this.lower_opt_sp_ident(opt_ident),
                                   hir::LoopSource::Loop))
             }
             ExprKind::Catch(ref body) => {
-                self.with_catch_scope(e.id, |this|
-                    hir::ExprBlock(this.lower_block(body, Some(e.id))))
+                self.with_catch_scope(body.id, |this|
+                    hir::ExprBlock(this.lower_block(body, true)))
             }
             ExprKind::Match(ref expr, ref arms) => {
                 hir::ExprMatch(P(self.lower_expr(expr)),
@@ -1891,7 +1894,7 @@ impl<'a> LoweringContext<'a> {
                     })
                 })
             }
-            ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, None)),
+            ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, false)),
             ExprKind::Assign(ref el, ref er) => {
                 hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
             }
@@ -2037,7 +2040,7 @@ impl<'a> LoweringContext<'a> {
 
                 // `<pat> => <body>`
                 {
-                    let body = self.lower_block(body, None);
+                    let body = self.lower_block(body, false);
                     let body_expr = P(self.expr_block(body, ThinVec::new()));
                     let pat = self.lower_pat(pat);
                     arms.push(self.arm(hir_vec![pat], body_expr));
@@ -2109,7 +2112,7 @@ impl<'a> LoweringContext<'a> {
                             let (guard, body) = if let ExprKind::If(ref cond,
                                                                     ref then,
                                                                     _) = else_expr.node {
-                                let then = self.lower_block(then, None);
+                                let then = self.lower_block(then, false);
                                 (Some(cond),
                                  self.expr_block(then, ThinVec::new()))
                             } else {
@@ -2159,7 +2162,7 @@ impl<'a> LoweringContext<'a> {
                 // Note that the block AND the condition are evaluated in the loop scope.
                 // This is done to allow `break` from inside the condition of the loop.
                 let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| (
-                    this.lower_block(body, None),
+                    this.lower_block(body, false),
                     this.expr_break(e.span, ThinVec::new()),
                     this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
                 ));
@@ -2220,7 +2223,7 @@ impl<'a> LoweringContext<'a> {
                 // `::std::option::Option::Some(<pat>) => <body>`
                 let pat_arm = {
                     let body_block = self.with_loop_scope(e.id,
-                                                          |this| this.lower_block(body, None));
+                                                          |this| this.lower_block(body, false));
                     let body_expr = P(self.expr_block(body_block, ThinVec::new()));
                     let pat = self.lower_pat(pat);
                     let some_pat = self.pat_some(e.span, pat);
@@ -2652,7 +2655,7 @@ impl<'a> LoweringContext<'a> {
             id: self.next_id(),
             rules: hir::DefaultBlock,
             span: span,
-            break_to_expr_id: None,
+            targeted_by_break: false,
         }
     }
 
@@ -2760,7 +2763,7 @@ impl<'a> LoweringContext<'a> {
             id: id,
             stmts: stmts,
             expr: Some(expr),
-            break_to_expr_id: None,
+            targeted_by_break: false,
         });
         self.expr_block(block, attrs)
     }
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 3ca75dc6c00..d5000ac9c18 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -549,9 +549,11 @@ pub struct Block {
     /// Distinguishes between `unsafe { ... }` and `{ ... }`
     pub rules: BlockCheckMode,
     pub span: Span,
-    /// The id of the expression that `break` breaks to if the block can be broken out of.
-    /// Currently only `Some(_)` for `catch {}` blocks
-    pub break_to_expr_id: Option<NodeId>,
+    /// If true, then there may exist `break 'a` values that aim to
+    /// break out of this block early. As of this writing, this is not
+    /// currently permitted in Rust itself, but it is generated as
+    /// part of `catch` statements.
+    pub targeted_by_break: bool,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
@@ -993,8 +995,8 @@ pub enum Expr_ {
     ExprType(P<Expr>, P<Ty>),
     /// An `if` block, with an optional else block
     ///
-    /// `if expr { block } else { expr }`
-    ExprIf(P<Expr>, P<Block>, Option<P<Expr>>),
+    /// `if expr { expr } else { expr }`
+    ExprIf(P<Expr>, P<Expr>, Option<P<Expr>>),
     /// A while loop, with an optional label
     ///
     /// `'label: while expr { block }`
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 3411de9bb5d..04a65fd5e3a 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -1036,7 +1036,7 @@ impl<'a> State<'a> {
                         word(&mut self.s, " else if ")?;
                         self.print_expr(&i)?;
                         space(&mut self.s)?;
-                        self.print_block(&then)?;
+                        self.print_expr(&then)?;
                         self.print_else(e.as_ref().map(|e| &**e))
                     }
                     // "final else"
@@ -1058,13 +1058,13 @@ impl<'a> State<'a> {
 
     pub fn print_if(&mut self,
                     test: &hir::Expr,
-                    blk: &hir::Block,
+                    blk: &hir::Expr,
                     elseopt: Option<&hir::Expr>)
                     -> io::Result<()> {
         self.head("if")?;
         self.print_expr(test)?;
         space(&mut self.s)?;
-        self.print_block(blk)?;
+        self.print_expr(blk)?;
         self.print_else(elseopt)
     }
 
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index 9c8419d9546..67f37e5f927 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -30,6 +30,7 @@ pub struct TypeVariableTable<'tcx> {
 }
 
 /// Reasons to create a type inference variable
+#[derive(Debug)]
 pub enum TypeVariableOrigin {
     MiscVariable(Span),
     NormalizeProjectionType(Span),
@@ -41,6 +42,7 @@ pub enum TypeVariableOrigin {
     AdjustmentType(Span),
     DivergingStmt(Span),
     DivergingBlockExpr(Span),
+    DivergingFn(Span),
     LatticeVariable(Span),
 }
 
@@ -196,6 +198,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
                    diverging: bool,
                    origin: TypeVariableOrigin,
                    default: Option<Default<'tcx>>,) -> ty::TyVid {
+        debug!("new_var(diverging={:?}, origin={:?})", diverging, origin);
         self.eq_relations.new_key(());
         let index = self.values.push(TypeVariableData {
             value: Bounded { relations: vec![], default: default },
@@ -203,7 +206,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
             diverging: diverging
         });
         let v = ty::TyVid { index: index as u32 };
-        debug!("new_var() -> {:?}", v);
+        debug!("new_var: diverging={:?} index={:?}", diverging, v);
         v
     }
 
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index a44679b0b3e..c7cf4a35a4b 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -414,9 +414,9 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                 self.consume_exprs(exprs);
             }
 
-            hir::ExprIf(ref cond_expr, ref then_blk, ref opt_else_expr) => {
+            hir::ExprIf(ref cond_expr, ref then_expr, ref opt_else_expr) => {
                 self.consume_expr(&cond_expr);
-                self.walk_block(&then_blk);
+                self.walk_expr(&then_expr);
                 if let Some(ref else_expr) = *opt_else_expr {
                     self.consume_expr(&else_expr);
                 }
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 769dc8aeb54..7cae08efc0d 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -821,8 +821,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
     fn propagate_through_block(&mut self, blk: &hir::Block, succ: LiveNode)
                                -> LiveNode {
-        if let Some(break_to_expr_id) = blk.break_to_expr_id {
-            self.breakable_block_ln.insert(break_to_expr_id, succ);
+        if blk.targeted_by_break {
+            self.breakable_block_ln.insert(blk.id, succ);
         }
         let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ);
         blk.stmts.iter().rev().fold(succ, |succ, stmt| {
@@ -951,7 +951,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             //   (  succ  )
             //
             let else_ln = self.propagate_through_opt_expr(els.as_ref().map(|e| &**e), succ);
-            let then_ln = self.propagate_through_block(&then, succ);
+            let then_ln = self.propagate_through_expr(&then, succ);
             let ln = self.live_node(expr.id, expr.span);
             self.init_from_succ(ln, else_ln);
             self.merge_from_succ(ln, then_ln, false);
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 27525d550ff..152dd6ac300 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -904,6 +904,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             ObligationCauseCode::StartFunctionType |
             ObligationCauseCode::IntrinsicType |
             ObligationCauseCode::MethodReceiver |
+            ObligationCauseCode::ReturnNoExpression |
             ObligationCauseCode::MiscObligation => {
             }
             ObligationCauseCode::SliceOrArrayElem => {
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index c71fc28b4d6..47cbccdd2ab 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -173,6 +173,9 @@ pub enum ObligationCauseCode<'tcx> {
 
     // method receiver
     MethodReceiver,
+
+    // `return` with no expression
+    ReturnNoExpression,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index 717c171db2a..44ef461327d 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -167,6 +167,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
     type Lifted = traits::ObligationCauseCode<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
         match *self {
+            super::ReturnNoExpression => Some(super::ReturnNoExpression),
             super::MiscObligation => Some(super::MiscObligation),
             super::SliceOrArrayElem => Some(super::SliceOrArrayElem),
             super::TupleElem => Some(super::TupleElem),
@@ -489,6 +490,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
             super::StructInitializerSized |
             super::VariableType(_) |
             super::ReturnType |
+            super::ReturnNoExpression |
             super::RepeatVec |
             super::FieldSized |
             super::ConstSized |
@@ -533,6 +535,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
             super::StructInitializerSized |
             super::VariableType(_) |
             super::ReturnType |
+            super::ReturnNoExpression |
             super::RepeatVec |
             super::FieldSized |
             super::ConstSized |
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index 3305cfc0dfe..7739766182c 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -12,90 +12,116 @@ use build::{BlockAnd, BlockAndExtension, Builder};
 use hair::*;
 use rustc::mir::*;
 use rustc::hir;
+use syntax_pos::Span;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn ast_block(&mut self,
                      destination: &Lvalue<'tcx>,
-                     mut block: BasicBlock,
-                     ast_block: &'tcx hir::Block)
+                     block: BasicBlock,
+                     ast_block: &'tcx hir::Block,
+                     source_info: SourceInfo)
                      -> BlockAnd<()> {
-        let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
+        let Block { extent, span, stmts, expr, targeted_by_break } = self.hir.mirror(ast_block);
         self.in_scope(extent, block, move |this| {
-            // This convoluted structure is to avoid using recursion as we walk down a list
-            // of statements. Basically, the structure we get back is something like:
-            //
-            //    let x = <init> in {
-            //       expr1;
-            //       let y = <init> in {
-            //           expr2;
-            //           expr3;
-            //           ...
-            //       }
-            //    }
-            //
-            // The let bindings are valid till the end of block so all we have to do is to pop all
-            // the let-scopes at the end.
-            //
-            // First we build all the statements in the block.
-            let mut let_extent_stack = Vec::with_capacity(8);
-            let outer_visibility_scope = this.visibility_scope;
-            for stmt in stmts {
-                let Stmt { span: _, kind } = this.hir.mirror(stmt);
-                match kind {
-                    StmtKind::Expr { scope, expr } => {
-                        unpack!(block = this.in_scope(scope, block, |this| {
-                            let expr = this.hir.mirror(expr);
-                            this.stmt_expr(block, expr)
-                        }));
-                    }
-                    StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
-                        let tcx = this.hir.tcx();
+            if targeted_by_break {
+                // This is a `break`-able block (currently only `catch { ... }`)
+                let exit_block = this.cfg.start_new_block();
+                let block_exit = this.in_breakable_scope(None, exit_block,
+                                                         destination.clone(), |this| {
+                    this.ast_block_stmts(destination, block, span, stmts, expr)
+                });
+                this.cfg.terminate(unpack!(block_exit), source_info,
+                                   TerminatorKind::Goto { target: exit_block });
+                exit_block.unit()
+            } else {
+                this.ast_block_stmts(destination, block, span, stmts, expr)
+            }
+        })
+    }
 
-                        // Enter the remainder scope, i.e. the bindings' destruction scope.
-                        this.push_scope(remainder_scope);
-                        let_extent_stack.push(remainder_scope);
+    fn ast_block_stmts(&mut self,
+                       destination: &Lvalue<'tcx>,
+                       mut block: BasicBlock,
+                       span: Span,
+                       stmts: Vec<StmtRef<'tcx>>,
+                       expr: Option<ExprRef<'tcx>>)
+                       -> BlockAnd<()> {
+        let this = self;
+
+        // This convoluted structure is to avoid using recursion as we walk down a list
+        // of statements. Basically, the structure we get back is something like:
+        //
+        //    let x = <init> in {
+        //       expr1;
+        //       let y = <init> in {
+        //           expr2;
+        //           expr3;
+        //           ...
+        //       }
+        //    }
+        //
+        // The let bindings are valid till the end of block so all we have to do is to pop all
+        // the let-scopes at the end.
+        //
+        // First we build all the statements in the block.
+        let mut let_extent_stack = Vec::with_capacity(8);
+        let outer_visibility_scope = this.visibility_scope;
+        for stmt in stmts {
+            let Stmt { span: _, kind } = this.hir.mirror(stmt);
+            match kind {
+                StmtKind::Expr { scope, expr } => {
+                    unpack!(block = this.in_scope(scope, block, |this| {
+                        let expr = this.hir.mirror(expr);
+                        this.stmt_expr(block, expr)
+                    }));
+                }
+                StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
+                    let tcx = this.hir.tcx();
 
-                        // Declare the bindings, which may create a visibility scope.
-                        let remainder_span = remainder_scope.span(&tcx.region_maps, &tcx.hir);
-                        let remainder_span = remainder_span.unwrap_or(span);
-                        let scope = this.declare_bindings(None, remainder_span, &pattern);
+                    // Enter the remainder scope, i.e. the bindings' destruction scope.
+                    this.push_scope(remainder_scope);
+                    let_extent_stack.push(remainder_scope);
 
-                        // Evaluate the initializer, if present.
-                        if let Some(init) = initializer {
-                            unpack!(block = this.in_scope(init_scope, block, move |this| {
-                                // FIXME #30046                              ^~~~
-                                this.expr_into_pattern(block, pattern, init)
-                            }));
-                        } else {
-                            this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
-                                this.storage_live_binding(block, node, span);
-                                this.schedule_drop_for_binding(node, span);
-                            })
-                        }
+                    // Declare the bindings, which may create a visibility scope.
+                    let remainder_span = remainder_scope.span(&tcx.region_maps, &tcx.hir);
+                    let remainder_span = remainder_span.unwrap_or(span);
+                    let scope = this.declare_bindings(None, remainder_span, &pattern);
 
-                        // Enter the visibility scope, after evaluating the initializer.
-                        if let Some(visibility_scope) = scope {
-                            this.visibility_scope = visibility_scope;
-                        }
+                    // Evaluate the initializer, if present.
+                    if let Some(init) = initializer {
+                        unpack!(block = this.in_scope(init_scope, block, move |this| {
+                            // FIXME #30046                              ^~~~
+                            this.expr_into_pattern(block, pattern, init)
+                        }));
+                    } else {
+                        this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
+                            this.storage_live_binding(block, node, span);
+                            this.schedule_drop_for_binding(node, span);
+                        })
+                    }
+
+                    // Enter the visibility scope, after evaluating the initializer.
+                    if let Some(visibility_scope) = scope {
+                        this.visibility_scope = visibility_scope;
                     }
                 }
             }
-            // Then, the block may have an optional trailing expression which is a “return” value
-            // of the block.
-            if let Some(expr) = expr {
-                unpack!(block = this.into(destination, block, expr));
-            } else {
-                let source_info = this.source_info(span);
-                this.cfg.push_assign_unit(block, source_info, destination);
-            }
-            // Finally, we pop all the let scopes before exiting out from the scope of block
-            // itself.
-            for extent in let_extent_stack.into_iter().rev() {
-                unpack!(block = this.pop_scope(extent, block));
-            }
-            // Restore the original visibility scope.
-            this.visibility_scope = outer_visibility_scope;
-            block.unit()
-        })
+        }
+        // Then, the block may have an optional trailing expression which is a “return” value
+        // of the block.
+        if let Some(expr) = expr {
+            unpack!(block = this.into(destination, block, expr));
+        } else {
+            let source_info = this.source_info(span);
+            this.cfg.push_assign_unit(block, source_info, destination);
+        }
+        // Finally, we pop all the let scopes before exiting out from the scope of block
+        // itself.
+        for extent in let_extent_stack.into_iter().rev() {
+            unpack!(block = this.pop_scope(extent, block));
+        }
+        // Restore the original visibility scope.
+        this.visibility_scope = outer_visibility_scope;
+        block.unit()
     }
 }
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index e1b0c6a6f04..a5a114c61bc 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -40,19 +40,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 this.in_scope(extent, block, |this| this.into(destination, block, value))
             }
             ExprKind::Block { body: ast_block } => {
-                if let Some(_) = ast_block.break_to_expr_id {
-                    // This is a `break`-able block (currently only `catch { ... }`)
-                    let exit_block = this.cfg.start_new_block();
-                    let block_exit = this.in_breakable_scope(None, exit_block,
-                                                             destination.clone(), |this| {
-                        this.ast_block(destination, block, ast_block)
-                    });
-                    this.cfg.terminate(unpack!(block_exit), source_info,
-                                       TerminatorKind::Goto { target: exit_block });
-                    exit_block.unit()
-                } else {
-                    this.ast_block(destination, block, ast_block)
-                }
+                this.ast_block(destination, block, ast_block, source_info)
             }
             ExprKind::Match { discriminant, arms } => {
                 this.match_expr(destination, expr_span, block, discriminant, arms)
diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs
index ba6b9361a83..d2465331df3 100644
--- a/src/librustc_mir/hair/cx/block.rs
+++ b/src/librustc_mir/hair/cx/block.rs
@@ -23,6 +23,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block {
         // in order to get the lexical scoping correctly.
         let stmts = mirror_stmts(cx, self.id, &*self.stmts);
         Block {
+            targeted_by_break: self.targeted_by_break,
             extent: cx.tcx.region_maps.node_extent(self.id),
             span: self.span,
             stmts: stmts,
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 44858a98e36..d9b8d04ad38 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -636,7 +636,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         hir::ExprIf(ref cond, ref then, ref otherwise) => {
             ExprKind::If {
                 condition: cond.to_ref(),
-                then: block::to_expr_ref(cx, then),
+                then: then.to_ref(),
                 otherwise: otherwise.to_ref(),
             }
         }
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 2ee375dee08..a3982efd2d6 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -31,6 +31,7 @@ pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPatt
 
 #[derive(Clone, Debug)]
 pub struct Block<'tcx> {
+    pub targeted_by_break: bool,
     pub extent: CodeExtent,
     pub span: Span,
     pub stmts: Vec<StmtRef<'tcx>>,
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs
index 1530708b4b8..453f65eb762 100644
--- a/src/librustc_trans/abi.rs
+++ b/src/librustc_trans/abi.rs
@@ -369,7 +369,7 @@ impl FnType {
             match sig.inputs().last().unwrap().sty {
                 ty::TyTuple(ref tupled_arguments, _) => {
                     inputs = &sig.inputs()[0..sig.inputs().len() - 1];
-                    &tupled_arguments
+                    tupled_arguments
                 }
                 _ => {
                     bug!("argument to function with \"rust-call\" ABI \
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index feed5752cf8..4a044642444 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -16,6 +16,7 @@ use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::traits::ObligationCauseCode;
 use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
 use check::{FnCtxt, Expectation, Diverges};
+use check::coercion::CoerceMany;
 use util::nodemap::FxHashMap;
 
 use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -414,6 +415,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span));
             self.check_expr_has_type(discrim, discrim_ty);
         };
+
+        // If the discriminant diverges, the match is pointless (e.g.,
+        // `match (return) { }`).
+        self.warn_if_unreachable(expr.id, expr.span, "expression");
+
+        // If there are no arms, that is a diverging match; a special case.
+        if arms.is_empty() {
+            self.diverges.set(self.diverges.get() | Diverges::Always);
+            return tcx.types.never;
+        }
+
+        // Otherwise, we have to union together the types that the
+        // arms produce and so forth.
+
         let discrim_diverges = self.diverges.get();
         self.diverges.set(Diverges::Maybe);
 
@@ -426,6 +441,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 self.check_pat(&p, discrim_ty);
                 all_pats_diverge &= self.diverges.get();
             }
+
             // As discussed with @eddyb, this is for disabling unreachable_code
             // warnings on patterns (they're now subsumed by unreachable_patterns
             // warnings).
@@ -444,20 +460,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // on any empty type and is therefore unreachable; should the flow
         // of execution reach it, we will panic, so bottom is an appropriate
         // type in that case)
-        let expected = expected.adjust_for_branches(self);
-        let mut result_ty = self.next_diverging_ty_var(
-            TypeVariableOrigin::DivergingBlockExpr(expr.span));
         let mut all_arms_diverge = Diverges::WarnedAlways;
-        let coerce_first = match expected {
-            // We don't coerce to `()` so that if the match expression is a
-            // statement it's branches can have any consistent type. That allows
-            // us to give better error messages (pointing to a usually better
-            // arm for inconsistent arms or to the whole match when a `()` type
-            // is required).
-            Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => {
-                ety
-            }
-            _ => result_ty
+
+        let expected = expected.adjust_for_branches(self);
+
+        let mut coercion = {
+            let coerce_first = match expected {
+                // We don't coerce to `()` so that if the match expression is a
+                // statement it's branches can have any consistent type. That allows
+                // us to give better error messages (pointing to a usually better
+                // arm for inconsistent arms or to the whole match when a `()` type
+                // is required).
+                Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => ety,
+                _ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
+            };
+            CoerceMany::with_coercion_sites(coerce_first, arms)
         };
 
         for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
@@ -470,11 +487,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
             all_arms_diverge &= self.diverges.get();
 
-            if result_ty.references_error() || arm_ty.references_error() {
-                result_ty = tcx.types.err;
-                continue;
-            }
-
             // Handle the fallback arm of a desugared if-let like a missing else.
             let is_if_let_fallback = match match_src {
                 hir::MatchSource::IfLetDesugar { contains_else_clause: false } => {
@@ -483,47 +495,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 _ => false
             };
 
-            let cause = if is_if_let_fallback {
-                self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse)
+            if is_if_let_fallback {
+                let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
+                assert!(arm_ty.is_nil());
+                coercion.coerce_forced_unit(self, &cause, &mut |_| ());
             } else {
-                self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
+                let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
                     arm_span: arm.body.span,
                     source: match_src
-                })
-            };
-
-            let result = if is_if_let_fallback {
-                self.eq_types(true, &cause, arm_ty, result_ty)
-                    .map(|infer_ok| {
-                        self.register_infer_ok_obligations(infer_ok);
-                        arm_ty
-                    })
-            } else if i == 0 {
-                // Special-case the first arm, as it has no "previous expressions".
-                self.try_coerce(&arm.body, arm_ty, coerce_first)
-            } else {
-                let prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
-                self.try_find_coercion_lub(&cause, prev_arms, result_ty, &arm.body, arm_ty)
-            };
-
-            result_ty = match result {
-                Ok(ty) => ty,
-                Err(e) => {
-                    let (expected, found) = if is_if_let_fallback {
-                        (arm_ty, result_ty)
-                    } else {
-                        (result_ty, arm_ty)
-                    };
-                    self.report_mismatched_types(&cause, expected, found, e).emit();
-                    self.tcx.types.err
-                }
-            };
+                });
+                coercion.coerce(self, &cause, &arm.body, arm_ty, self.diverges.get());
+            }
         }
 
         // We won't diverge unless the discriminant or all arms diverge.
         self.diverges.set(discrim_diverges | all_arms_diverge);
 
-        result_ty
+        coercion.complete(self)
     }
 
     fn check_pat_struct(&self,
diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs
index 1aab4853a4f..647adbbb82f 100644
--- a/src/librustc_typeck/check/autoderef.rs
+++ b/src/librustc_typeck/check/autoderef.rs
@@ -12,6 +12,7 @@ use astconv::AstConv;
 
 use super::FnCtxt;
 
+use check::coercion::AsCoercionSite;
 use rustc::infer::InferOk;
 use rustc::traits;
 use rustc::ty::{self, Ty, TraitRef};
@@ -148,16 +149,16 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
         self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
     }
 
-    pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
-        where I: IntoIterator<Item = &'b hir::Expr>
+    pub fn finalize<E>(self, pref: LvaluePreference, exprs: &[E])
+        where E: AsCoercionSite
     {
         let fcx = self.fcx;
         fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs));
     }
 
-    pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I)
-                                       -> InferOk<'tcx, ()>
-        where I: IntoIterator<Item = &'b hir::Expr>
+    pub fn finalize_as_infer_ok<E>(self, pref: LvaluePreference, exprs: &[E])
+                                   -> InferOk<'tcx, ()>
+        where E: AsCoercionSite
     {
         let methods: Vec<_> = self.steps
             .iter()
@@ -176,6 +177,7 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
                self.obligations);
 
         for expr in exprs {
+            let expr = expr.as_coercion_site();
             debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
             for (n, method) in methods.iter().enumerate() {
                 if let &Some(method) = method {
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 529ee107c46..f9bc947a973 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -55,7 +55,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             })
             .next();
         let callee_ty = autoderef.unambiguous_final_ty();
-        autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr));
+        autoderef.finalize(LvaluePreference::NoPreference, &[callee_expr]);
 
         let output = match result {
             None => {
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 441d427fe49..32b363ed755 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -38,7 +38,7 @@
 //! expression, `e as U2` is not necessarily so (in fact it will only be valid if
 //! `U1` coerces to `U2`).
 
-use super::FnCtxt;
+use super::{Diverges, FnCtxt};
 
 use lint;
 use hir::def_id::DefId;
@@ -56,6 +56,7 @@ use util::common::ErrorReported;
 pub struct CastCheck<'tcx> {
     expr: &'tcx hir::Expr,
     expr_ty: Ty<'tcx>,
+    expr_diverges: Diverges,
     cast_ty: Ty<'tcx>,
     cast_span: Span,
     span: Span,
@@ -115,6 +116,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
     pub fn new(fcx: &FnCtxt<'a, 'gcx, 'tcx>,
                expr: &'tcx hir::Expr,
                expr_ty: Ty<'tcx>,
+               expr_diverges: Diverges,
                cast_ty: Ty<'tcx>,
                cast_span: Span,
                span: Span)
@@ -122,6 +124,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
         let check = CastCheck {
             expr: expr,
             expr_ty: expr_ty,
+            expr_diverges: expr_diverges,
             cast_ty: cast_ty,
             cast_span: cast_span,
             span: span,
@@ -376,7 +379,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
             (None, Some(t_cast)) => {
                 if let ty::TyFnDef(.., f) = self.expr_ty.sty {
                     // Attempt a coercion to a fn pointer type.
-                    let res = fcx.try_coerce(self.expr, self.expr_ty, fcx.tcx.mk_fn_ptr(f));
+                    let res = fcx.try_coerce(self.expr,
+                                             self.expr_ty,
+                                             self.expr_diverges,
+                                             fcx.tcx.mk_fn_ptr(f));
                     if !res.is_ok() {
                         return Err(CastError::NonScalar);
                     }
@@ -542,7 +548,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
     }
 
     fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> bool {
-        fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty).is_ok()
+        fcx.try_coerce(self.expr, self.expr_ty, self.expr_diverges, self.cast_ty).is_ok()
     }
 }
 
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index c43291557f7..a5acd0c7e53 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -60,7 +60,7 @@
 //! sort of a minor point so I've opted to leave it for later---after all
 //! we may want to adjust precisely when coercions occur.
 
-use check::FnCtxt;
+use check::{Diverges, FnCtxt};
 
 use rustc::hir;
 use rustc::hir::def_id::DefId;
@@ -74,8 +74,10 @@ use rustc::ty::fold::TypeFoldable;
 use rustc::ty::error::TypeError;
 use rustc::ty::relate::RelateResult;
 use rustc::ty::subst::Subst;
+use errors::DiagnosticBuilder;
 use syntax::abi;
 use syntax::feature_gate;
+use syntax::ptr::P;
 
 use std::collections::VecDeque;
 use std::ops::Deref;
@@ -155,11 +157,13 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         })
     }
 
-    fn coerce<'a, E, I>(&self, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
-        where E: Fn() -> I,
-              I: IntoIterator<Item = &'a hir::Expr>
+    fn coerce<E>(&self,
+                 exprs: &[E],
+                 a: Ty<'tcx>,
+                 b: Ty<'tcx>)
+                 -> CoerceResult<'tcx>
+        where E: AsCoercionSite
     {
-
         let a = self.shallow_resolve(a);
         debug!("Coerce.tys({:?} => {:?})", a, b);
 
@@ -169,7 +173,23 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         }
 
         if a.is_never() {
-            return success(Adjust::NeverToAny, b, vec![]);
+            // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
+            // type variable, we want `?T` to fallback to `!` if not
+            // otherwise constrained. An example where this arises:
+            //
+            //     let _: Option<?T> = Some({ return; });
+            //
+            // here, we would coerce from `!` to `?T`.
+            let b = self.shallow_resolve(b);
+            return if self.shallow_resolve(b).is_ty_var() {
+                // micro-optimization: no need for this if `b` is
+                // already resolved in some way.
+                let diverging_ty = self.next_diverging_ty_var(
+                    TypeVariableOrigin::AdjustmentType(self.cause.span));
+                self.unify_and(&b, &diverging_ty, Adjust::NeverToAny)
+            } else {
+                success(Adjust::NeverToAny, b, vec![])
+            };
         }
 
         // Consider coercing the subtype to a DST
@@ -223,15 +243,14 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
     /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
     /// To match `A` with `B`, autoderef will be performed,
     /// calling `deref`/`deref_mut` where necessary.
-    fn coerce_borrowed_pointer<'a, E, I>(&self,
-                                         exprs: &E,
-                                         a: Ty<'tcx>,
-                                         b: Ty<'tcx>,
-                                         r_b: &'tcx ty::Region,
-                                         mt_b: TypeAndMut<'tcx>)
-                                         -> CoerceResult<'tcx>
-        where E: Fn() -> I,
-              I: IntoIterator<Item = &'a hir::Expr>
+    fn coerce_borrowed_pointer<E>(&self,
+                                  exprs: &[E],
+                                  a: Ty<'tcx>,
+                                  b: Ty<'tcx>,
+                                  r_b: &'tcx ty::Region,
+                                  mt_b: TypeAndMut<'tcx>)
+                                  -> CoerceResult<'tcx>
+        where E: AsCoercionSite
     {
 
         debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
@@ -408,7 +427,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                autoref);
 
         let pref = LvaluePreference::from_mutbl(mt_b.mutbl);
-        obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs()).obligations);
+        obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs).obligations);
 
         success(Adjust::DerefRef {
             autoderefs: autoderefs,
@@ -675,47 +694,66 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn try_coerce(&self,
                       expr: &hir::Expr,
                       expr_ty: Ty<'tcx>,
+                      expr_diverges: Diverges,
                       target: Ty<'tcx>)
                       -> RelateResult<'tcx, Ty<'tcx>> {
         let source = self.resolve_type_vars_with_obligations(expr_ty);
         debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
 
+        // Special-ish case: we can coerce any type `T` into the `!`
+        // type, but only if the source expression diverges.
+        if target.is_never() && expr_diverges.always() {
+            debug!("permit coercion to `!` because expr diverges");
+            return Ok(target);
+        }
+
         let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
         let coerce = Coerce::new(self, cause);
         self.commit_if_ok(|_| {
-            let ok = coerce.coerce(&|| Some(expr), source, target)?;
+            let ok = coerce.coerce(&[expr], source, target)?;
             let adjustment = self.register_infer_ok_obligations(ok);
             if !adjustment.is_identity() {
                 debug!("Success, coerced with {:?}", adjustment);
-                match self.tables.borrow().adjustments.get(&expr.id) {
-                    None |
-                    Some(&Adjustment { kind: Adjust::NeverToAny, .. }) => (),
-                    _ => bug!("expr already has an adjustment on it!"),
-                };
+                if self.tables.borrow().adjustments.get(&expr.id).is_some() {
+                    bug!("expr already has an adjustment on it!");
+                }
                 self.write_adjustment(expr.id, adjustment);
             }
-            Ok(adjustment.target)
+
+            // We should now have added sufficient adjustments etc to
+            // ensure that the type of expression, post-adjustment, is
+            // a subtype of target.
+            Ok(target)
         })
     }
 
     /// Given some expressions, their known unified type and another expression,
     /// tries to unify the types, potentially inserting coercions on any of the
     /// provided expressions and returns their LUB (aka "common supertype").
-    pub fn try_find_coercion_lub<'b, E, I>(&self,
-                                           cause: &ObligationCause<'tcx>,
-                                           exprs: E,
-                                           prev_ty: Ty<'tcx>,
-                                           new: &'b hir::Expr,
-                                           new_ty: Ty<'tcx>)
-                                           -> RelateResult<'tcx, Ty<'tcx>>
-        where E: Fn() -> I,
-              I: IntoIterator<Item = &'b hir::Expr>
+    ///
+    /// This is really an internal helper. From outside the coercion
+    /// module, you should instantiate a `CoerceMany` instance.
+    fn try_find_coercion_lub<E>(&self,
+                                cause: &ObligationCause<'tcx>,
+                                exprs: &[E],
+                                prev_ty: Ty<'tcx>,
+                                new: &hir::Expr,
+                                new_ty: Ty<'tcx>,
+                                new_diverges: Diverges)
+                                -> RelateResult<'tcx, Ty<'tcx>>
+        where E: AsCoercionSite
     {
-
         let prev_ty = self.resolve_type_vars_with_obligations(prev_ty);
         let new_ty = self.resolve_type_vars_with_obligations(new_ty);
         debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty);
 
+        // Special-ish case: we can coerce any type `T` into the `!`
+        // type, but only if the source expression diverges.
+        if prev_ty.is_never() && new_diverges.always() {
+            debug!("permit coercion to `!` because expr diverges");
+            return Ok(prev_ty);
+        }
+
         let trace = TypeTrace::types(cause, true, prev_ty, new_ty);
 
         // Special-case that coercion alone cannot handle:
@@ -741,7 +779,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
                 // Reify both sides and return the reified fn pointer type.
                 let fn_ptr = self.tcx.mk_fn_ptr(fty);
-                for expr in exprs().into_iter().chain(Some(new)) {
+                for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
                     // No adjustments can produce a fn item, so this should never trip.
                     assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
                     self.write_adjustment(expr.id, Adjustment {
@@ -761,7 +799,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // but only if the new expression has no coercion already applied to it.
         let mut first_error = None;
         if !self.tables.borrow().adjustments.contains_key(&new.id) {
-            let result = self.commit_if_ok(|_| coerce.coerce(&|| Some(new), new_ty, prev_ty));
+            let result = self.commit_if_ok(|_| coerce.coerce(&[new], new_ty, prev_ty));
             match result {
                 Ok(ok) => {
                     let adjustment = self.register_infer_ok_obligations(ok);
@@ -777,7 +815,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // Then try to coerce the previous expressions to the type of the new one.
         // This requires ensuring there are no coercions applied to *any* of the
         // previous expressions, other than noop reborrows (ignoring lifetimes).
-        for expr in exprs() {
+        for expr in exprs {
+            let expr = expr.as_coercion_site();
             let noop = match self.tables.borrow().adjustments.get(&expr.id).map(|adj| adj.kind) {
                 Some(Adjust::DerefRef {
                     autoderefs: 1,
@@ -821,7 +860,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 let adjustment = self.register_infer_ok_obligations(ok);
                 if !adjustment.is_identity() {
                     let mut tables = self.tables.borrow_mut();
-                    for expr in exprs() {
+                    for expr in exprs {
+                        let expr = expr.as_coercion_site();
                         if let Some(&mut Adjustment {
                             kind: Adjust::NeverToAny,
                             ref mut target
@@ -837,3 +877,337 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         }
     }
 }
+
+/// CoerceMany encapsulates the pattern you should use when you have
+/// many expressions that are all getting coerced to a common
+/// type. This arises, for example, when you have a match (the result
+/// of each arm is coerced to a common type). It also arises in less
+/// obvious places, such as when you have many `break foo` expressions
+/// that target the same loop, or the various `return` expressions in
+/// a function.
+///
+/// The basic protocol is as follows:
+///
+/// - Instantiate the `CoerceMany` with an initial `expected_ty`.
+///   This will also serve as the "starting LUB". The expectation is
+///   that this type is something which all of the expressions *must*
+///   be coercible to. Use a fresh type variable if needed.
+/// - For each expression whose result is to be coerced, invoke `coerce()` with.
+///   - In some cases we wish to coerce "non-expressions" whose types are implicitly
+///     unit. This happens for example if you have a `break` with no expression,
+///     or an `if` with no `else`. In that case, invoke `coerce_forced_unit()`.
+///   - `coerce()` and `coerce_forced_unit()` may report errors. They hide this
+///     from you so that you don't have to worry your pretty head about it.
+///     But if an error is reported, the final type will be `err`.
+///   - Invoking `coerce()` may cause us to go and adjust the "adjustments" on
+///     previously coerced expressions.
+/// - When all done, invoke `complete()`. This will return the LUB of
+///   all your expressions.
+///   - WARNING: I don't believe this final type is guaranteed to be
+///     related to your initial `expected_ty` in any particular way,
+///     although it will typically be a subtype, so you should check it.
+///   - Invoking `complete()` may cause us to go and adjust the "adjustments" on
+///     previously coerced expressions.
+///
+/// Example:
+///
+/// ```
+/// let mut coerce = CoerceMany::new(expected_ty);
+/// for expr in exprs {
+///     let expr_ty = fcx.check_expr_with_expectation(expr, expected);
+///     coerce.coerce(fcx, &cause, expr, expr_ty);
+/// }
+/// let final_ty = coerce.complete(fcx);
+/// ```
+pub struct CoerceMany<'gcx, 'tcx, 'exprs, E>
+    where 'gcx: 'tcx, E: 'exprs + AsCoercionSite,
+{
+    expected_ty: Ty<'tcx>,
+    final_ty: Option<Ty<'tcx>>,
+    expressions: Expressions<'gcx, 'exprs, E>,
+    pushed: usize,
+}
+
+/// The type of a `CoerceMany` that is storing up the expressions into
+/// a buffer. We use this in `check/mod.rs` for things like `break`.
+pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'gcx, P<hir::Expr>>;
+
+enum Expressions<'gcx, 'exprs, E>
+    where E: 'exprs + AsCoercionSite,
+{
+    Dynamic(Vec<&'gcx hir::Expr>),
+    UpFront(&'exprs [E]),
+}
+
+impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
+    where 'gcx: 'tcx, E: 'exprs + AsCoercionSite,
+{
+    /// The usual case; collect the set of expressions dynamically.
+    /// If the full set of coercion sites is known before hand,
+    /// consider `with_coercion_sites()` instead to avoid allocation.
+    pub fn new(expected_ty: Ty<'tcx>) -> Self {
+        Self::make(expected_ty, Expressions::Dynamic(vec![]))
+    }
+
+    /// As an optimization, you can create a `CoerceMany` with a
+    /// pre-existing slice of expressions. In this case, you are
+    /// expected to pass each element in the slice to `coerce(...)` in
+    /// order. This is used with arrays in particular to avoid
+    /// needlessly cloning the slice.
+    pub fn with_coercion_sites(expected_ty: Ty<'tcx>,
+                               coercion_sites: &'exprs [E])
+                      -> Self {
+        Self::make(expected_ty, Expressions::UpFront(coercion_sites))
+    }
+
+    fn make(expected_ty: Ty<'tcx>, expressions: Expressions<'gcx, 'exprs, E>) -> Self {
+        CoerceMany {
+            expected_ty,
+            final_ty: None,
+            expressions,
+            pushed: 0,
+        }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.pushed == 0
+    }
+
+    /// Return the "expected type" with which this coercion was
+    /// constructed.  This represents the "downward propagated" type
+    /// that was given to us at the start of typing whatever construct
+    /// we are typing (e.g., the match expression).
+    ///
+    /// Typically, this is used as the expected type when
+    /// type-checking each of the alternative expressions whose types
+    /// we are trying to merge.
+    pub fn expected_ty(&self) -> Ty<'tcx> {
+        self.expected_ty
+    }
+
+    /// Returns the current "merged type", representing our best-guess
+    /// at the LUB of the expressions we've seen so far (if any). This
+    /// isn't *final* until you call `self.final()`, which will return
+    /// the merged type.
+    pub fn merged_ty(&self) -> Ty<'tcx> {
+        self.final_ty.unwrap_or(self.expected_ty)
+    }
+
+    /// Indicates that the value generated by `expression`, which is
+    /// of type `expression_ty`, is one of the possibility that we
+    /// could coerce from. This will record `expression` and later
+    /// calls to `coerce` may come back and add adjustments and things
+    /// if necessary.
+    pub fn coerce<'a>(&mut self,
+                      fcx: &FnCtxt<'a, 'gcx, 'tcx>,
+                      cause: &ObligationCause<'tcx>,
+                      expression: &'gcx hir::Expr,
+                      expression_ty: Ty<'tcx>,
+                      expression_diverges: Diverges)
+    {
+        self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges, None)
+    }
+
+    /// Indicates that one of the inputs is a "forced unit". This
+    /// occurs in a case like `if foo { ... };`, where the issing else
+    /// generates a "forced unit". Another example is a `loop { break;
+    /// }`, where the `break` has no argument expression. We treat
+    /// these cases slightly differently for error-reporting
+    /// purposes. Note that these tend to correspond to cases where
+    /// the `()` expression is implicit in the source, and hence we do
+    /// not take an expression argument.
+    ///
+    /// The `augment_error` gives you a chance to extend the error
+    /// message, in case any results (e.g., we use this to suggest
+    /// removing a `;`).
+    pub fn coerce_forced_unit<'a>(&mut self,
+                                  fcx: &FnCtxt<'a, 'gcx, 'tcx>,
+                                  cause: &ObligationCause<'tcx>,
+                                  augment_error: &mut FnMut(&mut DiagnosticBuilder))
+    {
+        self.coerce_inner(fcx,
+                          cause,
+                          None,
+                          fcx.tcx.mk_nil(),
+                          Diverges::Maybe,
+                          Some(augment_error))
+    }
+
+    /// The inner coercion "engine". If `expression` is `None`, this
+    /// is a forced-unit case, and hence `expression_ty` must be
+    /// `Nil`.
+    fn coerce_inner<'a>(&mut self,
+                        fcx: &FnCtxt<'a, 'gcx, 'tcx>,
+                        cause: &ObligationCause<'tcx>,
+                        expression: Option<&'gcx hir::Expr>,
+                        mut expression_ty: Ty<'tcx>,
+                        expression_diverges: Diverges,
+                        augment_error: Option<&mut FnMut(&mut DiagnosticBuilder)>)
+    {
+        // Incorporate whatever type inference information we have
+        // until now; in principle we might also want to process
+        // pending obligations, but doing so should only improve
+        // compatibility (hopefully that is true) by helping us
+        // uncover never types better.
+        if expression_ty.is_ty_var() {
+            expression_ty = fcx.infcx.shallow_resolve(expression_ty);
+        }
+
+        // If we see any error types, just propagate that error
+        // upwards.
+        if expression_ty.references_error() || self.merged_ty().references_error() {
+            self.final_ty = Some(fcx.tcx.types.err);
+            return;
+        }
+
+        // Handle the actual type unification etc.
+        let result = if let Some(expression) = expression {
+            if self.pushed == 0 {
+                // Special-case the first expression we are coercing.
+                // To be honest, I'm not entirely sure why we do this.
+                fcx.try_coerce(expression, expression_ty, expression_diverges, self.expected_ty)
+            } else {
+                match self.expressions {
+                    Expressions::Dynamic(ref exprs) =>
+                        fcx.try_find_coercion_lub(cause,
+                                                  exprs,
+                                                  self.merged_ty(),
+                                                  expression,
+                                                  expression_ty,
+                                                  expression_diverges),
+                    Expressions::UpFront(ref coercion_sites) =>
+                        fcx.try_find_coercion_lub(cause,
+                                                  &coercion_sites[0..self.pushed],
+                                                  self.merged_ty(),
+                                                  expression,
+                                                  expression_ty,
+                                                  expression_diverges),
+                }
+            }
+        } else {
+            // this is a hack for cases where we default to `()` because
+            // the expression etc has been omitted from the source. An
+            // example is an `if let` without an else:
+            //
+            //     if let Some(x) = ... { }
+            //
+            // we wind up with a second match arm that is like `_ =>
+            // ()`.  That is the case we are considering here. We take
+            // a different path to get the right "expected, found"
+            // message and so forth (and because we know that
+            // `expression_ty` will be unit).
+            //
+            // Another example is `break` with no argument expression.
+            assert!(expression_ty.is_nil());
+            assert!(expression_ty.is_nil(), "if let hack without unit type");
+            fcx.eq_types(true, cause, expression_ty, self.merged_ty())
+               .map(|infer_ok| {
+                   fcx.register_infer_ok_obligations(infer_ok);
+                   expression_ty
+               })
+        };
+
+        match result {
+            Ok(v) => {
+                self.final_ty = Some(v);
+                if let Some(e) = expression {
+                    match self.expressions {
+                        Expressions::Dynamic(ref mut buffer) => buffer.push(e),
+                        Expressions::UpFront(coercion_sites) => {
+                            // if the user gave us an array to validate, check that we got
+                            // the next expression in the list, as expected
+                            assert_eq!(coercion_sites[self.pushed].as_coercion_site().id, e.id);
+                        }
+                    }
+                    self.pushed += 1;
+                }
+            }
+            Err(err) => {
+                let (expected, found) = if expression.is_none() {
+                    // In the case where this is a "forced unit", like
+                    // `break`, we want to call the `()` "expected"
+                    // since it is implied by the syntax.
+                    assert!(expression_ty.is_nil());
+                    (expression_ty, self.final_ty.unwrap_or(self.expected_ty))
+                } else {
+                    // Otherwise, the "expected" type for error
+                    // reporting is the current unification type,
+                    // which is basically the LUB of the expressions
+                    // we've seen so far (combined with the expected
+                    // type)
+                    (self.final_ty.unwrap_or(self.expected_ty), expression_ty)
+                };
+
+                let mut db;
+                match cause.code {
+                    ObligationCauseCode::ReturnNoExpression => {
+                        db = struct_span_err!(
+                            fcx.tcx.sess, cause.span, E0069,
+                            "`return;` in a function whose return type is not `()`");
+                        db.span_label(cause.span, &format!("return type is not ()"));
+                    }
+                    _ => {
+                        db = fcx.report_mismatched_types(cause, expected, found, err);
+                    }
+                }
+
+                if let Some(mut augment_error) = augment_error {
+                    augment_error(&mut db);
+                }
+
+                db.emit();
+
+                self.final_ty = Some(fcx.tcx.types.err);
+            }
+        }
+    }
+
+    pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
+        if let Some(final_ty) = self.final_ty {
+            final_ty
+        } else {
+            // If we only had inputs that were of type `!` (or no
+            // inputs at all), then the final type is `!`.
+            assert_eq!(self.pushed, 0);
+            fcx.tcx.types.never
+        }
+    }
+}
+
+/// Something that can be converted into an expression to which we can
+/// apply a coercion.
+pub trait AsCoercionSite {
+    fn as_coercion_site(&self) -> &hir::Expr;
+}
+
+impl AsCoercionSite for hir::Expr {
+    fn as_coercion_site(&self) -> &hir::Expr {
+        self
+    }
+}
+
+impl AsCoercionSite for P<hir::Expr> {
+    fn as_coercion_site(&self) -> &hir::Expr {
+        self
+    }
+}
+
+impl<'a, T> AsCoercionSite for &'a T
+    where T: AsCoercionSite
+{
+    fn as_coercion_site(&self) -> &hir::Expr {
+        (**self).as_coercion_site()
+    }
+}
+
+impl AsCoercionSite for ! {
+    fn as_coercion_site(&self) -> &hir::Expr {
+        unreachable!()
+    }
+}
+
+impl AsCoercionSite for hir::Arm {
+    fn as_coercion_site(&self) -> &hir::Expr {
+        &self.body
+    }
+}
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index 0e9abaf1cf9..905d8688ea1 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -362,7 +362,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 &infcx.parameter_environment.caller_bounds);
             infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id);
         } else {
-            let fcx = FnCtxt::new(&inh, Some(tcx.types.err), impl_m_body_id);
+            let fcx = FnCtxt::new(&inh, impl_m_body_id);
             fcx.regionck_item(impl_m_body_id, impl_m_span, &[]);
         }
 
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 232c4c4db7c..e922c7447ff 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -67,9 +67,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     }
 
     // Checks that the type of `expr` can be coerced to `expected`.
-    pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) {
+    //
+    // NB: This code relies on `self.diverges` to be accurate.  In
+    // particular, assignments to `!` will be permitted if the
+    // diverges flag is currently "always".
+    pub fn demand_coerce(&self,
+                         expr: &hir::Expr,
+                         checked_ty: Ty<'tcx>,
+                         expected: Ty<'tcx>) {
         let expected = self.resolve_type_vars_with_obligations(expected);
-        if let Err(e) = self.try_coerce(expr, checked_ty, expected) {
+
+        if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
             let cause = self.misc(expr.span);
             let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
             let mode = probe::Mode::MethodCall;
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index e6e4b577bd5..73f6cd76290 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -137,7 +137,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
         assert_eq!(n, pick.autoderefs);
 
         autoderef.unambiguous_final_ty();
-        autoderef.finalize(LvaluePreference::NoPreference, Some(self.self_expr));
+        autoderef.finalize(LvaluePreference::NoPreference, &[self.self_expr]);
 
         let target = pick.unsize.unwrap_or(autoderefd_ty);
         let target = target.adjust_for_autoref(self.tcx, autoref);
@@ -444,7 +444,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
                                       "expr was deref-able {} times but now isn't?",
                                       autoderefs);
                         });
-                        autoderef.finalize(PreferMutLvalue, Some(expr));
+                        autoderef.finalize(PreferMutLvalue, &[expr]);
                     }
                 }
                 Some(_) | None => {}
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index afa1e0557dc..aaa3cf0f29e 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -77,6 +77,7 @@ type parameter).
 */
 
 pub use self::Expectation::*;
+use self::coercion::{CoerceMany, DynamicCoerceMany};
 pub use self::compare_method::{compare_impl_method, compare_const_impl};
 use self::TupleArgumentsFlag::*;
 
@@ -85,7 +86,8 @@ use dep_graph::DepNode;
 use fmt_macros::{Parser, Piece, Position};
 use hir::def::{Def, CtorKind};
 use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin, TypeTrace};
+use rustc_back::slice::ref_slice;
+use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
 use rustc::infer::type_variable::{self, TypeVariableOrigin};
 use rustc::ty::subst::{Kind, Subst, Substs};
 use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
@@ -97,6 +99,7 @@ use rustc::ty::adjustment;
 use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
 use rustc::ty::maps::Providers;
 use rustc::ty::util::{Representability, IntTypeExt};
+use errors::DiagnosticBuilder;
 use require_c_abi_if_variadic;
 use session::{Session, CompileResult};
 use TypeAndSubsts;
@@ -299,12 +302,23 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
         }
     }
 
+    /// It sometimes happens that we want to turn an expectation into
+    /// a **hard constraint** (i.e., something that must be satisfied
+    /// for the program to type-check). `only_has_type` will return
+    /// such a constraint, if it exists.
     fn only_has_type(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Option<Ty<'tcx>> {
         match self.resolve(fcx) {
             ExpectHasType(ty) => Some(ty),
             _ => None
         }
     }
+
+    /// Like `only_has_type`, but instead of returning `None` if no
+    /// hard constraint exists, creates a fresh type variable.
+    fn coercion_target_type(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, span: Span) -> Ty<'tcx> {
+        self.only_has_type(fcx)
+            .unwrap_or_else(|| fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span)))
+    }
 }
 
 #[derive(Copy, Clone)]
@@ -348,12 +362,13 @@ impl UnsafetyState {
     }
 }
 
-/// Whether a node ever exits normally or not.
-/// Tracked semi-automatically (through type variables
-/// marked as diverging), with some manual adjustments
-/// for control-flow primitives (approximating a CFG).
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-enum Diverges {
+/// Tracks whether executing a node may exit normally (versus
+/// return/break/panic, which "diverge", leaving dead code in their
+/// wake). Tracked semi-automatically (through type variables marked
+/// as diverging), with some manual adjustments for control-flow
+/// primitives (approximating a CFG).
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Diverges {
     /// Potentially unknown, some cases converge,
     /// others require a CFG to determine them.
     Maybe,
@@ -401,34 +416,28 @@ impl Diverges {
     }
 }
 
-#[derive(Clone)]
-pub struct BreakableCtxt<'gcx, 'tcx> {
-    unified: Ty<'tcx>,
-    coerce_to: Ty<'tcx>,
-    break_exprs: Vec<&'gcx hir::Expr>,
+pub struct BreakableCtxt<'gcx: 'tcx, 'tcx> {
     may_break: bool,
+
+    // this is `null` for loops where break with a value is illegal,
+    // such as `while`, `for`, and `while let`
+    coerce: Option<DynamicCoerceMany<'gcx, 'tcx>>,
 }
 
-#[derive(Clone)]
-pub struct EnclosingBreakables<'gcx, 'tcx> {
+pub struct EnclosingBreakables<'gcx: 'tcx, 'tcx> {
     stack: Vec<BreakableCtxt<'gcx, 'tcx>>,
     by_id: NodeMap<usize>,
 }
 
 impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> {
-    fn find_breakable(&mut self, target: hir::ScopeTarget)
-        -> Option<&mut BreakableCtxt<'gcx, 'tcx>>
-    {
-        let opt_index = target.opt_id().and_then(|id| self.by_id.get(&id).cloned());
-        if let Some(ix) = opt_index {
-            Some(&mut self.stack[ix])
-        } else {
-            None
-        }
+    fn find_breakable(&mut self, target_id: ast::NodeId) -> &mut BreakableCtxt<'gcx, 'tcx> {
+        let ix = *self.by_id.get(&target_id).unwrap_or_else(|| {
+            bug!("could not find enclosing breakable with id {}", target_id);
+        });
+        &mut self.stack[ix]
     }
 }
 
-#[derive(Clone)]
 pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     ast_ty_to_ty_cache: RefCell<NodeMap<Ty<'tcx>>>,
 
@@ -440,11 +449,49 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     // expects the types within the function to be consistent.
     err_count_on_creation: usize,
 
-    ret_ty: Option<Ty<'tcx>>,
+    ret_coercion: Option<RefCell<DynamicCoerceMany<'gcx, 'tcx>>>,
 
     ps: RefCell<UnsafetyState>,
 
-    /// Whether the last checked node can ever exit.
+    /// Whether the last checked node generates a divergence (e.g.,
+    /// `return` will set this to Always). In general, when entering
+    /// an expression or other node in the tree, the initial value
+    /// indicates whether prior parts of the containing expression may
+    /// have diverged. It is then typically set to `Maybe` (and the
+    /// old value remembered) for processing the subparts of the
+    /// current expression. As each subpart is processed, they may set
+    /// the flag to `Always` etc.  Finally, at the end, we take the
+    /// result and "union" it with the original value, so that when we
+    /// return the flag indicates if any subpart of the the parent
+    /// expression (up to and including this part) has diverged.  So,
+    /// if you read it after evaluating a subexpression `X`, the value
+    /// you get indicates whether any subexpression that was
+    /// evaluating up to and including `X` diverged.
+    ///
+    /// We use this flag for two purposes:
+    ///
+    /// - To warn about unreachable code: if, after processing a
+    ///   sub-expression but before we have applied the effects of the
+    ///   current node, we see that the flag is set to `Always`, we
+    ///   can issue a warning. This corresponds to something like
+    ///   `foo(return)`; we warn on the `foo()` expression. (We then
+    ///   update the flag to `WarnedAlways` to suppress duplicate
+    ///   reports.) Similarly, if we traverse to a fresh statement (or
+    ///   tail expression) from a `Always` setting, we will isssue a
+    ///   warning. This corresponds to something like `{return;
+    ///   foo();}` or `{return; 22}`, where we would warn on the
+    ///   `foo()` or `22`.
+    ///
+    /// - To permit assignment into a local variable or other lvalue
+    ///   (including the "return slot") of type `!`.  This is allowed
+    ///   if **either** the type of value being assigned is `!`, which
+    ///   means the current code is dead, **or** the expression's
+    ///   divering flag is true, which means that a divering value was
+    ///   wrapped (e.g., `let x: ! = foo(return)`).
+    ///
+    /// To repeat the last point: an expression represents dead-code
+    /// if, after checking it, **either** its type is `!` OR the
+    /// diverges flag is set to something other than `Maybe`.
     diverges: Cell<Diverges>,
 
     /// Whether any child nodes have any type errors.
@@ -671,7 +718,7 @@ fn typeck_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
             check_fn(&inh, fn_sig, decl, id, body)
         } else {
-            let fcx = FnCtxt::new(&inh, None, body.value.id);
+            let fcx = FnCtxt::new(&inh, body.value.id);
             let expected_type = tcx.item_type(def_id);
             let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type);
             fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
@@ -792,15 +839,16 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
 
     // Create the function context.  This is either derived from scratch or,
     // in the case of function expressions, based on the outer context.
-    let mut fcx = FnCtxt::new(inherited, None, body.value.id);
-    let ret_ty = fn_sig.output();
+    let mut fcx = FnCtxt::new(inherited, body.value.id);
     *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
 
+    let ret_ty = fn_sig.output();
     fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
-    fcx.ret_ty = fcx.instantiate_anon_types(&Some(ret_ty));
+    let ret_ty = fcx.instantiate_anon_types(&ret_ty);
+    fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
     fn_sig = fcx.tcx.mk_fn_sig(
         fn_sig.inputs().iter().cloned(),
-        fcx.ret_ty.unwrap(),
+        ret_ty,
         fn_sig.variadic,
         fn_sig.unsafety,
         fn_sig.abi
@@ -825,7 +873,38 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
 
     inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
 
-    fcx.check_expr_coercable_to_type(&body.value, fcx.ret_ty.unwrap());
+    fcx.check_return_expr(&body.value);
+
+    // Finalize the return check by taking the LUB of the return types
+    // we saw and assigning it to the expected return type. This isn't
+    // really expected to fail, since the coercions would have failed
+    // earlier when trying to find a LUB.
+    //
+    // However, the behavior around `!` is sort of complex. In the
+    // event that the `actual_return_ty` comes back as `!`, that
+    // indicates that the fn either does not return or "returns" only
+    // values of type `!`. In this case, if there is an expected
+    // return type that is *not* `!`, that should be ok. But if the
+    // return type is being inferred, we want to "fallback" to `!`:
+    //
+    //     let x = move || panic!();
+    //
+    // To allow for that, I am creating a type variable with diverging
+    // fallback. This was deemed ever so slightly better than unifying
+    // the return value with `!` because it allows for the caller to
+    // make more assumptions about the return type (e.g., they could do
+    //
+    //     let y: Option<u32> = Some(x());
+    //
+    // which would then cause this return type to become `u32`, not
+    // `!`).
+    let coercion = fcx.ret_coercion.take().unwrap().into_inner();
+    let mut actual_return_ty = coercion.complete(&fcx);
+    if actual_return_ty.is_never() {
+        actual_return_ty = fcx.next_diverging_ty_var(
+            TypeVariableOrigin::DivergingFn(body.value.span));
+    }
+    fcx.demand_suptype(body.value.span, ret_ty, actual_return_ty);
 
     fcx
 }
@@ -1421,14 +1500,13 @@ enum TupleArgumentsFlag {
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>,
-               rty: Option<Ty<'tcx>>,
                body_id: ast::NodeId)
                -> FnCtxt<'a, 'gcx, 'tcx> {
         FnCtxt {
             ast_ty_to_ty_cache: RefCell::new(NodeMap()),
             body_id: body_id,
             err_count_on_creation: inh.tcx.sess.err_count(),
-            ret_ty: rty,
+            ret_coercion: None,
             ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal,
                                                      ast::CRATE_NODE_ID)),
             diverges: Cell::new(Diverges::Maybe),
@@ -1455,6 +1533,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         if self.diverges.get() == Diverges::Always {
             self.diverges.set(Diverges::WarnedAlways);
 
+            debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
+
             self.tables.borrow_mut().lints.add_lint(
                 lint::builtin::UNREACHABLE_CODE,
                 id, span,
@@ -1537,18 +1617,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     #[inline]
     pub fn write_ty(&self, node_id: ast::NodeId, ty: Ty<'tcx>) {
         debug!("write_ty({}, {:?}) in fcx {}",
-               node_id, ty, self.tag());
+               node_id, self.resolve_type_vars_if_possible(&ty), self.tag());
         self.tables.borrow_mut().node_types.insert(node_id, ty);
 
         if ty.references_error() {
             self.has_errors.set(true);
             self.set_tainted_by_errors();
         }
-
-        // FIXME(canndrew): This is_never should probably be an is_uninhabited
-        if ty.is_never() || self.type_var_diverges(ty) {
-            self.diverges.set(self.diverges.get() | Diverges::Always);
-        }
     }
 
     pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts<'tcx>) {
@@ -2177,12 +2252,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 expr, base_expr, adj_ty, autoderefs,
                 false, lvalue_pref, idx_ty)
             {
-                autoderef.finalize(lvalue_pref, Some(base_expr));
+                autoderef.finalize(lvalue_pref, &[base_expr]);
                 return Some(final_mt);
             }
 
             if let ty::TyArray(element_ty, _) = adj_ty.sty {
-                autoderef.finalize(lvalue_pref, Some(base_expr));
+                autoderef.finalize(lvalue_pref, &[base_expr]);
                 let adjusted_ty = self.tcx.mk_slice(element_ty);
                 return self.try_index_step(
                     MethodCall::expr(expr.id), expr, base_expr,
@@ -2479,8 +2554,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     Expectation::rvalue_hint(self, ty)
                 });
 
-                let checked_ty = self.check_expr_with_expectation(&arg,
-                                        expected.unwrap_or(ExpectHasType(formal_ty)));
+                let checked_ty = self.check_expr_with_expectation(
+                    &arg,
+                    expected.unwrap_or(ExpectHasType(formal_ty)));
+
                 // 2. Coerce to the most detailed type that could be coerced
                 //    to, which is `expected_ty` if `rvalue_hint` returns an
                 //    `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
@@ -2599,7 +2676,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn check_expr_has_type(&self,
                                expr: &'gcx hir::Expr,
                                expected: Ty<'tcx>) -> Ty<'tcx> {
-        let ty = self.check_expr_with_hint(expr, expected);
+        let mut ty = self.check_expr_with_hint(expr, expected);
+
+        // While we don't allow *arbitrary* coercions here, we *do* allow
+        // coercions from ! to `expected`.
+        if ty.is_never() {
+            assert!(!self.tables.borrow().adjustments.contains_key(&expr.id),
+                    "expression with never type wound up being adjusted");
+            let adj_ty = self.next_diverging_ty_var(
+                TypeVariableOrigin::AdjustmentType(expr.span));
+            self.write_adjustment(expr.id, adjustment::Adjustment {
+                kind: adjustment::Adjust::NeverToAny,
+                target: adj_ty
+            });
+            ty = adj_ty;
+        }
+
         self.demand_suptype(expr.span, expected, ty);
         ty
     }
@@ -2735,11 +2827,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         ret_ty
     }
 
+    fn check_return_expr(&self, return_expr: &'gcx hir::Expr) {
+        let ret_coercion =
+            self.ret_coercion
+                .as_ref()
+                .unwrap_or_else(|| span_bug!(return_expr.span,
+                                             "check_return_expr called outside fn body"));
+
+        let ret_ty = ret_coercion.borrow().expected_ty();
+        let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
+        ret_coercion.borrow_mut()
+                    .coerce(self,
+                            &self.misc(return_expr.span),
+                            return_expr,
+                            return_expr_ty,
+                            self.diverges.get());
+    }
+
+
     // A generic function for checking the then and else in an if
     // or if-else.
     fn check_then_else(&self,
                        cond_expr: &'gcx hir::Expr,
-                       then_blk: &'gcx hir::Block,
+                       then_expr: &'gcx hir::Expr,
                        opt_else_expr: Option<&'gcx hir::Expr>,
                        sp: Span,
                        expected: Expectation<'tcx>) -> Ty<'tcx> {
@@ -2748,71 +2858,43 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         self.diverges.set(Diverges::Maybe);
 
         let expected = expected.adjust_for_branches(self);
-        let then_ty = self.check_block_with_expected(then_blk, expected);
+        let then_ty = self.check_expr_with_expectation(then_expr, expected);
         let then_diverges = self.diverges.get();
         self.diverges.set(Diverges::Maybe);
 
-        let unit = self.tcx.mk_nil();
-        let (cause, expected_ty, found_ty, result);
+        // We've already taken the expected type's preferences
+        // into account when typing the `then` branch. To figure
+        // out the initial shot at a LUB, we thus only consider
+        // `expected` if it represents a *hard* constraint
+        // (`only_has_type`); otherwise, we just go with a
+        // fresh type variable.
+        let coerce_to_ty = expected.coercion_target_type(self, sp);
+        let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
+
+        let if_cause = self.cause(sp, ObligationCauseCode::IfExpression);
+        coerce.coerce(self, &if_cause, then_expr, then_ty, then_diverges);
+
         if let Some(else_expr) = opt_else_expr {
             let else_ty = self.check_expr_with_expectation(else_expr, expected);
             let else_diverges = self.diverges.get();
-            cause = self.cause(sp, ObligationCauseCode::IfExpression);
-
-            // Only try to coerce-unify if we have a then expression
-            // to assign coercions to, otherwise it's () or diverging.
-            expected_ty = then_ty;
-            found_ty = else_ty;
-            result = if let Some(ref then) = then_blk.expr {
-                let res = self.try_find_coercion_lub(&cause, || Some(&**then),
-                                                     then_ty, else_expr, else_ty);
-
-                // In case we did perform an adjustment, we have to update
-                // the type of the block, because old trans still uses it.
-                if res.is_ok() {
-                    let adj = self.tables.borrow().adjustments.get(&then.id).cloned();
-                    if let Some(adj) = adj {
-                        self.write_ty(then_blk.id, adj.target);
-                    }
-                }
 
-                res
-            } else {
-                self.commit_if_ok(|_| {
-                    let trace = TypeTrace::types(&cause, true, then_ty, else_ty);
-                    self.lub(true, trace, &then_ty, &else_ty)
-                        .map(|ok| self.register_infer_ok_obligations(ok))
-                })
-            };
+            coerce.coerce(self, &if_cause, else_expr, else_ty, else_diverges);
 
             // We won't diverge unless both branches do (or the condition does).
             self.diverges.set(cond_diverges | then_diverges & else_diverges);
         } else {
+            let else_cause = self.cause(sp, ObligationCauseCode::IfExpressionWithNoElse);
+            coerce.coerce_forced_unit(self, &else_cause, &mut |_| ());
+
             // If the condition is false we can't diverge.
             self.diverges.set(cond_diverges);
-
-            cause = self.cause(sp, ObligationCauseCode::IfExpressionWithNoElse);
-            expected_ty = unit;
-            found_ty = then_ty;
-            result = self.eq_types(true, &cause, unit, then_ty)
-                         .map(|ok| {
-                             self.register_infer_ok_obligations(ok);
-                             unit
-                         });
         }
 
-        match result {
-            Ok(ty) => {
-                if cond_ty.references_error() {
-                    self.tcx.types.err
-                } else {
-                    ty
-                }
-            }
-            Err(e) => {
-                self.report_mismatched_types(&cause, expected_ty, found_ty, e).emit();
-                self.tcx.types.err
-            }
+        let result_ty = coerce.complete(self);
+        if cond_ty.references_error() {
+            self.tcx.types.err
+        } else {
+            result_ty
         }
     }
 
@@ -2834,7 +2916,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
                         let field_ty = self.field_ty(expr.span, field, substs);
                         if self.tcx.vis_is_accessible_from(field.vis, self.body_id) {
-                            autoderef.finalize(lvalue_pref, Some(base));
+                            autoderef.finalize(lvalue_pref, &[base]);
                             self.write_autoderef_adjustment(base.id, autoderefs, base_t);
 
                             self.tcx.check_stability(field.did, expr.id, expr.span);
@@ -2958,7 +3040,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             };
 
             if let Some(field_ty) = field {
-                autoderef.finalize(lvalue_pref, Some(base));
+                autoderef.finalize(lvalue_pref, &[base]);
                 self.write_autoderef_adjustment(base.id, autoderefs, base_t);
                 return field_ty;
             }
@@ -3299,6 +3381,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             _ => self.warn_if_unreachable(expr.id, expr.span, "expression")
         }
 
+        // Any expression that produces a value of type `!` must have diverged
+        if ty.is_never() {
+            self.diverges.set(self.diverges.get() | Diverges::Always);
+        }
+
         // Record the type, which applies it effects.
         // We need to do this after the warning above, so that
         // we don't warn for the diverging expression itself.
@@ -3311,18 +3398,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         debug!("type of {} is...", self.tcx.hir.node_to_string(expr.id));
         debug!("... {:?}, expected is {:?}", ty, expected);
 
-        // Add adjustments to !-expressions
-        if ty.is_never() {
-            if let Some(hir::map::NodeExpr(node_expr)) = self.tcx.hir.find(expr.id) {
-                let adj_ty = self.next_diverging_ty_var(
-                    TypeVariableOrigin::AdjustmentType(node_expr.span));
-                self.write_adjustment(expr.id, adjustment::Adjustment {
-                    kind: adjustment::Adjust::NeverToAny,
-                    target: adj_ty
-                });
-                return adj_ty;
-            }
-        }
         ty
     }
 
@@ -3485,81 +3560,82 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
               tcx.mk_nil()
           }
           hir::ExprBreak(destination, ref expr_opt) => {
-            let coerce_to = {
-                let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
-                enclosing_breakables
-                    .find_breakable(destination.target_id).map(|ctxt| ctxt.coerce_to)
-            };
-            if let Some(coerce_to) = coerce_to {
-                let e_ty;
-                let cause;
-                if let Some(ref e) = *expr_opt {
-                    // Recurse without `enclosing_loops` borrowed.
-                    e_ty = self.check_expr_with_hint(e, coerce_to);
-                    cause = self.misc(e.span);
-                    // Notably, the recursive call may alter coerce_to - must not keep using it!
-                } else {
-                    // `break` without argument acts like `break ()`.
-                    e_ty = tcx.mk_nil();
-                    cause = self.misc(expr.span);
-                }
-
-                let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
-                let ctxt = enclosing_breakables.find_breakable(destination.target_id).unwrap();
+              if let Some(target_id) = destination.target_id.opt_id() {
+                  let (e_ty, e_diverges, cause);
+                  if let Some(ref e) = *expr_opt {
+                      // If this is a break with a value, we need to type-check
+                      // the expression. Get an expected type from the loop context.
+                      let opt_coerce_to = {
+                          let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+                          enclosing_breakables.find_breakable(target_id)
+                                              .coerce
+                                              .as_ref()
+                                              .map(|coerce| coerce.expected_ty())
+                      };
+
+                      // If the loop context is not a `loop { }`, then break with
+                      // a value is illegal, and `opt_coerce_to` will be `None`.
+                      // Just set expectation to error in that case.
+                      let coerce_to = opt_coerce_to.unwrap_or(tcx.types.err);
+
+                      // Recurse without `enclosing_breakables` borrowed.
+                      e_ty = self.check_expr_with_hint(e, coerce_to);
+                      e_diverges = self.diverges.get();
+                      cause = self.misc(e.span);
+                  } else {
+                      // Otherwise, this is a break *without* a value. That's
+                      // always legal, and is equivalent to `break ()`.
+                      e_ty = tcx.mk_nil();
+                      e_diverges = Diverges::Maybe;
+                      cause = self.misc(expr.span);
+                  }
 
-                let result = if let Some(ref e) = *expr_opt {
-                    // Special-case the first element, as it has no "previous expressions".
-                    let result = if !ctxt.may_break {
-                        self.try_coerce(e, e_ty, ctxt.coerce_to)
-                    } else {
-                        self.try_find_coercion_lub(&cause, || ctxt.break_exprs.iter().cloned(),
-                                                   ctxt.unified, e, e_ty)
-                    };
+                  // Now that we have type-checked `expr_opt`, borrow
+                  // the `enclosing_loops` field and let's coerce the
+                  // type of `expr_opt` into what is expected.
+                  let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+                  let ctxt = enclosing_breakables.find_breakable(target_id);
+                  if let Some(ref mut coerce) = ctxt.coerce {
+                      if let Some(ref e) = *expr_opt {
+                          coerce.coerce(self, &cause, e, e_ty, e_diverges);
+                      } else {
+                          assert!(e_ty.is_nil());
+                          coerce.coerce_forced_unit(self, &cause, &mut |_| ());
+                      }
+                  } else {
+                      // If `ctxt.coerce` is `None`, we can just ignore
+                      // the type of the expresison.  This is because
+                      // either this was a break *without* a value, in
+                      // which case it is always a legal type (`()`), or
+                      // else an error would have been flagged by the
+                      // `loops` pass for using break with an expression
+                      // where you are not supposed to.
+                      assert!(expr_opt.is_none() || self.tcx.sess.err_count() > 0);
+                  }
 
-                    ctxt.break_exprs.push(e);
-                    result
-                } else {
-                    self.eq_types(true, &cause, e_ty, ctxt.unified)
-                        .map(|InferOk { obligations, .. }| {
-                            // FIXME(#32730) propagate obligations
-                            assert!(obligations.is_empty());
-                            e_ty
-                        })
-                };
-                match result {
-                    Ok(ty) => ctxt.unified = ty,
-                    Err(err) => {
-                        self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit();
-                    }
-                }
+                  ctxt.may_break = true;
+              } else {
+                  // Otherwise, we failed to find the enclosing loop;
+                  // this can only happen if the `break` was not
+                  // inside a loop at all, which is caught by the
+                  // loop-checking pass.
+                  assert!(self.tcx.sess.err_count() > 0);
+              }
 
-                ctxt.may_break = true;
-            }
-            // Otherwise, we failed to find the enclosing breakable; this can only happen if the
-            // `break` target was not found, which is caught in HIR lowering and reported by the
-            // loop-checking pass.
-            tcx.types.never
+              // the type of a `break` is always `!`, since it diverges
+              tcx.types.never
           }
           hir::ExprAgain(_) => { tcx.types.never }
           hir::ExprRet(ref expr_opt) => {
-            if self.ret_ty.is_none() {
+            if self.ret_coercion.is_none() {
                 struct_span_err!(self.tcx.sess, expr.span, E0572,
                                  "return statement outside of function body").emit();
             } else if let Some(ref e) = *expr_opt {
-                self.check_expr_coercable_to_type(&e, self.ret_ty.unwrap());
+                self.check_return_expr(e);
             } else {
-                match self.eq_types(false,
-                                    &self.misc(expr.span),
-                                    self.ret_ty.unwrap(),
-                                    tcx.mk_nil()) {
-                    Ok(ok) => self.register_infer_ok_obligations(ok),
-                    Err(_) => {
-                        struct_span_err!(tcx.sess, expr.span, E0069,
-                                         "`return;` in a function whose return type is not `()`")
-                            .span_label(expr.span, &format!("return type is not ()"))
-                            .emit();
-                    }
-                }
+                let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
+                let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression);
+                coercion.coerce_forced_unit(self, &cause, &mut |_| ());
             }
             tcx.types.never
           }
@@ -3587,56 +3663,64 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 tcx.mk_nil()
             }
           }
-          hir::ExprIf(ref cond, ref then_blk, ref opt_else_expr) => {
-            self.check_then_else(&cond, &then_blk, opt_else_expr.as_ref().map(|e| &**e),
-                                 expr.span, expected)
+          hir::ExprIf(ref cond, ref then_expr, ref opt_else_expr) => {
+              self.check_then_else(&cond, then_expr, opt_else_expr.as_ref().map(|e| &**e),
+                                   expr.span, expected)
           }
           hir::ExprWhile(ref cond, ref body, _) => {
-            let unified = self.tcx.mk_nil();
-            let coerce_to = unified;
-            let ctxt = BreakableCtxt {
-                unified: unified,
-                coerce_to: coerce_to,
-                break_exprs: vec![],
-                may_break: true,
-            };
-            self.with_breakable_ctxt(expr.id, ctxt, || {
-                self.check_expr_has_type(&cond, tcx.types.bool);
-                let cond_diverging = self.diverges.get();
-                self.check_block_no_value(&body);
+              let ctxt = BreakableCtxt {
+                  // cannot use break with a value from a while loop
+                  coerce: None,
+                  may_break: true,
+              };
 
-                // We may never reach the body so it diverging means nothing.
-                self.diverges.set(cond_diverging);
-            });
+              self.with_breakable_ctxt(expr.id, ctxt, || {
+                  self.check_expr_has_type(&cond, tcx.types.bool);
+                  let cond_diverging = self.diverges.get();
+                  self.check_block_no_value(&body);
 
-            if self.has_errors.get() {
-                tcx.types.err
-            } else {
-                tcx.mk_nil()
-            }
+                  // We may never reach the body so it diverging means nothing.
+                  self.diverges.set(cond_diverging);
+              });
+
+              self.tcx.mk_nil()
           }
-          hir::ExprLoop(ref body, _, _) => {
-            let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(body.span));
-            let coerce_to = expected.only_has_type(self).unwrap_or(unified);
-            let ctxt = BreakableCtxt {
-                unified: unified,
-                coerce_to: coerce_to,
-                break_exprs: vec![],
-                may_break: false,
-            };
+          hir::ExprLoop(ref body, _, source) => {
+              let coerce = match source {
+                  // you can only use break with a value from a normal `loop { }`
+                  hir::LoopSource::Loop => {
+                      let coerce_to = expected.coercion_target_type(self, body.span);
+                      Some(CoerceMany::new(coerce_to))
+                  }
 
-            let (ctxt, ()) = self.with_breakable_ctxt(expr.id, ctxt, || {
-                self.check_block_no_value(&body);
-            });
-            if ctxt.may_break {
-                // No way to know whether it's diverging because
-                // of a `break` or an outer `break` or `return.
-                self.diverges.set(Diverges::Maybe);
+                  hir::LoopSource::WhileLet |
+                  hir::LoopSource::ForLoop => {
+                      None
+                  }
+              };
 
-                ctxt.unified
-            } else {
-                tcx.types.never
-            }
+              let ctxt = BreakableCtxt {
+                  coerce: coerce,
+                  may_break: false, // will get updated if/when we find a `break`
+              };
+
+              let (ctxt, ()) = self.with_breakable_ctxt(expr.id, ctxt, || {
+                  self.check_block_no_value(&body);
+              });
+
+              if ctxt.may_break {
+                  // No way to know whether it's diverging because
+                  // of a `break` or an outer `break` or `return.
+                  self.diverges.set(Diverges::Maybe);
+              }
+
+              // If we permit break with a value, then result type is
+              // the LUB of the breaks (possibly ! if none); else, it
+              // is nil. This makes sense because infinite loops
+              // (which would have type !) are only possible iff we
+              // permit break with a value [1].
+              assert!(ctxt.coerce.is_some() || ctxt.may_break); // [1]
+              ctxt.coerce.map(|c| c.complete(self)).unwrap_or(self.tcx.mk_nil())
           }
           hir::ExprMatch(ref discrim, ref arms, match_src) => {
             self.check_match(expr, &discrim, arms, expected, match_src)
@@ -3660,6 +3744,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             let t_cast = self.resolve_type_vars_if_possible(&t_cast);
             let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast));
             let t_cast = self.resolve_type_vars_if_possible(&t_cast);
+            let diverges = self.diverges.get();
 
             // Eagerly check for some obvious errors.
             if t_expr.references_error() || t_cast.references_error() {
@@ -3667,7 +3752,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             } else {
                 // Defer other checks until we're done type checking.
                 let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
-                match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) {
+                match cast::CastCheck::new(self, e, t_expr, diverges, t_cast, t.span, expr.span) {
                     Ok(cast_check) => {
                         deferred_cast_checks.push(cast_check);
                         t_cast
@@ -3684,36 +3769,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             typ
           }
           hir::ExprArray(ref args) => {
-            let uty = expected.to_option(self).and_then(|uty| {
-                match uty.sty {
-                    ty::TyArray(ty, _) | ty::TySlice(ty) => Some(ty),
-                    _ => None
-                }
-            });
-
-            let mut unified = self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span));
-            let coerce_to = uty.unwrap_or(unified);
-
-            for (i, e) in args.iter().enumerate() {
-                let e_ty = self.check_expr_with_hint(e, coerce_to);
-                let cause = self.misc(e.span);
-
-                // Special-case the first element, as it has no "previous expressions".
-                let result = if i == 0 {
-                    self.try_coerce(e, e_ty, coerce_to)
-                } else {
-                    let prev_elems = || args[..i].iter().map(|e| &*e);
-                    self.try_find_coercion_lub(&cause, prev_elems, unified, e, e_ty)
-                };
+              let uty = expected.to_option(self).and_then(|uty| {
+                  match uty.sty {
+                      ty::TyArray(ty, _) | ty::TySlice(ty) => Some(ty),
+                      _ => None
+                  }
+              });
 
-                match result {
-                    Ok(ty) => unified = ty,
-                    Err(e) => {
-                        self.report_mismatched_types(&cause, unified, e_ty, e).emit();
-                    }
-                }
-            }
-            tcx.mk_array(unified, args.len())
+              let element_ty = if !args.is_empty() {
+                  let coerce_to = uty.unwrap_or_else(
+                      || self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span)));
+                  let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
+                  assert_eq!(self.diverges.get(), Diverges::Maybe);
+                  for e in args {
+                      let e_ty = self.check_expr_with_hint(e, coerce_to);
+                      let cause = self.misc(e.span);
+                      coerce.coerce(self, &cause, e, e_ty, self.diverges.get());
+                  }
+                  coerce.complete(self)
+              } else {
+                  self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span))
+              };
+              tcx.mk_array(element_ty, args.len())
           }
           hir::ExprRepeat(ref element, count) => {
             let count = eval_length(self.tcx.global_tcx(), count, "repeat count")
@@ -3984,7 +4061,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         self.diverges.set(Diverges::Maybe);
         self.has_errors.set(false);
 
-        let (node_id, span) = match stmt.node {
+        let (node_id, _span) = match stmt.node {
             hir::StmtDecl(ref decl, id) => {
                 let span = match decl.node {
                     hir::DeclLocal(ref l) => {
@@ -4010,9 +4087,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         if self.has_errors.get() {
             self.write_error(node_id);
-        } else if self.diverges.get().always() {
-            self.write_ty(node_id, self.next_diverging_ty_var(
-                TypeVariableOrigin::DivergingStmt(span)));
         } else {
             self.write_nil(node_id);
         }
@@ -4025,7 +4099,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn check_block_no_value(&self, blk: &'gcx hir::Block)  {
         let unit = self.tcx.mk_nil();
         let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
-        self.demand_suptype(blk.span, unit, ty);
+
+        // if the block produces a `!` value, that can always be
+        // (effectively) coerced to unit.
+        if !ty.is_never() {
+            self.demand_suptype(blk.span, unit, ty);
+        }
     }
 
     fn check_block_with_expected(&self,
@@ -4037,96 +4116,79 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             replace(&mut *fcx_ps, unsafety_state)
         };
 
-        let mut ty = if let Some(break_to_expr_id) = blk.break_to_expr_id {
-            let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(blk.span));
-            let coerce_to = expected.only_has_type(self).unwrap_or(unified);
-            let ctxt = BreakableCtxt {
-                unified: unified,
-                coerce_to: coerce_to,
-                break_exprs: vec![],
-                may_break: false,
+        // In some cases, blocks have just one exit, but other blocks
+        // can be targeted by multiple breaks. This cannot happen in
+        // normal Rust syntax today, but it can happen when we desugar
+        // a `do catch { ... }` expression.
+        //
+        // Example 1:
+        //
+        //    'a: { if true { break 'a Err(()); } Ok(()) }
+        //
+        // Here we would wind up with two coercions, one from
+        // `Err(())` and the other from the tail expression
+        // `Ok(())`. If the tail expression is omitted, that's a
+        // "forced unit" -- unless the block diverges, in which
+        // case we can ignore the tail expression (e.g., `'a: {
+        // break 'a 22; }` would not force the type of the block
+        // to be `()`).
+        let tail_expr = blk.expr.as_ref();
+        let coerce_to_ty = expected.coercion_target_type(self, blk.span);
+        let coerce = if blk.targeted_by_break {
+            CoerceMany::new(coerce_to_ty)
+        } else {
+            let tail_expr: &[P<hir::Expr>] = match tail_expr {
+                Some(e) => ref_slice(e),
+                None => &[],
             };
+            CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
+        };
 
-            let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(break_to_expr_id, ctxt, || {
-                for s in &blk.stmts {
-                    self.check_stmt(s);
-                }
-                let coerce_to = {
-                    let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
-                    enclosing_breakables.find_breakable(
-                        hir::ScopeTarget::Block(break_to_expr_id)
-                    ).unwrap().coerce_to
-                };
-                let e_ty;
-                let cause;
-                match blk.expr {
-                    Some(ref e) => {
-                        e_ty = self.check_expr_with_hint(e, coerce_to);
-                        cause = self.misc(e.span);
-                    },
-                    None => {
-                        e_ty = self.tcx.mk_nil();
-                        cause = self.misc(blk.span);
-                    }
-                };
-
-                (e_ty, cause)
-            });
-
-            if let Some(ref e) = blk.expr {
-                let result = if !ctxt.may_break {
-                    self.try_coerce(e, e_ty, ctxt.coerce_to)
-                } else {
-                    self.try_find_coercion_lub(&cause, || ctxt.break_exprs.iter().cloned(),
-                                               ctxt.unified, e, e_ty)
-                };
-                match result {
-                    Ok(ty) => ctxt.unified = ty,
-                    Err(err) =>
-                        self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(),
-                }
-            } else {
-                self.check_block_no_expr(blk, self.tcx.mk_nil(), e_ty);
-            };
+        let ctxt = BreakableCtxt {
+            coerce: Some(coerce),
+            may_break: false,
+        };
 
-            ctxt.unified
-        } else {
+        let (ctxt, ()) = self.with_breakable_ctxt(blk.id, ctxt, || {
             for s in &blk.stmts {
                 self.check_stmt(s);
             }
 
-            let mut ty = match blk.expr {
-                Some(ref e) => self.check_expr_with_expectation(e, expected),
-                None => self.tcx.mk_nil()
-            };
+            // check the tail expression **without** holding the
+            // `enclosing_breakables` lock below.
+            let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
 
-            if self.diverges.get().always() {
-                if let ExpectHasType(ety) = expected {
-                    // Avoid forcing a type (only `!` for now) in unreachable code.
-                    // FIXME(aburka) do we need this special case? and should it be is_uninhabited?
-                    if !ety.is_never() {
-                        if let Some(ref e) = blk.expr {
-                            // Coerce the tail expression to the right type.
-                            self.demand_coerce(e, ty, ety);
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            let mut ctxt = enclosing_breakables.find_breakable(blk.id);
+            let mut coerce = ctxt.coerce.as_mut().unwrap();
+            if let Some(tail_expr_ty) = tail_expr_ty {
+                let tail_expr = tail_expr.unwrap();
+                coerce.coerce(self,
+                              &self.misc(tail_expr.span),
+                              tail_expr,
+                              tail_expr_ty,
+                              self.diverges.get());
+            } else {
+                // Subtle: if there is no explicit tail expression,
+                // that is typically equivalent to a tail expression
+                // of `()` -- except if the block diverges. In that
+                // case, there is no value supplied from the tail
+                // expression (assuming there are no other breaks,
+                // this implies that the type of the block will be
+                // `!`).
+                if !self.diverges.get().always() {
+                    coerce.coerce_forced_unit(self, &self.misc(blk.span), &mut |err| {
+                        if let Some(expected_ty) = expected.only_has_type(self) {
+                            self.consider_hint_about_removing_semicolon(blk,
+                                                                        expected_ty,
+                                                                        err);
                         }
-                    }
-                }
-
-                ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
-            } else if let ExpectHasType(ety) = expected {
-                if let Some(ref e) = blk.expr {
-                    // Coerce the tail expression to the right type.
-                    self.demand_coerce(e, ty, ety);
-                } else {
-                    self.check_block_no_expr(blk, ty, ety);
+                    });
                 }
-
-                // We already applied the type (and potentially errored),
-                // use the expected type to avoid further errors out.
-                ty = ety;
             }
-            ty
-        };
+        });
+
+        let mut ty = ctxt.coerce.unwrap().complete(self);
 
         if self.has_errors.get() || ty.references_error() {
             ty = self.tcx.types.err
@@ -4138,43 +4200,42 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         ty
     }
 
-    pub fn check_block_no_expr(&self, blk: &'gcx hir::Block, ty: Ty<'tcx>, ety: Ty<'tcx>) {
-        // We're not diverging and there's an expected type, which,
-        // in case it's not `()`, could result in an error higher-up.
-        // We have a chance to error here early and be more helpful.
-        let cause = self.misc(blk.span);
-        let trace = TypeTrace::types(&cause, false, ty, ety);
-        match self.sub_types(false, &cause, ty, ety) {
-            Ok(InferOk { obligations, .. }) => {
-                // FIXME(#32730) propagate obligations
-                assert!(obligations.is_empty());
-            },
-            Err(err) => {
-                let mut err = self.report_and_explain_type_error(trace, &err);
-
-                // Be helpful when the user wrote `{... expr;}` and
-                // taking the `;` off is enough to fix the error.
-                let mut extra_semi = None;
-                if let Some(stmt) = blk.stmts.last() {
-                    if let hir::StmtSemi(ref e, _) = stmt.node {
-                        if self.can_sub_types(self.node_ty(e.id), ety).is_ok() {
-                            extra_semi = Some(stmt);
-                        }
-                    }
-                }
-                if let Some(last_stmt) = extra_semi {
-                    let original_span = original_sp(last_stmt.span, blk.span);
-                    let span_semi = Span {
-                        lo: original_span.hi - BytePos(1),
-                        hi: original_span.hi,
-                        ctxt: original_span.ctxt,
-                    };
-                    err.span_help(span_semi, "consider removing this semicolon:");
-                }
-
-                err.emit();
-            }
+    /// A common error is to add an extra semicolon:
+    ///
+    /// ```
+    /// fn foo() -> usize {
+    ///     22;
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the final statement in a block is an
+    /// expression with an explicit semicolon whose type is compatible
+    /// with `expected_ty`. If so, it suggests removing the semicolon.
+    fn consider_hint_about_removing_semicolon(&self,
+                                              blk: &'gcx hir::Block,
+                                              expected_ty: Ty<'tcx>,
+                                              err: &mut DiagnosticBuilder) {
+        // Be helpful when the user wrote `{... expr;}` and
+        // taking the `;` off is enough to fix the error.
+        let last_stmt = match blk.stmts.last() {
+            Some(s) => s,
+            None => return,
+        };
+        let last_expr = match last_stmt.node {
+            hir::StmtSemi(ref e, _) => e,
+            _ => return,
+        };
+        let last_expr_ty = self.expr_ty(last_expr);
+        if self.can_sub_types(last_expr_ty, expected_ty).is_err() {
+            return;
         }
+        let original_span = original_sp(last_stmt.span, blk.span);
+        let span_semi = Span {
+            lo: original_span.hi - BytePos(1),
+            hi: original_span.hi,
+            ctxt: original_span.ctxt,
+        };
+        err.span_help(span_semi, "consider removing this semicolon:");
     }
 
     // Instantiates the given path, which must refer to an item with the given
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index a4cb4071b4d..85c87adf9be 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -50,7 +50,7 @@ impl<'a, 'gcx, 'tcx> CheckWfFcxBuilder<'a, 'gcx, 'tcx> {
         let id = self.id;
         let span = self.span;
         self.inherited.enter(|inh| {
-            let fcx = FnCtxt::new(&inh, None, id);
+            let fcx = FnCtxt::new(&inh, id);
             let wf_tys = f(&fcx, &mut CheckTypeWellFormedVisitor {
                 tcx: fcx.tcx.global_tcx(),
                 code: code
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index df1c94dc19b..0bde9fefeba 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -79,6 +79,7 @@ This API is completely unstable and subject to change.
 #![feature(conservative_impl_trait)]
 #![cfg_attr(stage0,feature(field_init_shorthand))]
 #![feature(loop_break_value)]
+#![feature(never_type)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs
index a46a788ca08..d5baec675e4 100644
--- a/src/libsyntax/parse/obsolete.rs
+++ b/src/libsyntax/parse/obsolete.rs
@@ -36,6 +36,7 @@ pub trait ParserObsoleteMethods {
 impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
     /// Reports an obsolete syntax non-fatal error.
     #[allow(unused_variables)]
+    #[allow(unreachable_code)]
     fn obsolete(&mut self, sp: Span, kind: ObsoleteSyntax) {
         let (kind_str, desc, error) = match kind {
             // Nothing here at the moment
diff --git a/src/test/compile-fail/coerce-to-bang-cast.rs b/src/test/compile-fail/coerce-to-bang-cast.rs
new file mode 100644
index 00000000000..57d2192e635
--- /dev/null
+++ b/src/test/compile-fail/coerce-to-bang-cast.rs
@@ -0,0 +1,23 @@
+// Copyright 2016 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.
+
+#![feature(never_type)]
+
+fn foo(x: usize, y: !, z: usize) { }
+
+fn cast_a() {
+    let y = {return; 22} as !;
+}
+
+fn cast_b() {
+    let y = 22 as !; //~ ERROR non-scalar cast
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/coerce-to-bang.rs b/src/test/compile-fail/coerce-to-bang.rs
new file mode 100644
index 00000000000..870665bb49e
--- /dev/null
+++ b/src/test/compile-fail/coerce-to-bang.rs
@@ -0,0 +1,90 @@
+// Copyright 2016 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.
+
+#![feature(never_type)]
+
+fn foo(x: usize, y: !, z: usize) { }
+
+fn call_foo_a() {
+    // FIXME(#40800) -- accepted beacuse divergence happens **before**
+    // the coercion to `!`, but within same expression. Not clear that
+    // these are the rules we want.
+    foo(return, 22, 44);
+}
+
+fn call_foo_b() {
+    // Divergence happens in the argument itself, definitely ok.
+    foo(22, return, 44);
+}
+
+fn call_foo_c() {
+    // This test fails because the divergence happens **after** the
+    // coercion to `!`:
+    foo(22, 44, return); //~ ERROR mismatched types
+}
+
+fn call_foo_d() {
+    // This test passes because `a` has type `!`:
+    let a: ! = return;
+    let b = 22;
+    let c = 44;
+    foo(a, b, c); // ... and hence a reference to `a` is expected to diverge.
+}
+
+fn call_foo_e() {
+    // This test probably could pass but we don't *know* that `a`
+    // has type `!` so we don't let it work.
+    let a = return;
+    let b = 22;
+    let c = 44;
+    foo(a, b, c); //~ ERROR mismatched types
+}
+
+fn call_foo_f() {
+    // This fn fails because `a` has type `usize`, and hence a
+    // reference to is it **not** considered to diverge.
+    let a: usize = return;
+    let b = 22;
+    let c = 44;
+    foo(a, b, c); //~ ERROR mismatched types
+}
+
+fn array_a() {
+    // Accepted: return is coerced to `!` just fine, and then `22` can be
+    // because we already diverged.
+    let x: [!; 2] = [return, 22];
+}
+
+fn array_b() {
+    // Error: divergence has not yet occurred.
+    let x: [!; 2] = [22, return]; //~ ERROR mismatched types
+}
+
+fn tuple_a() {
+    // No divergence at all.
+    let x: (usize, !, usize) = (22, 44, 66); //~ ERROR mismatched types
+}
+
+fn tuple_b() {
+    // Divergence happens before coercion: OK
+    let x: (usize, !, usize) = (return, 44, 66);
+}
+
+fn tuple_c() {
+    // Divergence happens before coercion: OK
+    let x: (usize, !, usize) = (22, return, 66);
+}
+
+fn tuple_d() {
+    // Error: divergence happens too late
+    let x: (usize, !, usize) = (22, 44, return); //~ ERROR mismatched types
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/defaulted-unit-warning.rs b/src/test/compile-fail/defaulted-unit-warning.rs
index 5213a189714..ed6263d0fdb 100644
--- a/src/test/compile-fail/defaulted-unit-warning.rs
+++ b/src/test/compile-fail/defaulted-unit-warning.rs
@@ -22,16 +22,6 @@ impl Deserialize for () {
     }
 }
 
-fn doit() -> Result<(), String> {
-    let _ = match Deserialize::deserialize() {
-        //~^ ERROR code relies on type
-        //~| WARNING previously accepted
-        Ok(x) => x,
-        Err(e) => return Err(e),
-    };
-    Ok(())
-}
-
 trait ImplementedForUnitButNotNever {}
 
 impl ImplementedForUnitButNotNever for () {}
@@ -46,6 +36,6 @@ fn smeg() {
 }
 
 fn main() {
-    let _ = doit();
+    smeg();
 }
 
diff --git a/src/test/run-make/graphviz-flowgraph/f23.rs b/src/test/compile-fail/diverging-tuple-parts-39485.rs
index 52341a3fbd4..eedad08ab55 100644
--- a/src/test/run-make/graphviz-flowgraph/f23.rs
+++ b/src/test/compile-fail/diverging-tuple-parts-39485.rs
@@ -8,24 +8,18 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[allow(unreachable_code)]
-pub fn expr_while_23() {
-    let mut x = 23;
-    let mut y = 23;
-    let mut z = 23;
+// After #39485, this test used to pass, but that change was reverted
+// due to numerous inference failures like #39808, so it now fails
+// again. #39485 made it so that diverging types never propagate
+// upward; but we now do propagate such types upward in many more
+// cases.
 
-    while x > 0 {
-        x -= 1;
-
-        while y > 0 {
-            y -= 1;
-
-            while z > 0 { z -= 1; }
+fn g() {
+    &panic!() //~ ERROR mismatched types
+}
 
-            if x > 10 {
-                return;
-                "unreachable";
-            }
-        }
-    }
+fn f() -> isize {
+    (return 1, return 2) //~ ERROR mismatched types
 }
+
+fn main() {}
diff --git a/src/test/compile-fail/index-bot.rs b/src/test/compile-fail/index-bot.rs
index 70c362303ae..05b04723300 100644
--- a/src/test/compile-fail/index-bot.rs
+++ b/src/test/compile-fail/index-bot.rs
@@ -9,5 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    (return)[0]; //~ ERROR the type of this value must be known in this context
+    (return)[0]; //~ ERROR cannot index a value of type `!`
 }
diff --git a/src/test/compile-fail/issue-10176.rs b/src/test/compile-fail/issue-10176.rs
index 434b795ff31..c968844ae21 100644
--- a/src/test/compile-fail/issue-10176.rs
+++ b/src/test/compile-fail/issue-10176.rs
@@ -12,7 +12,7 @@ fn f() -> isize {
     (return 1, return 2)
 //~^ ERROR mismatched types
 //~| expected type `isize`
-//~| found type `(_, _)`
+//~| found type `(!, !)`
 //~| expected isize, found tuple
 }
 
diff --git a/src/test/compile-fail/issue-13847.rs b/src/test/compile-fail/issue-13847.rs
index aa823d9a70e..0314f109a7c 100644
--- a/src/test/compile-fail/issue-13847.rs
+++ b/src/test/compile-fail/issue-13847.rs
@@ -9,5 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    return.is_failure //~ ERROR the type of this value must be known in this context
+    return.is_failure //~ ERROR no field `is_failure` on type `!`
 }
diff --git a/src/test/compile-fail/issue-15207.rs b/src/test/compile-fail/issue-15207.rs
index 61877775269..70da8cf4169 100644
--- a/src/test/compile-fail/issue-15207.rs
+++ b/src/test/compile-fail/issue-15207.rs
@@ -10,7 +10,7 @@
 
 fn main() {
     loop {
-        break.push(1) //~ ERROR the type of this value must be known in this context
+        break.push(1) //~ ERROR no method named `push` found for type `!`
         ;
     }
 }
diff --git a/src/test/compile-fail/issue-17373.rs b/src/test/compile-fail/issue-17373.rs
index 6895893adc4..f6e6a8a0852 100644
--- a/src/test/compile-fail/issue-17373.rs
+++ b/src/test/compile-fail/issue-17373.rs
@@ -9,6 +9,6 @@
 // except according to those terms.
 
 fn main() {
-    *return //~ ERROR the type of this value must be known in this context
+    *return //~ ERROR type `!` cannot be dereferenced
     ;
 }
diff --git a/src/test/compile-fail/issue-18532.rs b/src/test/compile-fail/issue-18532.rs
index 94eab97c42a..2be5fdcac4e 100644
--- a/src/test/compile-fail/issue-18532.rs
+++ b/src/test/compile-fail/issue-18532.rs
@@ -13,6 +13,5 @@
 // into it.
 
 fn main() {
-    (return)((),());
-    //~^ ERROR the type of this value must be known
+    (return)((),()); //~ ERROR expected function, found `!`
 }
diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs
index 9143a226a24..256c5d8e6f7 100644
--- a/src/test/compile-fail/issue-2149.rs
+++ b/src/test/compile-fail/issue-2149.rs
@@ -21,5 +21,5 @@ impl<A> vec_monad<A> for Vec<A> {
 }
 fn main() {
     ["hi"].bind(|x| [x] );
-    //~^ ERROR no method named `bind` found for type `[&'static str; 1]` in the current scope
+    //~^ ERROR no method named `bind` found for type `[&str; 1]` in the current scope
 }
diff --git a/src/test/compile-fail/issue-27042.rs b/src/test/compile-fail/issue-27042.rs
index f31389f1337..23afa4b6296 100644
--- a/src/test/compile-fail/issue-27042.rs
+++ b/src/test/compile-fail/issue-27042.rs
@@ -12,14 +12,14 @@
 
 fn main() {
     let _: i32 =
-        'a: //~ ERROR mismatched types
-        loop { break };
+        'a: // in this case, the citation is just the `break`:
+        loop { break }; //~ ERROR mismatched types
     let _: i32 =
         'b: //~ ERROR mismatched types
-        while true { break };
+        while true { break }; // but here we cite the whole loop
     let _: i32 =
         'c: //~ ERROR mismatched types
-        for _ in None { break };
+        for _ in None { break }; // but here we cite the whole loop
     let _: i32 =
         'd: //~ ERROR mismatched types
         while let Some(_) = None { break };
diff --git a/src/test/compile-fail/loop-break-value.rs b/src/test/compile-fail/loop-break-value.rs
index d4f29597486..a4143218992 100644
--- a/src/test/compile-fail/loop-break-value.rs
+++ b/src/test/compile-fail/loop-break-value.rs
@@ -40,37 +40,40 @@ fn main() {
         loop {
             break 'while_loop 123;
             //~^ ERROR `break` with value from a `while` loop
-            //~| ERROR mismatched types
             break 456;
             break 789;
         };
     }
 
-    'while_let_loop: while let Some(_) = Some(()) {
+    while let Some(_) = Some(()) {
         if break () { //~ ERROR `break` with value from a `while let` loop
-            break;
-            break None;
-            //~^ ERROR `break` with value from a `while let` loop
-            //~| ERROR mismatched types
         }
+    }
+
+    while let Some(_) = Some(()) {
+        break None;
+        //~^ ERROR `break` with value from a `while let` loop
+    }
+
+    'while_let_loop: while let Some(_) = Some(()) {
         loop {
             break 'while_let_loop "nope";
             //~^ ERROR `break` with value from a `while let` loop
-            //~| ERROR mismatched types
             break 33;
         };
     }
 
-    'for_loop: for _ in &[1,2,3] {
+    for _ in &[1,2,3] {
         break (); //~ ERROR `break` with value from a `for` loop
         break [()];
         //~^ ERROR `break` with value from a `for` loop
-        //~| ERROR mismatched types
+    }
+
+    'for_loop: for _ in &[1,2,3] {
         loop {
             break Some(3);
             break 'for_loop Some(17);
             //~^ ERROR `break` with value from a `for` loop
-            //~| ERROR mismatched types
         };
     }
 
diff --git a/src/test/run-make/graphviz-flowgraph/f01.rs b/src/test/compile-fail/match-no-arms-unreachable-after.rs
index 231aab69e50..db08f5e5e66 100644
--- a/src/test/run-make/graphviz-flowgraph/f01.rs
+++ b/src/test/compile-fail/match-no-arms-unreachable-after.rs
@@ -1,4 +1,4 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,6 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub fn lit_1() {
-    1;
+#![allow(warnings)]
+#![deny(unreachable_code)]
+
+enum Void { }
+
+fn foo(v: Void) {
+    match v { }
+    let x = 2; //~ ERROR unreachable
+}
+
+fn main() {
 }
diff --git a/src/test/run-make/graphviz-flowgraph/f07.rs b/src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs
index f36b8d0abc7..aae0f3135d8 100644
--- a/src/test/run-make/graphviz-flowgraph/f07.rs
+++ b/src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs
@@ -1,4 +1,4 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,10 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(slice_patterns)]
+#![allow(unused_parens)]
+#![deny(unreachable_code)]
 
-pub fn pat_vec_7() {
-    match [7, 77, 777, 7777] {
-        [x, y, ..] => x + y
-    };
+fn main() {
+    match (return) { } //~ ERROR unreachable expression
 }
diff --git a/src/test/compile-fail/match-unresolved-one-arm.rs b/src/test/compile-fail/match-unresolved-one-arm.rs
new file mode 100644
index 00000000000..ea0f8db99e8
--- /dev/null
+++ b/src/test/compile-fail/match-unresolved-one-arm.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 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.
+
+fn foo<T>() -> T { panic!("Rocks for my pillow") }
+
+fn main() {
+    let x = match () { //~ ERROR type annotations needed
+        () => foo() // T here should be unresolved
+    };
+}
diff --git a/src/test/compile-fail/never-assign-dead-code.rs b/src/test/compile-fail/never-assign-dead-code.rs
index 57e0bca6a6d..d8752e1c050 100644
--- a/src/test/compile-fail/never-assign-dead-code.rs
+++ b/src/test/compile-fail/never-assign-dead-code.rs
@@ -16,5 +16,6 @@
 fn main() {
     let x: ! = panic!("aah"); //~ ERROR unused
     drop(x); //~ ERROR unreachable
+    //~^ ERROR unreachable
 }
 
diff --git a/src/test/compile-fail/never-assign-wrong-type.rs b/src/test/compile-fail/never-assign-wrong-type.rs
index 53d96aaf4fe..d854e6eb203 100644
--- a/src/test/compile-fail/never-assign-wrong-type.rs
+++ b/src/test/compile-fail/never-assign-wrong-type.rs
@@ -11,6 +11,7 @@
 // Test that we can't use another type in place of !
 
 #![feature(never_type)]
+#![deny(warnings)]
 
 fn main() {
     let x: ! = "hello"; //~ ERROR mismatched types
diff --git a/src/test/compile-fail/never-fallback.rs b/src/test/compile-fail/never-fallback.rs
deleted file mode 100644
index a43b1a45fe9..00000000000
--- a/src/test/compile-fail/never-fallback.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 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.
-
-// Test that diverging types default to ! when feature(never_type) is enabled. This test is the
-// same as run-pass/unit-fallback.rs except that ! is enabled.
-
-#![feature(never_type)]
-
-trait Balls: Sized {
-    fn smeg() -> Result<Self, ()>;
-}
-
-impl Balls for () {
-    fn smeg() -> Result<(), ()> { Ok(()) }
-}
-
-struct Flah;
-
-impl Flah {
-    fn flah<T: Balls>(&self) -> Result<T, ()> {
-        T::smeg()
-    }
-}
-
-fn doit() -> Result<(), ()> {
-    // The type of _ is unconstrained here and should default to !
-    let _ = try!(Flah.flah()); //~ ERROR the trait bound
-    Ok(())
-}
-
-fn main() {
-    let _ = doit();
-}
-
diff --git a/src/test/compile-fail/region-invariant-static-error-reporting.rs b/src/test/compile-fail/region-invariant-static-error-reporting.rs
index ac0167e08bd..d25674a74b1 100644
--- a/src/test/compile-fail/region-invariant-static-error-reporting.rs
+++ b/src/test/compile-fail/region-invariant-static-error-reporting.rs
@@ -16,9 +16,6 @@
 // error-pattern:cannot infer
 // error-pattern:cannot outlive the lifetime 'a
 // error-pattern:must be valid for the static lifetime
-// error-pattern:cannot infer
-// error-pattern:cannot outlive the lifetime 'a
-// error-pattern:must be valid for the static lifetime
 
 struct Invariant<'a>(Option<&'a mut &'a mut ()>);
 
diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs
index 64dbf27b78e..5ce80be98d9 100644
--- a/src/test/compile-fail/regions-bounds.rs
+++ b/src/test/compile-fail/regions-bounds.rs
@@ -16,17 +16,11 @@ struct an_enum<'a>(&'a isize);
 struct a_class<'a> { x:&'a isize }
 
 fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> {
-    return e; //~  ERROR mismatched types
-              //~| expected type `an_enum<'b>`
-              //~| found type `an_enum<'a>`
-              //~| lifetime mismatch
+    return e; //~ ERROR mismatched types
 }
 
 fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> {
-    return e; //~  ERROR mismatched types
-              //~| expected type `a_class<'b>`
-              //~| found type `a_class<'a>`
-              //~| lifetime mismatch
+    return e; //~ ERROR mismatched types
 }
 
 fn main() { }
diff --git a/src/test/run-make/graphviz-flowgraph/Makefile b/src/test/run-make/graphviz-flowgraph/Makefile
deleted file mode 100644
index 5740a36359c..00000000000
--- a/src/test/run-make/graphviz-flowgraph/Makefile
+++ /dev/null
@@ -1,38 +0,0 @@
--include ../tools.mk
-
-FILES=f00.rs f01.rs f02.rs f03.rs f04.rs f05.rs f06.rs f07.rs \
-      f08.rs f09.rs f10.rs f11.rs f12.rs f13.rs f14.rs f15.rs \
-      f16.rs f17.rs f18.rs f19.rs f20.rs f21.rs f22.rs f23.rs \
-      f24.rs f25.rs
-
-
-# all: $(patsubst %.rs,$(TMPDIR)/%.dot,$(FILES)) $(patsubst %.rs,$(TMPDIR)/%.pp,$(FILES))
-all: $(patsubst %.rs,$(TMPDIR)/%.check,$(FILES))
-
-
-RUSTC_LIB=$(RUSTC) --crate-type=lib
-
-define FIND_LAST_BLOCK
-LASTBLOCKNUM_$(1) := $(shell $(RUSTC_LIB) -Z unstable-options --pretty=expanded,identified $(1) \
-			 | grep block
-			 | tail -1
-			 | sed -e 's@.*/\* block \([0-9]*\) \*/.*@\1@')
-endef
-
-ifeq ($(findstring rustc,$(RUSTC)),)
-$(error Must set RUSTC)
-endif
-
-$(TMPDIR)/%.pp: %.rs
-	$(RUSTC_LIB) --pretty=expanded,identified $< -o $@
-
-$(TMPDIR)/%.dot: %.rs
-	$(eval $(call FIND_LAST_BLOCK,$<))
-	$(RUSTC_LIB) -Z unstable-options --unpretty flowgraph,unlabelled=$(LASTBLOCKNUM_$<) $< -o $@.tmp
-	cat $@.tmp | sed -e 's@ (id=[0-9]*)@@g' \
-                         -e 's@\[label=""\]@@' \
-                         -e 's@digraph [a-zA-Z0-9_]* @digraph block @' \
-                     > $@
-
-$(TMPDIR)/%.check: %.rs $(TMPDIR)/%.dot
-	diff -u $(patsubst %.rs,$(TMPDIR)/%.dot,$<) $(patsubst %.rs,%.dot-expected.dot,$<)
diff --git a/src/test/run-make/graphviz-flowgraph/f00.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f00.dot-expected.dot
deleted file mode 100644
index 8ea8370ab23..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f00.dot-expected.dot
+++ /dev/null
@@ -1,9 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="block { }"];
-    N3[label="expr { }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot
deleted file mode 100644
index 5982fbea769..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot
+++ /dev/null
@@ -1,13 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 1"];
-    N3[label="stmt 1;"];
-    N4[label="block { 1; }"];
-    N5[label="expr { 1; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot
deleted file mode 100644
index 1639785bd68..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot
+++ /dev/null
@@ -1,13 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="local _x"];
-    N3[label="stmt let _x: isize;"];
-    N4[label="block { let _x: isize; }"];
-    N5[label="expr { let _x: isize; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot
deleted file mode 100644
index b0ae00d8167..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot
+++ /dev/null
@@ -1,17 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 3"];
-    N3[label="expr 4"];
-    N4[label="expr 3 + 4"];
-    N5[label="stmt 3 + 4;"];
-    N6[label="block { 3 + 4; }"];
-    N7[label="expr { 3 + 4; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f03.rs b/src/test/run-make/graphviz-flowgraph/f03.rs
deleted file mode 100644
index 2dd71b623c2..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f03.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2014 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.
-
-pub fn expr_add_3() {
-    3 + 4;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot
deleted file mode 100644
index 41ace15a4c6..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot
+++ /dev/null
@@ -1,15 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 4"];
-    N3[label="local _x"];
-    N4[label="stmt let _x = 4;"];
-    N5[label="block { let _x = 4; }"];
-    N6[label="expr { let _x = 4; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f04.rs b/src/test/run-make/graphviz-flowgraph/f04.rs
deleted file mode 100644
index 2a0ac8ac9e5..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f04.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2014 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.
-
-pub fn pat_id_4() {
-    let _x = 4;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot
deleted file mode 100644
index 72b8ae71751..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot
+++ /dev/null
@@ -1,23 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 5"];
-    N3[label="expr 55"];
-    N4[label="expr (5, 55)"];
-    N5[label="local _x"];
-    N6[label="local _y"];
-    N7[label="pat (_x, _y)"];
-    N8[label="stmt let (_x, _y) = (5, 55);"];
-    N9[label="block { let (_x, _y) = (5, 55); }"];
-    N10[label="expr { let (_x, _y) = (5, 55); }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f05.rs b/src/test/run-make/graphviz-flowgraph/f05.rs
deleted file mode 100644
index 616d822bed0..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f05.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2014 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.
-
-pub fn pat_tup_5() {
-    let (_x, _y) = (5, 55);
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot
deleted file mode 100644
index acba71ef625..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot
+++ /dev/null
@@ -1,19 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 6"];
-    N3[label="expr S6{val: 6,}"];
-    N4[label="local _x"];
-    N5[label="pat S6 { val: _x }"];
-    N6[label="stmt let S6 { val: _x } = S6{val: 6,};"];
-    N7[label="block { let S6 { val: _x } = S6{val: 6,}; }"];
-    N8[label="expr { let S6 { val: _x } = S6{val: 6,}; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f06.rs b/src/test/run-make/graphviz-flowgraph/f06.rs
deleted file mode 100644
index 538ef2af898..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f06.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 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.
-
-struct S6 { val: isize }
-pub fn pat_struct_6() {
-    let S6 { val: _x } = S6{ val: 6 };
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot
deleted file mode 100644
index 251e2b39f14..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot
+++ /dev/null
@@ -1,39 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 7"];
-    N3[label="expr 77"];
-    N4[label="expr 777"];
-    N5[label="expr 7777"];
-    N6[label="expr [7, 77, 777, 7777]"];
-    N7[label="expr match [7, 77, 777, 7777] { [x, y, ..] => x + y, }"];
-    N8[label="(dummy_node)"];
-    N9[label="local x"];
-    N10[label="local y"];
-    N11[label="pat _"];
-    N12[label="pat [x, y, ..]"];
-    N13[label="expr x"];
-    N14[label="expr y"];
-    N15[label="expr x + y"];
-    N16[label="stmt match [7, 77, 777, 7777] { [x, y, ..] => x + y, };"];
-    N17[label="block { match [7, 77, 777, 7777] { [x, y, ..] => x + y, }; }"];
-    N18[label="expr { match [7, 77, 777, 7777] { [x, y, ..] => x + y, }; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N12;
-    N12 -> N8;
-    N8 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N7;
-    N7 -> N16;
-    N16 -> N17;
-    N17 -> N18;
-    N18 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot
deleted file mode 100644
index e2779c9414a..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot
+++ /dev/null
@@ -1,38 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 8"];
-    N3[label="local x"];
-    N4[label="stmt let x = 8;"];
-    N5[label="local _y"];
-    N6[label="stmt let _y;"];
-    N7[label="expr x"];
-    N8[label="expr 88"];
-    N9[label="expr x > 88"];
-    N10[label="expr 888"];
-    N11[label="expr _y"];
-    N12[label="expr _y = 888"];
-    N13[label="stmt _y = 888;"];
-    N14[label="block { _y = 888; }"];
-    N15[label="expr if x > 88 { _y = 888; }"];
-    N16[label="block { let x = 8; let _y; if x > 88 { _y = 888; } }"];
-    N17[label="expr { let x = 8; let _y; if x > 88 { _y = 888; } }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N9 -> N15;
-    N14 -> N15;
-    N15 -> N16;
-    N16 -> N17;
-    N17 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot
deleted file mode 100644
index 536abde91e8..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot
+++ /dev/null
@@ -1,54 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 91"];
-    N3[label="local x"];
-    N4[label="stmt let x = 91;"];
-    N5[label="local _y"];
-    N6[label="stmt let _y;"];
-    N7[label="expr x"];
-    N8[label="expr 92"];
-    N9[label="expr x > 92"];
-    N10[label="expr 93"];
-    N11[label="expr _y"];
-    N12[label="expr _y = 93"];
-    N13[label="stmt _y = 93;"];
-    N14[label="block { _y = 93; }"];
-    N15[label="expr 94"];
-    N16[label="expr 95"];
-    N17[label="expr 94 + 95"];
-    N18[label="expr _y"];
-    N19[label="expr _y = 94 + 95"];
-    N20[label="stmt _y = 94 + 95;"];
-    N21[label="block { _y = 94 + 95; }"];
-    N22[label="expr { _y = 94 + 95; }"];
-    N23[label="expr if x > 92 { _y = 93; } else { _y = 94 + 95; }"];
-    N24[label="block { let x = 91; let _y; if x > 92 { _y = 93; } else { _y = 94 + 95; } }"];
-    N25[label="expr { let x = 91; let _y; if x > 92 { _y = 93; } else { _y = 94 + 95; } }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N9 -> N15;
-    N15 -> N16;
-    N16 -> N17;
-    N17 -> N18;
-    N18 -> N19;
-    N19 -> N20;
-    N20 -> N21;
-    N21 -> N22;
-    N14 -> N23;
-    N22 -> N23;
-    N23 -> N24;
-    N24 -> N25;
-    N25 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f09.rs b/src/test/run-make/graphviz-flowgraph/f09.rs
deleted file mode 100644
index a78ccb8a937..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f09.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2014 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.
-
-pub fn expr_if_twoarm_9() {
-    let x = 91; let _y;
-    if x > 92 {
-        _y = 93;
-    } else {
-        _y = 94+95;
-    }
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot
deleted file mode 100644
index 07b9c744a71..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot
+++ /dev/null
@@ -1,36 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 10"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 10;"];
-    N5[label="(dummy_node)"];
-    N6[label="expr while x > 0 { x -= 1; }"];
-    N7[label="expr x"];
-    N8[label="expr 0"];
-    N9[label="expr x > 0"];
-    N10[label="expr 1"];
-    N11[label="expr x"];
-    N12[label="expr x -= 1"];
-    N13[label="stmt x -= 1;"];
-    N14[label="block { x -= 1; }"];
-    N15[label="block { let mut x = 10; while x > 0 { x -= 1; } }"];
-    N16[label="expr { let mut x = 10; while x > 0 { x -= 1; } }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N6;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N14 -> N5;
-    N6 -> N15;
-    N15 -> N16;
-    N16 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot
deleted file mode 100644
index 70034d299ba..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot
+++ /dev/null
@@ -1,35 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 11"];
-    N3[label="local mut _x"];
-    N4[label="stmt let mut _x = 11;"];
-    N5[label="(dummy_node)"];
-    N6[label="expr loop  { _x -= 1; }"];
-    N7[label="expr 1"];
-    N8[label="expr _x"];
-    N9[label="expr _x -= 1"];
-    N10[label="stmt _x -= 1;"];
-    N11[label="block { _x -= 1; }"];
-    N12[label="stmt loop  { _x -= 1; }"];
-    N13[label="expr \"unreachable\""];
-    N14[label="stmt \"unreachable\";"];
-    N15[label="block { let mut _x = 11; loop  { _x -= 1; } \"unreachable\"; }"];
-    N16[label="expr { let mut _x = 11; loop  { _x -= 1; } \"unreachable\"; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N5;
-    N6 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N16;
-    N16 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f11.rs b/src/test/run-make/graphviz-flowgraph/f11.rs
deleted file mode 100644
index d0f3452119e..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f11.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_loop_11() {
-    let mut _x = 11;
-    loop {
-        _x -= 1;
-    }
-    "unreachable";
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot
deleted file mode 100644
index 245afc43504..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot
+++ /dev/null
@@ -1,50 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 12"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 12;"];
-    N5[label="(dummy_node)"];
-    N6[label="expr loop  { x -= 1; if x == 2 { break ; \"unreachable\"; } }"];
-    N7[label="expr 1"];
-    N8[label="expr x"];
-    N9[label="expr x -= 1"];
-    N10[label="stmt x -= 1;"];
-    N11[label="expr x"];
-    N12[label="expr 2"];
-    N13[label="expr x == 2"];
-    N14[label="expr break"];
-    N15[label="(dummy_node)"];
-    N16[label="stmt break ;"];
-    N17[label="expr \"unreachable\""];
-    N18[label="stmt \"unreachable\";"];
-    N19[label="block { break ; \"unreachable\"; }"];
-    N20[label="expr if x == 2 { break ; \"unreachable\"; }"];
-    N21[label="block { x -= 1; if x == 2 { break ; \"unreachable\"; } }"];
-    N22[label="block { let mut x = 12; loop  { x -= 1; if x == 2 { break ; \"unreachable\"; } } }"];
-    N23[label="expr { let mut x = 12; loop  { x -= 1; if x == 2 { break ; \"unreachable\"; } } }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N14 -> N6;
-    N15 -> N16;
-    N16 -> N17;
-    N17 -> N18;
-    N18 -> N19;
-    N13 -> N20;
-    N19 -> N20;
-    N20 -> N21;
-    N21 -> N5;
-    N6 -> N22;
-    N22 -> N23;
-    N23 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f12.rs b/src/test/run-make/graphviz-flowgraph/f12.rs
deleted file mode 100644
index 90b146340b6..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f12.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_loop_12() {
-    let mut x = 12;
-    loop {
-        x -= 1;
-        if x == 2 { break; "unreachable"; }
-    }
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot
deleted file mode 100644
index 0f268bd0f2a..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot
+++ /dev/null
@@ -1,54 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr E13::E13b"];
-    N3[label="expr 13"];
-    N4[label="expr E13::E13b(13)"];
-    N5[label="local x"];
-    N6[label="stmt let x = E13::E13b(13);"];
-    N7[label="local _y"];
-    N8[label="stmt let _y;"];
-    N9[label="expr x"];
-    N10[label="expr match x { E13::E13a => _y = 1, E13::E13b(v) => _y = v + 1, }"];
-    N11[label="(dummy_node)"];
-    N12[label="pat E13::E13a"];
-    N13[label="expr 1"];
-    N14[label="expr _y"];
-    N15[label="expr _y = 1"];
-    N16[label="(dummy_node)"];
-    N17[label="local v"];
-    N18[label="pat E13::E13b(v)"];
-    N19[label="expr v"];
-    N20[label="expr 1"];
-    N21[label="expr v + 1"];
-    N22[label="expr _y"];
-    N23[label="expr _y = v + 1"];
-    N24[label="block {\l    let x = E13::E13b(13);\l    let _y;\l    match x { E13::E13a => _y = 1, E13::E13b(v) => _y = v + 1, }\l}\l"];
-    N25[label="expr {\l    let x = E13::E13b(13);\l    let _y;\l    match x { E13::E13a => _y = 1, E13::E13b(v) => _y = v + 1, }\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N12;
-    N12 -> N11;
-    N11 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N10;
-    N9 -> N17;
-    N17 -> N18;
-    N18 -> N16;
-    N16 -> N19;
-    N19 -> N20;
-    N20 -> N21;
-    N21 -> N22;
-    N22 -> N23;
-    N23 -> N10;
-    N10 -> N24;
-    N24 -> N25;
-    N25 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f13.rs b/src/test/run-make/graphviz-flowgraph/f13.rs
deleted file mode 100644
index babb283c734..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f13.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2014 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.
-
-enum E13 { E13a, E13b(isize) }
-pub fn expr_match_13() {
-    let x = E13::E13b(13); let _y;
-    match x {
-        E13::E13a => _y = 1,
-        E13::E13b(v) => _y = v + 1,
-    }
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot
deleted file mode 100644
index 719a6cf2619..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot
+++ /dev/null
@@ -1,36 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 14"];
-    N3[label="local x"];
-    N4[label="stmt let x = 14;"];
-    N5[label="expr x"];
-    N6[label="expr 1"];
-    N7[label="expr x > 1"];
-    N8[label="expr return"];
-    N9[label="(dummy_node)"];
-    N10[label="stmt return;"];
-    N11[label="expr \"unreachable\""];
-    N12[label="stmt \"unreachable\";"];
-    N13[label="block { return; \"unreachable\"; }"];
-    N14[label="expr if x > 1 { return; \"unreachable\"; }"];
-    N15[label="block { let x = 14; if x > 1 { return; \"unreachable\"; } }"];
-    N16[label="expr { let x = 14; if x > 1 { return; \"unreachable\"; } }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N1;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N12;
-    N12 -> N13;
-    N7 -> N14;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N16;
-    N16 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f14.rs b/src/test/run-make/graphviz-flowgraph/f14.rs
deleted file mode 100644
index 98ff095c831..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f14.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_ret_14() {
-    let x = 14;
-    if x > 1 {
-        return;
-        "unreachable";
-    }
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot
deleted file mode 100644
index d8cbd8411e2..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot
+++ /dev/null
@@ -1,105 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 15"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 15;"];
-    N5[label="expr 151"];
-    N6[label="local mut y"];
-    N7[label="stmt let mut y = 151;"];
-    N8[label="(dummy_node)"];
-    N9[label="expr \'outer:\l    loop  {\l        \'inner:\l            loop  {\l                if x == 1 { break \'outer ; \"unreachable\"; }\l                if y >= 2 { break ; \"unreachable\"; }\l                y -= 3;\l            }\l        y -= 4;\l        x -= 5;\l    }\l"];
-    N10[label="(dummy_node)"];
-    N11[label="expr \'inner:\l    loop  {\l        if x == 1 { break \'outer ; \"unreachable\"; }\l        if y >= 2 { break ; \"unreachable\"; }\l        y -= 3;\l    }\l"];
-    N12[label="expr x"];
-    N13[label="expr 1"];
-    N14[label="expr x == 1"];
-    N15[label="expr break \'outer"];
-    N16[label="(dummy_node)"];
-    N17[label="stmt break \'outer ;"];
-    N18[label="expr \"unreachable\""];
-    N19[label="stmt \"unreachable\";"];
-    N20[label="block { break \'outer ; \"unreachable\"; }"];
-    N21[label="expr if x == 1 { break \'outer ; \"unreachable\"; }"];
-    N22[label="stmt if x == 1 { break \'outer ; \"unreachable\"; }"];
-    N23[label="expr y"];
-    N24[label="expr 2"];
-    N25[label="expr y >= 2"];
-    N26[label="expr break"];
-    N27[label="(dummy_node)"];
-    N28[label="stmt break ;"];
-    N29[label="expr \"unreachable\""];
-    N30[label="stmt \"unreachable\";"];
-    N31[label="block { break ; \"unreachable\"; }"];
-    N32[label="expr if y >= 2 { break ; \"unreachable\"; }"];
-    N33[label="stmt if y >= 2 { break ; \"unreachable\"; }"];
-    N34[label="expr 3"];
-    N35[label="expr y"];
-    N36[label="expr y -= 3"];
-    N37[label="stmt y -= 3;"];
-    N38[label="block {\l    if x == 1 { break \'outer ; \"unreachable\"; }\l    if y >= 2 { break ; \"unreachable\"; }\l    y -= 3;\l}\l"];
-    N39[label="stmt \'inner:\l    loop  {\l        if x == 1 { break \'outer ; \"unreachable\"; }\l        if y >= 2 { break ; \"unreachable\"; }\l        y -= 3;\l    }\l"];
-    N40[label="expr 4"];
-    N41[label="expr y"];
-    N42[label="expr y -= 4"];
-    N43[label="stmt y -= 4;"];
-    N44[label="expr 5"];
-    N45[label="expr x"];
-    N46[label="expr x -= 5"];
-    N47[label="stmt x -= 5;"];
-    N48[label="block {\l    \'inner:\l        loop  {\l            if x == 1 { break \'outer ; \"unreachable\"; }\l            if y >= 2 { break ; \"unreachable\"; }\l            y -= 3;\l        }\l    y -= 4;\l    x -= 5;\l}\l"];
-    N49[label="block {\l    let mut x = 15;\l    let mut y = 151;\l    \'outer:\l        loop  {\l            \'inner:\l                loop  {\l                    if x == 1 { break \'outer ; \"unreachable\"; }\l                    if y >= 2 { break ; \"unreachable\"; }\l                    y -= 3;\l                }\l            y -= 4;\l            x -= 5;\l        }\l}\l"];
-    N50[label="expr {\l    let mut x = 15;\l    let mut y = 151;\l    \'outer:\l        loop  {\l            \'inner:\l                loop  {\l                    if x == 1 { break \'outer ; \"unreachable\"; }\l                    if y >= 2 { break ; \"unreachable\"; }\l                    y -= 3;\l                }\l            y -= 4;\l            x -= 5;\l        }\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N10;
-    N10 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N9;
-    N16 -> N17;
-    N17 -> N18;
-    N18 -> N19;
-    N19 -> N20;
-    N14 -> N21;
-    N20 -> N21;
-    N21 -> N22;
-    N22 -> N23;
-    N23 -> N24;
-    N24 -> N25;
-    N25 -> N26;
-    N26 -> N11;
-    N27 -> N28;
-    N28 -> N29;
-    N29 -> N30;
-    N30 -> N31;
-    N25 -> N32;
-    N31 -> N32;
-    N32 -> N33;
-    N33 -> N34;
-    N34 -> N35;
-    N35 -> N36;
-    N36 -> N37;
-    N37 -> N38;
-    N38 -> N10;
-    N11 -> N39;
-    N39 -> N40;
-    N40 -> N41;
-    N41 -> N42;
-    N42 -> N43;
-    N43 -> N44;
-    N44 -> N45;
-    N45 -> N46;
-    N46 -> N47;
-    N47 -> N48;
-    N48 -> N8;
-    N9 -> N49;
-    N49 -> N50;
-    N50 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f15.rs b/src/test/run-make/graphviz-flowgraph/f15.rs
deleted file mode 100644
index 056458e5558..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f15.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_break_label_15() {
-    let mut x = 15;
-    let mut y = 151;
-    'outer: loop {
-        'inner: loop {
-            if x == 1 {
-                break 'outer;
-                "unreachable";
-            }
-            if y >= 2 {
-                break;
-                "unreachable";
-            }
-            y -= 3;
-        }
-        y -= 4;
-        x -= 5;
-    }
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot
deleted file mode 100644
index b11881247fb..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot
+++ /dev/null
@@ -1,111 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 16"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 16;"];
-    N5[label="expr 16"];
-    N6[label="local mut y"];
-    N7[label="stmt let mut y = 16;"];
-    N8[label="(dummy_node)"];
-    N9[label="expr \'outer:\l    loop  {\l        \'inner:\l            loop  {\l                if x == 1 { continue \'outer ; \"unreachable\"; }\l                if y >= 1 { break ; \"unreachable\"; }\l                y -= 1;\l            }\l        y -= 1;\l        x -= 1;\l    }\l"];
-    N10[label="(dummy_node)"];
-    N11[label="expr \'inner:\l    loop  {\l        if x == 1 { continue \'outer ; \"unreachable\"; }\l        if y >= 1 { break ; \"unreachable\"; }\l        y -= 1;\l    }\l"];
-    N12[label="expr x"];
-    N13[label="expr 1"];
-    N14[label="expr x == 1"];
-    N15[label="expr continue \'outer"];
-    N16[label="(dummy_node)"];
-    N17[label="stmt continue \'outer ;"];
-    N18[label="expr \"unreachable\""];
-    N19[label="stmt \"unreachable\";"];
-    N20[label="block { continue \'outer ; \"unreachable\"; }"];
-    N21[label="expr if x == 1 { continue \'outer ; \"unreachable\"; }"];
-    N22[label="stmt if x == 1 { continue \'outer ; \"unreachable\"; }"];
-    N23[label="expr y"];
-    N24[label="expr 1"];
-    N25[label="expr y >= 1"];
-    N26[label="expr break"];
-    N27[label="(dummy_node)"];
-    N28[label="stmt break ;"];
-    N29[label="expr \"unreachable\""];
-    N30[label="stmt \"unreachable\";"];
-    N31[label="block { break ; \"unreachable\"; }"];
-    N32[label="expr if y >= 1 { break ; \"unreachable\"; }"];
-    N33[label="stmt if y >= 1 { break ; \"unreachable\"; }"];
-    N34[label="expr 1"];
-    N35[label="expr y"];
-    N36[label="expr y -= 1"];
-    N37[label="stmt y -= 1;"];
-    N38[label="block {\l    if x == 1 { continue \'outer ; \"unreachable\"; }\l    if y >= 1 { break ; \"unreachable\"; }\l    y -= 1;\l}\l"];
-    N39[label="stmt \'inner:\l    loop  {\l        if x == 1 { continue \'outer ; \"unreachable\"; }\l        if y >= 1 { break ; \"unreachable\"; }\l        y -= 1;\l    }\l"];
-    N40[label="expr 1"];
-    N41[label="expr y"];
-    N42[label="expr y -= 1"];
-    N43[label="stmt y -= 1;"];
-    N44[label="expr 1"];
-    N45[label="expr x"];
-    N46[label="expr x -= 1"];
-    N47[label="stmt x -= 1;"];
-    N48[label="block {\l    \'inner:\l        loop  {\l            if x == 1 { continue \'outer ; \"unreachable\"; }\l            if y >= 1 { break ; \"unreachable\"; }\l            y -= 1;\l        }\l    y -= 1;\l    x -= 1;\l}\l"];
-    N49[label="stmt \'outer:\l    loop  {\l        \'inner:\l            loop  {\l                if x == 1 { continue \'outer ; \"unreachable\"; }\l                if y >= 1 { break ; \"unreachable\"; }\l                y -= 1;\l            }\l        y -= 1;\l        x -= 1;\l    }\l"];
-    N50[label="expr \"unreachable\""];
-    N51[label="stmt \"unreachable\";"];
-    N52[label="block {\l    let mut x = 16;\l    let mut y = 16;\l    \'outer:\l        loop  {\l            \'inner:\l                loop  {\l                    if x == 1 { continue \'outer ; \"unreachable\"; }\l                    if y >= 1 { break ; \"unreachable\"; }\l                    y -= 1;\l                }\l            y -= 1;\l            x -= 1;\l        }\l    \"unreachable\";\l}\l"];
-    N53[label="expr {\l    let mut x = 16;\l    let mut y = 16;\l    \'outer:\l        loop  {\l            \'inner:\l                loop  {\l                    if x == 1 { continue \'outer ; \"unreachable\"; }\l                    if y >= 1 { break ; \"unreachable\"; }\l                    y -= 1;\l                }\l            y -= 1;\l            x -= 1;\l        }\l    \"unreachable\";\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N10;
-    N10 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N8;
-    N16 -> N17;
-    N17 -> N18;
-    N18 -> N19;
-    N19 -> N20;
-    N14 -> N21;
-    N20 -> N21;
-    N21 -> N22;
-    N22 -> N23;
-    N23 -> N24;
-    N24 -> N25;
-    N25 -> N26;
-    N26 -> N11;
-    N27 -> N28;
-    N28 -> N29;
-    N29 -> N30;
-    N30 -> N31;
-    N25 -> N32;
-    N31 -> N32;
-    N32 -> N33;
-    N33 -> N34;
-    N34 -> N35;
-    N35 -> N36;
-    N36 -> N37;
-    N37 -> N38;
-    N38 -> N10;
-    N11 -> N39;
-    N39 -> N40;
-    N40 -> N41;
-    N41 -> N42;
-    N42 -> N43;
-    N43 -> N44;
-    N44 -> N45;
-    N45 -> N46;
-    N46 -> N47;
-    N47 -> N48;
-    N48 -> N8;
-    N9 -> N49;
-    N49 -> N50;
-    N50 -> N51;
-    N51 -> N52;
-    N52 -> N53;
-    N53 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f16.rs b/src/test/run-make/graphviz-flowgraph/f16.rs
deleted file mode 100644
index e225b0080e5..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f16.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_continue_label_16() {
-    let mut x = 16;
-    let mut y = 16;
-    'outer: loop {
-        'inner: loop {
-            if x == 1 {
-                continue 'outer;
-                "unreachable";
-            }
-            if y >= 1 {
-                break;
-                "unreachable";
-            }
-            y -= 1;
-        }
-        y -= 1;
-        x -= 1;
-    }
-    "unreachable";
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot
deleted file mode 100644
index 705eece7755..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot
+++ /dev/null
@@ -1,21 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 1"];
-    N3[label="expr 7"];
-    N4[label="expr 17"];
-    N5[label="expr [1, 7, 17]"];
-    N6[label="local _v"];
-    N7[label="stmt let _v = [1, 7, 17];"];
-    N8[label="block { let _v = [1, 7, 17]; }"];
-    N9[label="expr { let _v = [1, 7, 17]; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f17.rs b/src/test/run-make/graphviz-flowgraph/f17.rs
deleted file mode 100644
index 23f5bb8a1eb..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f17.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2014 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.
-
-pub fn expr_vec_17() {
-    let _v = [1, 7, 17];
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot
deleted file mode 100644
index b0491fe6e27..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot
+++ /dev/null
@@ -1,23 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="stmt fn inner(x: isize) -> isize { x + x }"];
-    N3[label="expr inner"];
-    N4[label="expr inner"];
-    N5[label="expr 18"];
-    N6[label="expr inner(18)"];
-    N7[label="expr inner(inner(18))"];
-    N8[label="stmt inner(inner(18));"];
-    N9[label="block {\l    fn inner(x: isize) -> isize { x + x }\l    inner(inner(18));\l}\l"];
-    N10[label="expr {\l    fn inner(x: isize) -> isize { x + x }\l    inner(inner(18));\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f18.rs b/src/test/run-make/graphviz-flowgraph/f18.rs
deleted file mode 100644
index cbf8aa5db43..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f18.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 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.
-
-pub fn expr_call_18() {
-    fn inner(x:isize) -> isize { x + x }
-    inner(inner(18));
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot
deleted file mode 100644
index 223978c3d76..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot
+++ /dev/null
@@ -1,29 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="stmt struct S19 {\l    x: isize,\l}\l"];
-    N3[label="stmt impl S19 {\l    fn inner(self: Self) -> S19 { S19{x: self.x + self.x,} }\l}\l"];
-    N4[label="expr 19"];
-    N5[label="expr S19{x: 19,}"];
-    N6[label="local s"];
-    N7[label="stmt let s = S19{x: 19,};"];
-    N8[label="expr s"];
-    N9[label="expr s.inner()"];
-    N10[label="expr s.inner().inner()"];
-    N11[label="stmt s.inner().inner();"];
-    N12[label="block {\l    struct S19 {\l        x: isize,\l    }\l    impl S19 {\l        fn inner(self: Self) -> S19 { S19{x: self.x + self.x,} }\l    }\l    let s = S19{x: 19,};\l    s.inner().inner();\l}\l"];
-    N13[label="expr {\l    struct S19 {\l        x: isize,\l    }\l    impl S19 {\l        fn inner(self: Self) -> S19 { S19{x: self.x + self.x,} }\l    }\l    let s = S19{x: 19,};\l    s.inner().inner();\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N12;
-    N12 -> N13;
-    N13 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f19.rs b/src/test/run-make/graphviz-flowgraph/f19.rs
deleted file mode 100644
index 78c15dd64ad..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f19.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2014 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.
-
-pub fn expr_method_call_19() {
-    struct S19 { x: isize }
-    impl S19 { fn inner(self) -> S19 { S19 { x: self.x + self.x } } }
-    let s = S19 { x: 19 };
-    s.inner().inner();
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot
deleted file mode 100644
index 120eab4dac9..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot
+++ /dev/null
@@ -1,29 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 2"];
-    N3[label="expr 0"];
-    N4[label="expr 20"];
-    N5[label="expr [2, 0, 20]"];
-    N6[label="local v"];
-    N7[label="stmt let v = [2, 0, 20];"];
-    N8[label="expr v"];
-    N9[label="expr 20"];
-    N10[label="expr v[20]"];
-    N11[label="stmt v[20];"];
-    N12[label="block { let v = [2, 0, 20]; v[20]; }"];
-    N13[label="expr { let v = [2, 0, 20]; v[20]; }"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N12;
-    N12 -> N13;
-    N13 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f20.rs b/src/test/run-make/graphviz-flowgraph/f20.rs
deleted file mode 100644
index d7349932355..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f20.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 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.
-
-pub fn expr_index_20() {
-    let v = [2, 0, 20];
-    v[20];
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot
deleted file mode 100644
index 370dcdd8554..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot
+++ /dev/null
@@ -1,101 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 15"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 15;"];
-    N5[label="expr 151"];
-    N6[label="local mut y"];
-    N7[label="stmt let mut y = 151;"];
-    N8[label="(dummy_node)"];
-    N9[label="expr \'outer:\l    loop  {\l        \'inner:\l            loop  {\l                if x == 1 { break \'outer ; \"unreachable\"; }\l                if y >= 2 { return; \"unreachable\"; }\l                y -= 3;\l                x -= 5;\l            }\l        \"unreachable\";\l    }\l"];
-    N10[label="(dummy_node)"];
-    N11[label="expr \'inner:\l    loop  {\l        if x == 1 { break \'outer ; \"unreachable\"; }\l        if y >= 2 { return; \"unreachable\"; }\l        y -= 3;\l        x -= 5;\l    }\l"];
-    N12[label="expr x"];
-    N13[label="expr 1"];
-    N14[label="expr x == 1"];
-    N15[label="expr break \'outer"];
-    N16[label="(dummy_node)"];
-    N17[label="stmt break \'outer ;"];
-    N18[label="expr \"unreachable\""];
-    N19[label="stmt \"unreachable\";"];
-    N20[label="block { break \'outer ; \"unreachable\"; }"];
-    N21[label="expr if x == 1 { break \'outer ; \"unreachable\"; }"];
-    N22[label="stmt if x == 1 { break \'outer ; \"unreachable\"; }"];
-    N23[label="expr y"];
-    N24[label="expr 2"];
-    N25[label="expr y >= 2"];
-    N26[label="expr return"];
-    N27[label="(dummy_node)"];
-    N28[label="stmt return;"];
-    N29[label="expr \"unreachable\""];
-    N30[label="stmt \"unreachable\";"];
-    N31[label="block { return; \"unreachable\"; }"];
-    N32[label="expr if y >= 2 { return; \"unreachable\"; }"];
-    N33[label="stmt if y >= 2 { return; \"unreachable\"; }"];
-    N34[label="expr 3"];
-    N35[label="expr y"];
-    N36[label="expr y -= 3"];
-    N37[label="stmt y -= 3;"];
-    N38[label="expr 5"];
-    N39[label="expr x"];
-    N40[label="expr x -= 5"];
-    N41[label="stmt x -= 5;"];
-    N42[label="block {\l    if x == 1 { break \'outer ; \"unreachable\"; }\l    if y >= 2 { return; \"unreachable\"; }\l    y -= 3;\l    x -= 5;\l}\l"];
-    N43[label="stmt \'inner:\l    loop  {\l        if x == 1 { break \'outer ; \"unreachable\"; }\l        if y >= 2 { return; \"unreachable\"; }\l        y -= 3;\l        x -= 5;\l    }\l"];
-    N44[label="expr \"unreachable\""];
-    N45[label="stmt \"unreachable\";"];
-    N46[label="block {\l    \'inner:\l        loop  {\l            if x == 1 { break \'outer ; \"unreachable\"; }\l            if y >= 2 { return; \"unreachable\"; }\l            y -= 3;\l            x -= 5;\l        }\l    \"unreachable\";\l}\l"];
-    N47[label="block {\l    let mut x = 15;\l    let mut y = 151;\l    \'outer:\l        loop  {\l            \'inner:\l                loop  {\l                    if x == 1 { break \'outer ; \"unreachable\"; }\l                    if y >= 2 { return; \"unreachable\"; }\l                    y -= 3;\l                    x -= 5;\l                }\l            \"unreachable\";\l        }\l}\l"];
-    N48[label="expr {\l    let mut x = 15;\l    let mut y = 151;\l    \'outer:\l        loop  {\l            \'inner:\l                loop  {\l                    if x == 1 { break \'outer ; \"unreachable\"; }\l                    if y >= 2 { return; \"unreachable\"; }\l                    y -= 3;\l                    x -= 5;\l                }\l            \"unreachable\";\l        }\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N10;
-    N10 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N9;
-    N16 -> N17;
-    N17 -> N18;
-    N18 -> N19;
-    N19 -> N20;
-    N14 -> N21;
-    N20 -> N21;
-    N21 -> N22;
-    N22 -> N23;
-    N23 -> N24;
-    N24 -> N25;
-    N25 -> N26;
-    N26 -> N1;
-    N27 -> N28;
-    N28 -> N29;
-    N29 -> N30;
-    N30 -> N31;
-    N25 -> N32;
-    N31 -> N32;
-    N32 -> N33;
-    N33 -> N34;
-    N34 -> N35;
-    N35 -> N36;
-    N36 -> N37;
-    N37 -> N38;
-    N38 -> N39;
-    N39 -> N40;
-    N40 -> N41;
-    N41 -> N42;
-    N42 -> N10;
-    N11 -> N43;
-    N43 -> N44;
-    N44 -> N45;
-    N45 -> N46;
-    N46 -> N8;
-    N9 -> N47;
-    N47 -> N48;
-    N48 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f21.rs b/src/test/run-make/graphviz-flowgraph/f21.rs
deleted file mode 100644
index 70083ed8312..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f21.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_break_label_21() {
-    let mut x = 15;
-    let mut y = 151;
-    'outer: loop {
-        'inner: loop {
-            if x == 1 {
-                break 'outer;
-                "unreachable";
-            }
-            if y >= 2 {
-                return;
-                "unreachable";
-            }
-            y -= 3;
-            x -= 5;
-        }
-        "unreachable";
-    }
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot
deleted file mode 100644
index 9d3bc22831a..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot
+++ /dev/null
@@ -1,107 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 15"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 15;"];
-    N5[label="expr 151"];
-    N6[label="local mut y"];
-    N7[label="stmt let mut y = 151;"];
-    N8[label="(dummy_node)"];
-    N9[label="expr \'outer:\l    loop  {\l        \'inner:\l            loop  {\l                if x == 1 { continue \'outer ; \"unreachable\"; }\l                if y >= 2 { return; \"unreachable\"; }\l                x -= 1;\l                y -= 3;\l            }\l        \"unreachable\";\l    }\l"];
-    N10[label="(dummy_node)"];
-    N11[label="expr \'inner:\l    loop  {\l        if x == 1 { continue \'outer ; \"unreachable\"; }\l        if y >= 2 { return; \"unreachable\"; }\l        x -= 1;\l        y -= 3;\l    }\l"];
-    N12[label="expr x"];
-    N13[label="expr 1"];
-    N14[label="expr x == 1"];
-    N15[label="expr continue \'outer"];
-    N16[label="(dummy_node)"];
-    N17[label="stmt continue \'outer ;"];
-    N18[label="expr \"unreachable\""];
-    N19[label="stmt \"unreachable\";"];
-    N20[label="block { continue \'outer ; \"unreachable\"; }"];
-    N21[label="expr if x == 1 { continue \'outer ; \"unreachable\"; }"];
-    N22[label="stmt if x == 1 { continue \'outer ; \"unreachable\"; }"];
-    N23[label="expr y"];
-    N24[label="expr 2"];
-    N25[label="expr y >= 2"];
-    N26[label="expr return"];
-    N27[label="(dummy_node)"];
-    N28[label="stmt return;"];
-    N29[label="expr \"unreachable\""];
-    N30[label="stmt \"unreachable\";"];
-    N31[label="block { return; \"unreachable\"; }"];
-    N32[label="expr if y >= 2 { return; \"unreachable\"; }"];
-    N33[label="stmt if y >= 2 { return; \"unreachable\"; }"];
-    N34[label="expr 1"];
-    N35[label="expr x"];
-    N36[label="expr x -= 1"];
-    N37[label="stmt x -= 1;"];
-    N38[label="expr 3"];
-    N39[label="expr y"];
-    N40[label="expr y -= 3"];
-    N41[label="stmt y -= 3;"];
-    N42[label="block {\l    if x == 1 { continue \'outer ; \"unreachable\"; }\l    if y >= 2 { return; \"unreachable\"; }\l    x -= 1;\l    y -= 3;\l}\l"];
-    N43[label="stmt \'inner:\l    loop  {\l        if x == 1 { continue \'outer ; \"unreachable\"; }\l        if y >= 2 { return; \"unreachable\"; }\l        x -= 1;\l        y -= 3;\l    }\l"];
-    N44[label="expr \"unreachable\""];
-    N45[label="stmt \"unreachable\";"];
-    N46[label="block {\l    \'inner:\l        loop  {\l            if x == 1 { continue \'outer ; \"unreachable\"; }\l            if y >= 2 { return; \"unreachable\"; }\l            x -= 1;\l            y -= 3;\l        }\l    \"unreachable\";\l}\l"];
-    N47[label="stmt \'outer:\l    loop  {\l        \'inner:\l            loop  {\l                if x == 1 { continue \'outer ; \"unreachable\"; }\l                if y >= 2 { return; \"unreachable\"; }\l                x -= 1;\l                y -= 3;\l            }\l        \"unreachable\";\l    }\l"];
-    N48[label="expr \"unreachable\""];
-    N49[label="stmt \"unreachable\";"];
-    N50[label="block {\l    let mut x = 15;\l    let mut y = 151;\l    \'outer:\l        loop  {\l            \'inner:\l                loop  {\l                    if x == 1 { continue \'outer ; \"unreachable\"; }\l                    if y >= 2 { return; \"unreachable\"; }\l                    x -= 1;\l                    y -= 3;\l                }\l            \"unreachable\";\l        }\l    \"unreachable\";\l}\l"];
-    N51[label="expr {\l    let mut x = 15;\l    let mut y = 151;\l    \'outer:\l        loop  {\l            \'inner:\l                loop  {\l                    if x == 1 { continue \'outer ; \"unreachable\"; }\l                    if y >= 2 { return; \"unreachable\"; }\l                    x -= 1;\l                    y -= 3;\l                }\l            \"unreachable\";\l        }\l    \"unreachable\";\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N10;
-    N10 -> N12;
-    N12 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N8;
-    N16 -> N17;
-    N17 -> N18;
-    N18 -> N19;
-    N19 -> N20;
-    N14 -> N21;
-    N20 -> N21;
-    N21 -> N22;
-    N22 -> N23;
-    N23 -> N24;
-    N24 -> N25;
-    N25 -> N26;
-    N26 -> N1;
-    N27 -> N28;
-    N28 -> N29;
-    N29 -> N30;
-    N30 -> N31;
-    N25 -> N32;
-    N31 -> N32;
-    N32 -> N33;
-    N33 -> N34;
-    N34 -> N35;
-    N35 -> N36;
-    N36 -> N37;
-    N37 -> N38;
-    N38 -> N39;
-    N39 -> N40;
-    N40 -> N41;
-    N41 -> N42;
-    N42 -> N10;
-    N11 -> N43;
-    N43 -> N44;
-    N44 -> N45;
-    N45 -> N46;
-    N46 -> N8;
-    N9 -> N47;
-    N47 -> N48;
-    N48 -> N49;
-    N49 -> N50;
-    N50 -> N51;
-    N51 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f22.rs b/src/test/run-make/graphviz-flowgraph/f22.rs
deleted file mode 100644
index b35aac9ec42..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f22.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_break_label_21() {
-    let mut x = 15;
-    let mut y = 151;
-    'outer: loop {
-        'inner: loop {
-            if x == 1 {
-                continue 'outer;
-                "unreachable";
-            }
-            if y >= 2 {
-                return;
-                "unreachable";
-            }
-            x -= 1;
-            y -= 3;
-        }
-        "unreachable";
-    }
-    "unreachable";
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot
deleted file mode 100644
index c8bfcd6510b..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot
+++ /dev/null
@@ -1,113 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 23"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 23;"];
-    N5[label="expr 23"];
-    N6[label="local mut y"];
-    N7[label="stmt let mut y = 23;"];
-    N8[label="expr 23"];
-    N9[label="local mut z"];
-    N10[label="stmt let mut z = 23;"];
-    N11[label="(dummy_node)"];
-    N12[label="expr while x > 0 {\l    x -= 1;\l    while y > 0 {\l        y -= 1;\l        while z > 0 { z -= 1; }\l        if x > 10 { return; \"unreachable\"; }\l    }\l}\l"];
-    N13[label="expr x"];
-    N14[label="expr 0"];
-    N15[label="expr x > 0"];
-    N16[label="expr 1"];
-    N17[label="expr x"];
-    N18[label="expr x -= 1"];
-    N19[label="stmt x -= 1;"];
-    N20[label="(dummy_node)"];
-    N21[label="expr while y > 0 {\l    y -= 1;\l    while z > 0 { z -= 1; }\l    if x > 10 { return; \"unreachable\"; }\l}\l"];
-    N22[label="expr y"];
-    N23[label="expr 0"];
-    N24[label="expr y > 0"];
-    N25[label="expr 1"];
-    N26[label="expr y"];
-    N27[label="expr y -= 1"];
-    N28[label="stmt y -= 1;"];
-    N29[label="(dummy_node)"];
-    N30[label="expr while z > 0 { z -= 1; }"];
-    N31[label="expr z"];
-    N32[label="expr 0"];
-    N33[label="expr z > 0"];
-    N34[label="expr 1"];
-    N35[label="expr z"];
-    N36[label="expr z -= 1"];
-    N37[label="stmt z -= 1;"];
-    N38[label="block { z -= 1; }"];
-    N39[label="stmt while z > 0 { z -= 1; }"];
-    N40[label="expr x"];
-    N41[label="expr 10"];
-    N42[label="expr x > 10"];
-    N43[label="expr return"];
-    N44[label="(dummy_node)"];
-    N45[label="stmt return;"];
-    N46[label="expr \"unreachable\""];
-    N47[label="stmt \"unreachable\";"];
-    N48[label="block { return; \"unreachable\"; }"];
-    N49[label="expr if x > 10 { return; \"unreachable\"; }"];
-    N50[label="block { y -= 1; while z > 0 { z -= 1; } if x > 10 { return; \"unreachable\"; } }"];
-    N51[label="block {\l    x -= 1;\l    while y > 0 {\l        y -= 1;\l        while z > 0 { z -= 1; }\l        if x > 10 { return; \"unreachable\"; }\l    }\l}\l"];
-    N52[label="block {\l    let mut x = 23;\l    let mut y = 23;\l    let mut z = 23;\l    while x > 0 {\l        x -= 1;\l        while y > 0 {\l            y -= 1;\l            while z > 0 { z -= 1; }\l            if x > 10 { return; \"unreachable\"; }\l        }\l    }\l}\l"];
-    N53[label="expr {\l    let mut x = 23;\l    let mut y = 23;\l    let mut z = 23;\l    while x > 0 {\l        x -= 1;\l        while y > 0 {\l            y -= 1;\l            while z > 0 { z -= 1; }\l            if x > 10 { return; \"unreachable\"; }\l        }\l    }\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N12;
-    N15 -> N16;
-    N16 -> N17;
-    N17 -> N18;
-    N18 -> N19;
-    N19 -> N20;
-    N20 -> N22;
-    N22 -> N23;
-    N23 -> N24;
-    N24 -> N21;
-    N24 -> N25;
-    N25 -> N26;
-    N26 -> N27;
-    N27 -> N28;
-    N28 -> N29;
-    N29 -> N31;
-    N31 -> N32;
-    N32 -> N33;
-    N33 -> N30;
-    N33 -> N34;
-    N34 -> N35;
-    N35 -> N36;
-    N36 -> N37;
-    N37 -> N38;
-    N38 -> N29;
-    N30 -> N39;
-    N39 -> N40;
-    N40 -> N41;
-    N41 -> N42;
-    N42 -> N43;
-    N43 -> N1;
-    N44 -> N45;
-    N45 -> N46;
-    N46 -> N47;
-    N47 -> N48;
-    N42 -> N49;
-    N48 -> N49;
-    N49 -> N50;
-    N50 -> N20;
-    N21 -> N51;
-    N51 -> N11;
-    N12 -> N52;
-    N52 -> N53;
-    N53 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot
deleted file mode 100644
index e40dd014f0a..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot
+++ /dev/null
@@ -1,161 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 24"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 24;"];
-    N5[label="expr 24"];
-    N6[label="local mut y"];
-    N7[label="stmt let mut y = 24;"];
-    N8[label="expr 24"];
-    N9[label="local mut z"];
-    N10[label="stmt let mut z = 24;"];
-    N11[label="(dummy_node)"];
-    N12[label="expr loop  {\l    if x == 0 { break ; \"unreachable\"; }\l    x -= 1;\l    loop  {\l        if y == 0 { break ; \"unreachable\"; }\l        y -= 1;\l        loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l        if x > 10 { return; \"unreachable\"; }\l    }\l}\l"];
-    N13[label="expr x"];
-    N14[label="expr 0"];
-    N15[label="expr x == 0"];
-    N16[label="expr break"];
-    N17[label="(dummy_node)"];
-    N18[label="stmt break ;"];
-    N19[label="expr \"unreachable\""];
-    N20[label="stmt \"unreachable\";"];
-    N21[label="block { break ; \"unreachable\"; }"];
-    N22[label="expr if x == 0 { break ; \"unreachable\"; }"];
-    N23[label="stmt if x == 0 { break ; \"unreachable\"; }"];
-    N24[label="expr 1"];
-    N25[label="expr x"];
-    N26[label="expr x -= 1"];
-    N27[label="stmt x -= 1;"];
-    N28[label="(dummy_node)"];
-    N29[label="expr loop  {\l    if y == 0 { break ; \"unreachable\"; }\l    y -= 1;\l    loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l    if x > 10 { return; \"unreachable\"; }\l}\l"];
-    N30[label="expr y"];
-    N31[label="expr 0"];
-    N32[label="expr y == 0"];
-    N33[label="expr break"];
-    N34[label="(dummy_node)"];
-    N35[label="stmt break ;"];
-    N36[label="expr \"unreachable\""];
-    N37[label="stmt \"unreachable\";"];
-    N38[label="block { break ; \"unreachable\"; }"];
-    N39[label="expr if y == 0 { break ; \"unreachable\"; }"];
-    N40[label="stmt if y == 0 { break ; \"unreachable\"; }"];
-    N41[label="expr 1"];
-    N42[label="expr y"];
-    N43[label="expr y -= 1"];
-    N44[label="stmt y -= 1;"];
-    N45[label="(dummy_node)"];
-    N46[label="expr loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
-    N47[label="expr z"];
-    N48[label="expr 0"];
-    N49[label="expr z == 0"];
-    N50[label="expr break"];
-    N51[label="(dummy_node)"];
-    N52[label="stmt break ;"];
-    N53[label="expr \"unreachable\""];
-    N54[label="stmt \"unreachable\";"];
-    N55[label="block { break ; \"unreachable\"; }"];
-    N56[label="expr if z == 0 { break ; \"unreachable\"; }"];
-    N57[label="stmt if z == 0 { break ; \"unreachable\"; }"];
-    N58[label="expr 1"];
-    N59[label="expr z"];
-    N60[label="expr z -= 1"];
-    N61[label="stmt z -= 1;"];
-    N62[label="block { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
-    N63[label="stmt loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
-    N64[label="expr x"];
-    N65[label="expr 10"];
-    N66[label="expr x > 10"];
-    N67[label="expr return"];
-    N68[label="(dummy_node)"];
-    N69[label="stmt return;"];
-    N70[label="expr \"unreachable\""];
-    N71[label="stmt \"unreachable\";"];
-    N72[label="block { return; \"unreachable\"; }"];
-    N73[label="expr if x > 10 { return; \"unreachable\"; }"];
-    N74[label="block {\l    if y == 0 { break ; \"unreachable\"; }\l    y -= 1;\l    loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l    if x > 10 { return; \"unreachable\"; }\l}\l"];
-    N75[label="block {\l    if x == 0 { break ; \"unreachable\"; }\l    x -= 1;\l    loop  {\l        if y == 0 { break ; \"unreachable\"; }\l        y -= 1;\l        loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l        if x > 10 { return; \"unreachable\"; }\l    }\l}\l"];
-    N76[label="block {\l    let mut x = 24;\l    let mut y = 24;\l    let mut z = 24;\l    loop  {\l        if x == 0 { break ; \"unreachable\"; }\l        x -= 1;\l        loop  {\l            if y == 0 { break ; \"unreachable\"; }\l            y -= 1;\l            loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l            if x > 10 { return; \"unreachable\"; }\l        }\l    }\l}\l"];
-    N77[label="expr {\l    let mut x = 24;\l    let mut y = 24;\l    let mut z = 24;\l    loop  {\l        if x == 0 { break ; \"unreachable\"; }\l        x -= 1;\l        loop  {\l            if y == 0 { break ; \"unreachable\"; }\l            y -= 1;\l            loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l            if x > 10 { return; \"unreachable\"; }\l        }\l    }\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N16;
-    N16 -> N12;
-    N17 -> N18;
-    N18 -> N19;
-    N19 -> N20;
-    N20 -> N21;
-    N15 -> N22;
-    N21 -> N22;
-    N22 -> N23;
-    N23 -> N24;
-    N24 -> N25;
-    N25 -> N26;
-    N26 -> N27;
-    N27 -> N28;
-    N28 -> N30;
-    N30 -> N31;
-    N31 -> N32;
-    N32 -> N33;
-    N33 -> N29;
-    N34 -> N35;
-    N35 -> N36;
-    N36 -> N37;
-    N37 -> N38;
-    N32 -> N39;
-    N38 -> N39;
-    N39 -> N40;
-    N40 -> N41;
-    N41 -> N42;
-    N42 -> N43;
-    N43 -> N44;
-    N44 -> N45;
-    N45 -> N47;
-    N47 -> N48;
-    N48 -> N49;
-    N49 -> N50;
-    N50 -> N46;
-    N51 -> N52;
-    N52 -> N53;
-    N53 -> N54;
-    N54 -> N55;
-    N49 -> N56;
-    N55 -> N56;
-    N56 -> N57;
-    N57 -> N58;
-    N58 -> N59;
-    N59 -> N60;
-    N60 -> N61;
-    N61 -> N62;
-    N62 -> N45;
-    N46 -> N63;
-    N63 -> N64;
-    N64 -> N65;
-    N65 -> N66;
-    N66 -> N67;
-    N67 -> N1;
-    N68 -> N69;
-    N69 -> N70;
-    N70 -> N71;
-    N71 -> N72;
-    N66 -> N73;
-    N72 -> N73;
-    N73 -> N74;
-    N74 -> N28;
-    N29 -> N75;
-    N75 -> N11;
-    N12 -> N76;
-    N76 -> N77;
-    N77 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f24.rs b/src/test/run-make/graphviz-flowgraph/f24.rs
deleted file mode 100644
index f796d660a18..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f24.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_while_24() {
-    let mut x = 24;
-    let mut y = 24;
-    let mut z = 24;
-
-    loop {
-        if x == 0 { break; "unreachable"; }
-        x -= 1;
-
-        loop {
-            if y == 0 { break; "unreachable"; }
-            y -= 1;
-
-            loop {
-                if z == 0 { break; "unreachable"; }
-                z -= 1;
-            }
-
-            if x > 10 {
-                return;
-                "unreachable";
-            }
-        }
-    }
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot
deleted file mode 100644
index 1e2df1ab5e7..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot
+++ /dev/null
@@ -1,161 +0,0 @@
-digraph block {
-    N0[label="entry"];
-    N1[label="exit"];
-    N2[label="expr 25"];
-    N3[label="local mut x"];
-    N4[label="stmt let mut x = 25;"];
-    N5[label="expr 25"];
-    N6[label="local mut y"];
-    N7[label="stmt let mut y = 25;"];
-    N8[label="expr 25"];
-    N9[label="local mut z"];
-    N10[label="stmt let mut z = 25;"];
-    N11[label="(dummy_node)"];
-    N12[label="expr \'a:\l    loop  {\l        if x == 0 { break ; \"unreachable\"; }\l        x -= 1;\l        \'a:\l            loop  {\l                if y == 0 { break ; \"unreachable\"; }\l                y -= 1;\l                \'a: loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l                if x > 10 { continue \'a ; \"unreachable\"; }\l            }\l    }\l"];
-    N13[label="expr x"];
-    N14[label="expr 0"];
-    N15[label="expr x == 0"];
-    N16[label="expr break"];
-    N17[label="(dummy_node)"];
-    N18[label="stmt break ;"];
-    N19[label="expr \"unreachable\""];
-    N20[label="stmt \"unreachable\";"];
-    N21[label="block { break ; \"unreachable\"; }"];
-    N22[label="expr if x == 0 { break ; \"unreachable\"; }"];
-    N23[label="stmt if x == 0 { break ; \"unreachable\"; }"];
-    N24[label="expr 1"];
-    N25[label="expr x"];
-    N26[label="expr x -= 1"];
-    N27[label="stmt x -= 1;"];
-    N28[label="(dummy_node)"];
-    N29[label="expr \'a:\l    loop  {\l        if y == 0 { break ; \"unreachable\"; }\l        y -= 1;\l        \'a: loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l        if x > 10 { continue \'a ; \"unreachable\"; }\l    }\l"];
-    N30[label="expr y"];
-    N31[label="expr 0"];
-    N32[label="expr y == 0"];
-    N33[label="expr break"];
-    N34[label="(dummy_node)"];
-    N35[label="stmt break ;"];
-    N36[label="expr \"unreachable\""];
-    N37[label="stmt \"unreachable\";"];
-    N38[label="block { break ; \"unreachable\"; }"];
-    N39[label="expr if y == 0 { break ; \"unreachable\"; }"];
-    N40[label="stmt if y == 0 { break ; \"unreachable\"; }"];
-    N41[label="expr 1"];
-    N42[label="expr y"];
-    N43[label="expr y -= 1"];
-    N44[label="stmt y -= 1;"];
-    N45[label="(dummy_node)"];
-    N46[label="expr \'a: loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
-    N47[label="expr z"];
-    N48[label="expr 0"];
-    N49[label="expr z == 0"];
-    N50[label="expr break"];
-    N51[label="(dummy_node)"];
-    N52[label="stmt break ;"];
-    N53[label="expr \"unreachable\""];
-    N54[label="stmt \"unreachable\";"];
-    N55[label="block { break ; \"unreachable\"; }"];
-    N56[label="expr if z == 0 { break ; \"unreachable\"; }"];
-    N57[label="stmt if z == 0 { break ; \"unreachable\"; }"];
-    N58[label="expr 1"];
-    N59[label="expr z"];
-    N60[label="expr z -= 1"];
-    N61[label="stmt z -= 1;"];
-    N62[label="block { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
-    N63[label="stmt \'a: loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
-    N64[label="expr x"];
-    N65[label="expr 10"];
-    N66[label="expr x > 10"];
-    N67[label="expr continue \'a"];
-    N68[label="(dummy_node)"];
-    N69[label="stmt continue \'a ;"];
-    N70[label="expr \"unreachable\""];
-    N71[label="stmt \"unreachable\";"];
-    N72[label="block { continue \'a ; \"unreachable\"; }"];
-    N73[label="expr if x > 10 { continue \'a ; \"unreachable\"; }"];
-    N74[label="block {\l    if y == 0 { break ; \"unreachable\"; }\l    y -= 1;\l    \'a: loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l    if x > 10 { continue \'a ; \"unreachable\"; }\l}\l"];
-    N75[label="block {\l    if x == 0 { break ; \"unreachable\"; }\l    x -= 1;\l    \'a:\l        loop  {\l            if y == 0 { break ; \"unreachable\"; }\l            y -= 1;\l            \'a: loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l            if x > 10 { continue \'a ; \"unreachable\"; }\l        }\l}\l"];
-    N76[label="block {\l    let mut x = 25;\l    let mut y = 25;\l    let mut z = 25;\l    \'a:\l        loop  {\l            if x == 0 { break ; \"unreachable\"; }\l            x -= 1;\l            \'a:\l                loop  {\l                    if y == 0 { break ; \"unreachable\"; }\l                    y -= 1;\l                    \'a: loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l                    if x > 10 { continue \'a ; \"unreachable\"; }\l                }\l        }\l}\l"];
-    N77[label="expr {\l    let mut x = 25;\l    let mut y = 25;\l    let mut z = 25;\l    \'a:\l        loop  {\l            if x == 0 { break ; \"unreachable\"; }\l            x -= 1;\l            \'a:\l                loop  {\l                    if y == 0 { break ; \"unreachable\"; }\l                    y -= 1;\l                    \'a: loop  { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l                    if x > 10 { continue \'a ; \"unreachable\"; }\l                }\l        }\l}\l"];
-    N0 -> N2;
-    N2 -> N3;
-    N3 -> N4;
-    N4 -> N5;
-    N5 -> N6;
-    N6 -> N7;
-    N7 -> N8;
-    N8 -> N9;
-    N9 -> N10;
-    N10 -> N11;
-    N11 -> N13;
-    N13 -> N14;
-    N14 -> N15;
-    N15 -> N16;
-    N16 -> N12;
-    N17 -> N18;
-    N18 -> N19;
-    N19 -> N20;
-    N20 -> N21;
-    N15 -> N22;
-    N21 -> N22;
-    N22 -> N23;
-    N23 -> N24;
-    N24 -> N25;
-    N25 -> N26;
-    N26 -> N27;
-    N27 -> N28;
-    N28 -> N30;
-    N30 -> N31;
-    N31 -> N32;
-    N32 -> N33;
-    N33 -> N29;
-    N34 -> N35;
-    N35 -> N36;
-    N36 -> N37;
-    N37 -> N38;
-    N32 -> N39;
-    N38 -> N39;
-    N39 -> N40;
-    N40 -> N41;
-    N41 -> N42;
-    N42 -> N43;
-    N43 -> N44;
-    N44 -> N45;
-    N45 -> N47;
-    N47 -> N48;
-    N48 -> N49;
-    N49 -> N50;
-    N50 -> N46;
-    N51 -> N52;
-    N52 -> N53;
-    N53 -> N54;
-    N54 -> N55;
-    N49 -> N56;
-    N55 -> N56;
-    N56 -> N57;
-    N57 -> N58;
-    N58 -> N59;
-    N59 -> N60;
-    N60 -> N61;
-    N61 -> N62;
-    N62 -> N45;
-    N46 -> N63;
-    N63 -> N64;
-    N64 -> N65;
-    N65 -> N66;
-    N66 -> N67;
-    N67 -> N28;
-    N68 -> N69;
-    N69 -> N70;
-    N70 -> N71;
-    N71 -> N72;
-    N66 -> N73;
-    N72 -> N73;
-    N73 -> N74;
-    N74 -> N28;
-    N29 -> N75;
-    N75 -> N11;
-    N12 -> N76;
-    N76 -> N77;
-    N77 -> N1;
-}
diff --git a/src/test/run-make/graphviz-flowgraph/f25.rs b/src/test/run-make/graphviz-flowgraph/f25.rs
deleted file mode 100644
index 2ee2e48fd10..00000000000
--- a/src/test/run-make/graphviz-flowgraph/f25.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 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.
-
-#[allow(unreachable_code)]
-pub fn expr_while_25() {
-    let mut x = 25;
-    let mut y = 25;
-    let mut z = 25;
-
-    'a: loop {
-        if x == 0 { break; "unreachable"; }
-        x -= 1;
-
-        'a: loop {
-            if y == 0 { break; "unreachable"; }
-            y -= 1;
-
-            'a: loop {
-                if z == 0 { break; "unreachable"; }
-                z -= 1;
-            }
-
-            if x > 10 {
-                continue 'a;
-                "unreachable";
-            }
-        }
-    }
-}
diff --git a/src/test/run-pass/diverging-fallback-control-flow.rs b/src/test/run-pass/diverging-fallback-control-flow.rs
new file mode 100644
index 00000000000..656e90d2d52
--- /dev/null
+++ b/src/test/run-pass/diverging-fallback-control-flow.rs
@@ -0,0 +1,106 @@
+// Copyright 2015 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.
+
+// Test various cases where we permit an unconstrained variable
+// to fallback based on control-flow.
+//
+// These represent current behavior, but are pretty dubious.  I would
+// like to revisit these and potentially change them. --nmatsakis
+
+#![feature(never_type)]
+#![feature(loop_break_value)]
+
+trait BadDefault {
+    fn default() -> Self;
+}
+
+impl BadDefault for u32 {
+    fn default() -> Self {
+        0
+    }
+}
+
+impl BadDefault for ! {
+    fn default() -> ! {
+        panic!()
+    }
+}
+
+fn assignment() {
+    let x;
+
+    if true {
+        x = BadDefault::default();
+    } else {
+        x = return;
+    }
+}
+
+fn assignment_rev() {
+    let x;
+
+    if true {
+        x = return;
+    } else {
+        x = BadDefault::default();
+    }
+}
+
+fn if_then_else() {
+    let _x = if true {
+        BadDefault::default()
+    } else {
+        return;
+    };
+}
+
+fn if_then_else_rev() {
+    let _x = if true {
+        return;
+    } else {
+        BadDefault::default()
+    };
+}
+
+fn match_arm() {
+    let _x = match Ok(BadDefault::default()) {
+        Ok(v) => v,
+        Err(()) => return,
+    };
+}
+
+fn match_arm_rev() {
+    let _x = match Ok(BadDefault::default()) {
+        Err(()) => return,
+        Ok(v) => v,
+    };
+}
+
+fn loop_break() {
+    let _x = loop {
+        if false {
+            break return;
+        } else {
+            break BadDefault::default();
+        }
+    };
+}
+
+fn loop_break_rev() {
+    let _x = loop {
+        if false {
+            break return;
+        } else {
+            break BadDefault::default();
+        }
+    };
+}
+
+fn main() { }
diff --git a/src/test/run-pass/unit-fallback.rs b/src/test/run-pass/diverging-fallback-method-chain.rs
index 2babc6348e1..664a329c228 100644
--- a/src/test/run-pass/unit-fallback.rs
+++ b/src/test/run-pass/diverging-fallback-method-chain.rs
@@ -8,31 +8,20 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Test that diverging types default to () (with feature(never_type) disabled).
+// Test a regression found when building compiler. The `produce()`
+// error type `T` winds up getting unified with result of `x.parse()`;
+// the type of the closure given to `unwrap_or_else` needs to be
+// inferred to `usize`.
 
-trait Balls: Sized {
-    fn smeg() -> Result<Self, ()>;
-}
-
-impl Balls for () {
-    fn smeg() -> Result<(), ()> { Ok(()) }
-}
-
-struct Flah;
+use std::num::ParseIntError;
 
-impl Flah {
-    fn flah<T: Balls>(&self) -> Result<T, ()> {
-        T::smeg()
-    }
-}
-
-fn doit() -> Result<(), ()> {
-    // The type of _ is unconstrained here and should default to ()
-    let _ = try!(Flah.flah());
-    Ok(())
+fn produce<T>() -> Result<&'static str, T> {
+    Ok("22")
 }
 
 fn main() {
-    let _ = doit();
+    let x: usize = produce()
+        .and_then(|x| x.parse())
+        .unwrap_or_else(|_| panic!());
+    println!("{}", x);
 }
-
diff --git a/src/test/run-pass/diverging-fallback-option.rs b/src/test/run-pass/diverging-fallback-option.rs
new file mode 100644
index 00000000000..49f90e7c91f
--- /dev/null
+++ b/src/test/run-pass/diverging-fallback-option.rs
@@ -0,0 +1,22 @@
+// Copyright 2012 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.
+
+#![allow(warnings)]
+
+// Here the type of `c` is `Option<?T>`, where `?T` is unconstrained.
+// Because there is data-flow from the `{ return; }` block, which
+// diverges and hence has type `!`, into `c`, we will default `?T` to
+// `!`, and hence this code compiles rather than failing and requiring
+// a type annotation.
+
+fn main() {
+    let c = Some({ return; });
+    c.unwrap();
+}
diff --git a/src/test/run-pass/issue-39808.rs b/src/test/run-pass/issue-39808.rs
new file mode 100644
index 00000000000..91c70d76eef
--- /dev/null
+++ b/src/test/run-pass/issue-39808.rs
@@ -0,0 +1,26 @@
+// Copyright 2016 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.
+
+#![allow(unreachable_code)]
+
+// Regression test for #39808. The type parameter of `Owned` was
+// considered to be "unconstrained" because the type resulting from
+// `format!` (`String`) was not being propagated upward, owing to the
+// fact that the expression diverges.
+
+use std::borrow::Cow;
+
+fn main() {
+    let _ = if false {
+        Cow::Owned(format!("{:?}", panic!()))
+    } else {
+        Cow::Borrowed("")
+    };
+}
diff --git a/src/test/run-make/graphviz-flowgraph/f08.rs b/src/test/ui/loop-break-value-no-repeat.rs
index 6ba7b03d54d..790f796fae0 100644
--- a/src/test/run-make/graphviz-flowgraph/f08.rs
+++ b/src/test/ui/loop-break-value-no-repeat.rs
@@ -1,4 +1,4 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,9 +8,18 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub fn expr_if_onearm_8() {
-    let x = 8; let _y;
-    if x > 88 {
-        _y = 888;
+#![feature(loop_break_value)]
+#![allow(unused_variables)]
+
+use std::ptr;
+
+// Test that we only report **one** error here and that is that
+// `break` with an expression is illegal in this context. In
+// particular, we don't report any mismatched types error, which is
+// besides the point.
+
+fn main() {
+    for _ in &[1,2,3] {
+        break 22
     }
 }
diff --git a/src/test/ui/loop-break-value-no-repeat.stderr b/src/test/ui/loop-break-value-no-repeat.stderr
new file mode 100644
index 00000000000..0d99abd3907
--- /dev/null
+++ b/src/test/ui/loop-break-value-no-repeat.stderr
@@ -0,0 +1,8 @@
+error[E0571]: `break` with value from a `for` loop
+  --> $DIR/loop-break-value-no-repeat.rs:23:9
+   |
+23 |         break 22
+   |         ^^^^^^^^ can only break with a value inside `loop`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/README.md b/src/test/ui/reachable/README.md
new file mode 100644
index 00000000000..8bed5fba7a2
--- /dev/null
+++ b/src/test/ui/reachable/README.md
@@ -0,0 +1,7 @@
+A variety of tests around reachability. These tests in general check
+two things:
+
+- that we get unreachable code warnings in reasonable locations;
+- that we permit coercions **into** `!` from expressions which
+  diverge, where an expression "diverges" if it must execute some
+  subexpression of type `!`, or it has type `!` itself.
diff --git a/src/test/run-make/graphviz-flowgraph/f10.rs b/src/test/ui/reachable/expr_add.rs
index 0ca7cc5ee86..87d017adf68 100644
--- a/src/test/run-make/graphviz-flowgraph/f10.rs
+++ b/src/test/ui/reachable/expr_add.rs
@@ -1,4 +1,4 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,9 +8,21 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub fn expr_while_10() {
-    let mut x = 10;
-    while x > 0 {
-        x -= 1;
+#![feature(never_type)]
+#![allow(unused_variables)]
+#![deny(unreachable_code)]
+
+use std::ops;
+
+struct Foo;
+
+impl ops::Add<!> for Foo {
+    type Output = !;
+    fn add(self, rhs: !) -> ! {
+        unimplemented!()
     }
 }
+
+fn main() {
+    let x = Foo + return;
+}
diff --git a/src/test/ui/reachable/expr_add.stderr b/src/test/ui/reachable/expr_add.stderr
new file mode 100644
index 00000000000..1a2cc252051
--- /dev/null
+++ b/src/test/ui/reachable/expr_add.stderr
@@ -0,0 +1,14 @@
+error: unreachable expression
+  --> $DIR/expr_add.rs:27:13
+   |
+27 |     let x = Foo + return;
+   |             ^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_add.rs:13:9
+   |
+13 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/run-make/graphviz-flowgraph/f02.rs b/src/test/ui/reachable/expr_again.rs
index f7fe1266198..cdbdb8dc0db 100644
--- a/src/test/run-make/graphviz-flowgraph/f02.rs
+++ b/src/test/ui/reachable/expr_again.rs
@@ -1,4 +1,4 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,6 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub fn decl_x_2() {
-    let _x : isize;
+#![feature(box_syntax)]
+#![allow(unused_variables)]
+#![deny(unreachable_code)]
+
+fn main() {
+    let x = loop {
+        continue;
+        println!("hi");
+    };
 }
diff --git a/src/test/ui/reachable/expr_again.stderr b/src/test/ui/reachable/expr_again.stderr
new file mode 100644
index 00000000000..bf4e4dc4711
--- /dev/null
+++ b/src/test/ui/reachable/expr_again.stderr
@@ -0,0 +1,15 @@
+error: unreachable statement
+  --> $DIR/expr_again.rs:18:9
+   |
+18 |         println!("hi");
+   |         ^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_again.rs:13:9
+   |
+13 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro outside of the current crate
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/expr_andand.rs b/src/test/ui/reachable/expr_andand.rs
new file mode 100644
index 00000000000..af404d03097
--- /dev/null
+++ b/src/test/ui/reachable/expr_andand.rs
@@ -0,0 +1,21 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+
+fn foo() {
+    // No error here.
+    let x = false && (return);
+    println!("I am not dead.");
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_andand.stderr b/src/test/ui/reachable/expr_andand.stderr
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/test/ui/reachable/expr_andand.stderr
diff --git a/src/test/ui/reachable/expr_array.rs b/src/test/ui/reachable/expr_array.rs
new file mode 100644
index 00000000000..00e8be07725
--- /dev/null
+++ b/src/test/ui/reachable/expr_array.rs
@@ -0,0 +1,28 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+#![feature(type_ascription)]
+
+fn a() {
+    // the `22` is unreachable:
+    let x: [usize; 2] = [return, 22];
+}
+
+fn b() {
+    // the `array is unreachable:
+    let x: [usize; 2] = [22, return];
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_array.stderr b/src/test/ui/reachable/expr_array.stderr
new file mode 100644
index 00000000000..f8dbdb5f8bb
--- /dev/null
+++ b/src/test/ui/reachable/expr_array.stderr
@@ -0,0 +1,20 @@
+error: unreachable expression
+  --> $DIR/expr_array.rs:20:34
+   |
+20 |     let x: [usize; 2] = [return, 22];
+   |                                  ^^
+   |
+note: lint level defined here
+  --> $DIR/expr_array.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable expression
+  --> $DIR/expr_array.rs:25:25
+   |
+25 |     let x: [usize; 2] = [22, return];
+   |                         ^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/reachable/expr_assign.rs b/src/test/ui/reachable/expr_assign.rs
new file mode 100644
index 00000000000..1b9357013d2
--- /dev/null
+++ b/src/test/ui/reachable/expr_assign.rs
@@ -0,0 +1,39 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+fn foo() {
+    // No error here.
+    let x;
+    x = return;
+}
+
+fn bar() {
+    use std::ptr;
+    let p: *mut ! = ptr::null_mut::<!>();
+    unsafe {
+        // Here we consider the `return` unreachable because
+        // "evaluating" the `*p` has type `!`. This is somewhat
+        // dubious, I suppose.
+        *p = return;
+    }
+}
+
+fn baz() {
+    let mut i = 0;
+    *{return; &mut i} = 22;
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_assign.stderr b/src/test/ui/reachable/expr_assign.stderr
new file mode 100644
index 00000000000..807f6a1c1d5
--- /dev/null
+++ b/src/test/ui/reachable/expr_assign.stderr
@@ -0,0 +1,26 @@
+error: unreachable expression
+  --> $DIR/expr_assign.rs:20:5
+   |
+20 |     x = return;
+   |     ^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_assign.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable expression
+  --> $DIR/expr_assign.rs:30:14
+   |
+30 |         *p = return;
+   |              ^^^^^^
+
+error: unreachable expression
+  --> $DIR/expr_assign.rs:36:15
+   |
+36 |     *{return; &mut i} = 22;
+   |               ^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/reachable/expr_block.rs b/src/test/ui/reachable/expr_block.rs
new file mode 100644
index 00000000000..093589b4dc8
--- /dev/null
+++ b/src/test/ui/reachable/expr_block.rs
@@ -0,0 +1,41 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+fn a() {
+    // Here the tail expression is considered unreachable:
+    let x = {
+        return;
+        22
+    };
+}
+
+fn b() {
+    // Here the `x` assignment is considered unreachable, not the block:
+    let x = {
+        return;
+    };
+}
+
+fn c() {
+    // Here the `println!` is unreachable:
+    let x = {
+        return;
+        println!("foo");
+        22
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_block.stderr b/src/test/ui/reachable/expr_block.stderr
new file mode 100644
index 00000000000..542ce1c3fd9
--- /dev/null
+++ b/src/test/ui/reachable/expr_block.stderr
@@ -0,0 +1,22 @@
+error: unreachable expression
+  --> $DIR/expr_block.rs:21:9
+   |
+21 |         22
+   |         ^^
+   |
+note: lint level defined here
+  --> $DIR/expr_block.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable statement
+  --> $DIR/expr_block.rs:36:9
+   |
+36 |         println!("foo");
+   |         ^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/run-make/graphviz-flowgraph/f00.rs b/src/test/ui/reachable/expr_box.rs
index 4e7fc7ea9b0..6509b608335 100644
--- a/src/test/run-make/graphviz-flowgraph/f00.rs
+++ b/src/test/ui/reachable/expr_box.rs
@@ -1,4 +1,4 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,6 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub fn empty_0() {
+#![feature(box_syntax)]
+#![allow(unused_variables)]
+#![deny(unreachable_code)]
 
+fn main() {
+    let x = box return;
+    println!("hi");
 }
diff --git a/src/test/ui/reachable/expr_box.stderr b/src/test/ui/reachable/expr_box.stderr
new file mode 100644
index 00000000000..78ba231cef9
--- /dev/null
+++ b/src/test/ui/reachable/expr_box.stderr
@@ -0,0 +1,14 @@
+error: unreachable expression
+  --> $DIR/expr_box.rs:16:13
+   |
+16 |     let x = box return;
+   |             ^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_box.rs:13:9
+   |
+13 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/expr_call.rs b/src/test/ui/reachable/expr_call.rs
new file mode 100644
index 00000000000..8d9f303df7f
--- /dev/null
+++ b/src/test/ui/reachable/expr_call.rs
@@ -0,0 +1,31 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+fn foo(x: !, y: usize) { }
+
+fn bar(x: !) { }
+
+fn a() {
+    // the `22` is unreachable:
+    foo(return, 22);
+}
+
+fn b() {
+    // the call is unreachable:
+    bar(return);
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_call.stderr b/src/test/ui/reachable/expr_call.stderr
new file mode 100644
index 00000000000..5526827f59f
--- /dev/null
+++ b/src/test/ui/reachable/expr_call.stderr
@@ -0,0 +1,20 @@
+error: unreachable expression
+  --> $DIR/expr_call.rs:23:17
+   |
+23 |     foo(return, 22);
+   |                 ^^
+   |
+note: lint level defined here
+  --> $DIR/expr_call.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable expression
+  --> $DIR/expr_call.rs:28:5
+   |
+28 |     bar(return);
+   |     ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/reachable/expr_cast.rs b/src/test/ui/reachable/expr_cast.rs
new file mode 100644
index 00000000000..926ef864ebf
--- /dev/null
+++ b/src/test/ui/reachable/expr_cast.rs
@@ -0,0 +1,23 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+#![feature(type_ascription)]
+
+fn a() {
+    // the cast is unreachable:
+    let x = {return} as !;
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_cast.stderr b/src/test/ui/reachable/expr_cast.stderr
new file mode 100644
index 00000000000..a22300dcc13
--- /dev/null
+++ b/src/test/ui/reachable/expr_cast.stderr
@@ -0,0 +1,14 @@
+error: unreachable expression
+  --> $DIR/expr_cast.rs:20:13
+   |
+20 |     let x = {return} as !;
+   |             ^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_cast.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/expr_if.rs b/src/test/ui/reachable/expr_if.rs
new file mode 100644
index 00000000000..2a265e772f3
--- /dev/null
+++ b/src/test/ui/reachable/expr_if.rs
@@ -0,0 +1,41 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+fn foo() {
+    if {return} {
+        println!("Hello, world!");
+    }
+}
+
+fn bar() {
+    if {true} {
+        return;
+    }
+    println!("I am not dead.");
+}
+
+fn baz() {
+    if {true} {
+        return;
+    } else {
+        return;
+    }
+    // As the next action to be taken after the if arms, we should
+    // report the `println!` as unreachable:
+    println!("But I am.");
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_if.stderr b/src/test/ui/reachable/expr_if.stderr
new file mode 100644
index 00000000000..2cf17474f6e
--- /dev/null
+++ b/src/test/ui/reachable/expr_if.stderr
@@ -0,0 +1,15 @@
+error: unreachable statement
+  --> $DIR/expr_if.rs:38:5
+   |
+38 |     println!("But I am.");
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_if.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro outside of the current crate
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/expr_loop.rs b/src/test/ui/reachable/expr_loop.rs
new file mode 100644
index 00000000000..3ed4b2dcf0c
--- /dev/null
+++ b/src/test/ui/reachable/expr_loop.rs
@@ -0,0 +1,44 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+fn a() {
+    loop { return; }
+    println!("I am dead.");
+}
+
+fn b() {
+    loop {
+        break;
+    }
+    println!("I am not dead.");
+}
+
+fn c() {
+    loop { return; }
+    println!("I am dead.");
+}
+
+fn d() {
+    'outer: loop { loop { break 'outer; } }
+    println!("I am not dead.");
+}
+
+fn e() {
+    loop { 'middle: loop { loop { break 'middle; } } }
+    println!("I am dead.");
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_loop.stderr b/src/test/ui/reachable/expr_loop.stderr
new file mode 100644
index 00000000000..6e98e754c54
--- /dev/null
+++ b/src/test/ui/reachable/expr_loop.stderr
@@ -0,0 +1,31 @@
+error: unreachable statement
+  --> $DIR/expr_loop.rs:19:5
+   |
+19 |     println!("I am dead.");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_loop.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro outside of the current crate
+
+error: unreachable statement
+  --> $DIR/expr_loop.rs:31:5
+   |
+31 |     println!("I am dead.");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate
+
+error: unreachable statement
+  --> $DIR/expr_loop.rs:41:5
+   |
+41 |     println!("I am dead.");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/reachable/expr_match.rs b/src/test/ui/reachable/expr_match.rs
new file mode 100644
index 00000000000..23bdcc035b2
--- /dev/null
+++ b/src/test/ui/reachable/expr_match.rs
@@ -0,0 +1,54 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+fn a() {
+    // The match is considered unreachable here, because the `return`
+    // diverges:
+    match {return} { }
+}
+
+fn b() {
+    match () { () => return }
+    println!("I am dead");
+}
+
+fn c() {
+    match () { () if false => return, () => () }
+    println!("I am not dead");
+}
+
+fn d() {
+    match () { () if false => return, () => return }
+    println!("I am dead");
+}
+
+fn e() {
+    // Here the compiler fails to figure out that the `println` is dead.
+    match () { () if return => (), () => return }
+    println!("I am dead");
+}
+
+fn f() {
+    match Some(()) { None => (), Some(()) => return }
+    println!("I am not dead");
+}
+
+fn g() {
+    match Some(()) { None => return, Some(()) => () }
+    println!("I am not dead");
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_match.stderr b/src/test/ui/reachable/expr_match.stderr
new file mode 100644
index 00000000000..f5857a5b345
--- /dev/null
+++ b/src/test/ui/reachable/expr_match.stderr
@@ -0,0 +1,30 @@
+error: unreachable expression
+  --> $DIR/expr_match.rs:20:5
+   |
+20 |     match {return} { }
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_match.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable statement
+  --> $DIR/expr_match.rs:25:5
+   |
+25 |     println!("I am dead");
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate
+
+error: unreachable statement
+  --> $DIR/expr_match.rs:35:5
+   |
+35 |     println!("I am dead");
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/reachable/expr_method.rs b/src/test/ui/reachable/expr_method.rs
new file mode 100644
index 00000000000..f1d979d7df7
--- /dev/null
+++ b/src/test/ui/reachable/expr_method.rs
@@ -0,0 +1,34 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+struct Foo;
+
+impl Foo {
+    fn foo(&self, x: !, y: usize) { }
+    fn bar(&self, x: !) { }
+}
+
+fn a() {
+    // the `22` is unreachable:
+    Foo.foo(return, 22);
+}
+
+fn b() {
+    // the call is unreachable:
+    Foo.bar(return);
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_method.stderr b/src/test/ui/reachable/expr_method.stderr
new file mode 100644
index 00000000000..177d4352a37
--- /dev/null
+++ b/src/test/ui/reachable/expr_method.stderr
@@ -0,0 +1,20 @@
+error: unreachable expression
+  --> $DIR/expr_method.rs:26:21
+   |
+26 |     Foo.foo(return, 22);
+   |                     ^^
+   |
+note: lint level defined here
+  --> $DIR/expr_method.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable expression
+  --> $DIR/expr_method.rs:31:5
+   |
+31 |     Foo.bar(return);
+   |     ^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/reachable/expr_oror.rs b/src/test/ui/reachable/expr_oror.rs
new file mode 100644
index 00000000000..d01304d4034
--- /dev/null
+++ b/src/test/ui/reachable/expr_oror.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+
+fn foo() {
+    let x = false || (return);
+    println!("I am not dead.");
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_oror.stderr b/src/test/ui/reachable/expr_oror.stderr
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/test/ui/reachable/expr_oror.stderr
diff --git a/src/test/ui/reachable/expr_repeat.rs b/src/test/ui/reachable/expr_repeat.rs
new file mode 100644
index 00000000000..6078d6d5bde
--- /dev/null
+++ b/src/test/ui/reachable/expr_repeat.rs
@@ -0,0 +1,23 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+#![feature(type_ascription)]
+
+fn a() {
+    // the repeat is unreachable:
+    let x: [usize; 2] = [return; 2];
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_repeat.stderr b/src/test/ui/reachable/expr_repeat.stderr
new file mode 100644
index 00000000000..19afc5dd7b5
--- /dev/null
+++ b/src/test/ui/reachable/expr_repeat.stderr
@@ -0,0 +1,14 @@
+error: unreachable expression
+  --> $DIR/expr_repeat.rs:20:25
+   |
+20 |     let x: [usize; 2] = [return; 2];
+   |                         ^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_repeat.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/expr_return.rs b/src/test/ui/reachable/expr_return.rs
new file mode 100644
index 00000000000..c640ca06630
--- /dev/null
+++ b/src/test/ui/reachable/expr_return.rs
@@ -0,0 +1,24 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+#![feature(type_ascription)]
+
+fn a() {
+    // Here we issue that the "2nd-innermost" return is unreachable,
+    // but we stop there.
+    let x = {return {return {return;}}};
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_return.stderr b/src/test/ui/reachable/expr_return.stderr
new file mode 100644
index 00000000000..3eb70a4dd7c
--- /dev/null
+++ b/src/test/ui/reachable/expr_return.stderr
@@ -0,0 +1,14 @@
+error: unreachable expression
+  --> $DIR/expr_return.rs:21:22
+   |
+21 |     let x = {return {return {return;}}};
+   |                      ^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_return.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/expr_struct.rs b/src/test/ui/reachable/expr_struct.rs
new file mode 100644
index 00000000000..09e31819279
--- /dev/null
+++ b/src/test/ui/reachable/expr_struct.rs
@@ -0,0 +1,43 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+#![feature(type_ascription)]
+
+struct Foo {
+    a: usize,
+    b: usize,
+}
+
+fn a() {
+    // struct expr is unreachable:
+    let x = Foo { a: 22, b: 33, ..return };
+}
+
+fn b() {
+    // the `33` is unreachable:
+    let x = Foo { a: return, b: 33, ..return };
+}
+
+fn c() {
+    // the `..return` is unreachable:
+    let x = Foo { a: 22, b: return, ..return };
+}
+
+fn d() {
+    // the struct expr is unreachable:
+    let x = Foo { a: 22, b: return };
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_struct.stderr b/src/test/ui/reachable/expr_struct.stderr
new file mode 100644
index 00000000000..4b7ac660413
--- /dev/null
+++ b/src/test/ui/reachable/expr_struct.stderr
@@ -0,0 +1,32 @@
+error: unreachable expression
+  --> $DIR/expr_struct.rs:25:13
+   |
+25 |     let x = Foo { a: 22, b: 33, ..return };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_struct.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable expression
+  --> $DIR/expr_struct.rs:30:33
+   |
+30 |     let x = Foo { a: return, b: 33, ..return };
+   |                                 ^^
+
+error: unreachable expression
+  --> $DIR/expr_struct.rs:35:39
+   |
+35 |     let x = Foo { a: 22, b: return, ..return };
+   |                                       ^^^^^^
+
+error: unreachable expression
+  --> $DIR/expr_struct.rs:40:13
+   |
+40 |     let x = Foo { a: 22, b: return };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/reachable/expr_tup.rs b/src/test/ui/reachable/expr_tup.rs
new file mode 100644
index 00000000000..7c75296de6c
--- /dev/null
+++ b/src/test/ui/reachable/expr_tup.rs
@@ -0,0 +1,28 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+#![feature(type_ascription)]
+
+fn a() {
+    // the `2` is unreachable:
+    let x: (usize, usize) = (return, 2);
+}
+
+fn b() {
+    // the tuple is unreachable:
+    let x: (usize, usize) = (2, return);
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_tup.stderr b/src/test/ui/reachable/expr_tup.stderr
new file mode 100644
index 00000000000..63f477fd0c3
--- /dev/null
+++ b/src/test/ui/reachable/expr_tup.stderr
@@ -0,0 +1,20 @@
+error: unreachable expression
+  --> $DIR/expr_tup.rs:20:38
+   |
+20 |     let x: (usize, usize) = (return, 2);
+   |                                      ^
+   |
+note: lint level defined here
+  --> $DIR/expr_tup.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable expression
+  --> $DIR/expr_tup.rs:25:29
+   |
+25 |     let x: (usize, usize) = (2, return);
+   |                             ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/reachable/expr_type.rs b/src/test/ui/reachable/expr_type.rs
new file mode 100644
index 00000000000..2fa277c382e
--- /dev/null
+++ b/src/test/ui/reachable/expr_type.rs
@@ -0,0 +1,23 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+#![feature(type_ascription)]
+
+fn a() {
+    // the cast is unreachable:
+    let x = {return}: !;
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_type.stderr b/src/test/ui/reachable/expr_type.stderr
new file mode 100644
index 00000000000..6ed79974ccb
--- /dev/null
+++ b/src/test/ui/reachable/expr_type.stderr
@@ -0,0 +1,14 @@
+error: unreachable expression
+  --> $DIR/expr_type.rs:20:13
+   |
+20 |     let x = {return}: !;
+   |             ^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_type.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/expr_unary.rs b/src/test/ui/reachable/expr_unary.rs
new file mode 100644
index 00000000000..57901fbaa7c
--- /dev/null
+++ b/src/test/ui/reachable/expr_unary.rs
@@ -0,0 +1,21 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+fn foo() {
+    let x: ! = ! { return; 22 };
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_unary.stderr b/src/test/ui/reachable/expr_unary.stderr
new file mode 100644
index 00000000000..11172652d84
--- /dev/null
+++ b/src/test/ui/reachable/expr_unary.stderr
@@ -0,0 +1,8 @@
+error: cannot apply unary operator `!` to type `!`
+  --> $DIR/expr_unary.rs:18:16
+   |
+18 |     let x: ! = ! { return; 22 };
+   |                ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/reachable/expr_while.rs b/src/test/ui/reachable/expr_while.rs
new file mode 100644
index 00000000000..7dcd609fbc8
--- /dev/null
+++ b/src/test/ui/reachable/expr_while.rs
@@ -0,0 +1,38 @@
+// Copyright 2016 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.
+
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+#![deny(unreachable_code)]
+#![feature(never_type)]
+
+fn foo() {
+    while {return} {
+        println!("Hello, world!");
+    }
+}
+
+fn bar() {
+    while {true} {
+        return;
+    }
+    println!("I am not dead.");
+}
+
+fn baz() {
+    // Here, we cite the `while` loop as dead.
+    while {return} {
+        println!("I am dead.");
+    }
+    println!("I am, too.");
+}
+
+fn main() { }
diff --git a/src/test/ui/reachable/expr_while.stderr b/src/test/ui/reachable/expr_while.stderr
new file mode 100644
index 00000000000..066cfc86c64
--- /dev/null
+++ b/src/test/ui/reachable/expr_while.stderr
@@ -0,0 +1,31 @@
+error: unreachable statement
+  --> $DIR/expr_while.rs:19:9
+   |
+19 |         println!("Hello, world!");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/expr_while.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro outside of the current crate
+
+error: unreachable statement
+  --> $DIR/expr_while.rs:33:9
+   |
+33 |         println!("I am dead.");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate
+
+error: unreachable statement
+  --> $DIR/expr_while.rs:35:5
+   |
+35 |     println!("I am, too.");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/resolve/token-error-correct-3.stderr b/src/test/ui/resolve/token-error-correct-3.stderr
index 56e36889575..5be23d8ca48 100644
--- a/src/test/ui/resolve/token-error-correct-3.stderr
+++ b/src/test/ui/resolve/token-error-correct-3.stderr
@@ -36,10 +36,6 @@ error[E0308]: mismatched types
    |
    = note: expected type `()`
               found type `std::result::Result<bool, std::io::Error>`
-   = help: here are some functions which might fulfill your needs:
-           - .unwrap()
-           - .unwrap_err()
-           - .unwrap_or_default()
 
 error: aborting due to previous error