about summary refs log tree commit diff
diff options
context:
space:
mode:
authorkennytm <kennytm@gmail.com>2018-05-17 03:07:35 +0800
committerkennytm <kennytm@gmail.com>2018-05-17 05:18:19 +0800
commit63ea42fd3c82c629a0e5a8b981bdaeb30cdc0253 (patch)
tree02b249287151f4f5e67c47c1a67bab41c40f3862
parent0306630925bf4ab112bf1778bea0b27919b1cc8f (diff)
parent6ed200aaeabb7ab2e12c6fd2a52627bd3f457a95 (diff)
downloadrust-63ea42fd3c82c629a0e5a8b981bdaeb30cdc0253.tar.gz
rust-63ea42fd3c82c629a0e5a8b981bdaeb30cdc0253.zip
Rollup merge of #50793 - jrlusby:master, r=petrochenkov
tidy: Add a check for empty UI test files

Check for empty `.stderr` and `.stdout` files in UI test directories.
Empty files could  still pass testing for `compile-pass` tests with no output
so they can get into the repo accidentally, but they are not necessary and can
be removed.

This is very much an in progress pull request. I'm having an issue with rustfmt. It wanted to reformat the entire file for almost every file by default. And when I run tidy it just errors out because it catches the empty files that are already in the repo.

My next step is goin got be to remove those empty file and see if running tidy again will actually reformat things outside of the context of `cargo fmt`

Fixes https://github.com/rust-lang/rust/issues/50785
-rw-r--r--src/librustc/cfg/construct.rs2
-rw-r--r--src/librustc/hir/intravisit.rs5
-rw-r--r--src/librustc/hir/lowering.rs10
-rw-r--r--src/librustc/hir/mod.rs9
-rw-r--r--src/librustc/hir/print.rs16
-rw-r--r--src/librustc/ich/impls_hir.rs2
-rw-r--r--src/librustc/middle/expr_use_visitor.rs2
-rw-r--r--src/librustc/middle/liveness.rs4
-rw-r--r--src/librustc/middle/region.rs2
-rw-r--r--src/librustc_lint/builtin.rs2
-rw-r--r--src/librustc_mir/build/block.rs2
-rw-r--r--src/librustc_mir/hair/cx/expr.rs2
-rw-r--r--src/librustc_mir/transform/add_validation.rs2
-rw-r--r--src/librustc_passes/diagnostics.rs39
-rw-r--r--src/librustc_passes/loops.rs62
-rw-r--r--src/librustc_passes/rvalue_promotion.rs4
-rw-r--r--src/librustc_resolve/lib.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs8
-rw-r--r--src/libsyntax/ast.rs6
-rw-r--r--src/libsyntax/ext/build.rs2
-rw-r--r--src/libsyntax/feature_gate.rs9
-rw-r--r--src/libsyntax/fold.rs5
-rw-r--r--src/libsyntax/parse/classify.rs2
-rw-r--r--src/libsyntax/parse/parser.rs22
-rw-r--r--src/libsyntax/print/pprust.rs15
-rw-r--r--src/libsyntax/visit.rs5
-rw-r--r--src/test/run-pass/label_break_value.rs123
-rw-r--r--src/test/ui/compare-method/proj-outlives-region.stdout0
-rw-r--r--src/test/ui/compare-method/region-extra.stdout0
-rw-r--r--src/test/ui/compare-method/region-unrelated.stdout0
-rw-r--r--src/test/ui/const-eval/const_transmute.stderr0
-rw-r--r--src/test/ui/const-expr-addr-operator.stderr0
-rw-r--r--src/test/ui/const-pattern-not-const-evaluable.stderr0
-rw-r--r--src/test/ui/feature-gate-const-indexing.stderr0
-rw-r--r--src/test/ui/feature-gate-label_break_value.rs15
-rw-r--r--src/test/ui/feature-gate-label_break_value.stderr11
-rw-r--r--src/test/ui/issue-38875/issue_38875.stderr0
-rw-r--r--src/test/ui/label_break_value_continue.rs36
-rw-r--r--src/test/ui/label_break_value_continue.stderr30
-rw-r--r--src/test/ui/label_break_value_illegal_uses.rs31
-rw-r--r--src/test/ui/label_break_value_illegal_uses.stderr31
-rw-r--r--src/test/ui/label_break_value_unlabeled_break.rs29
-rw-r--r--src/test/ui/label_break_value_unlabeled_break.stderr15
-rw-r--r--src/test/ui/loop-break-value-no-repeat.stderr2
-rw-r--r--src/test/ui/nll/drop-may-dangle.stderr0
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr0
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr0
-rw-r--r--src/test/ui/print_type_sizes/anonymous.stderr0
-rw-r--r--src/test/ui/print_type_sizes/anonymous.stdout0
-rw-r--r--src/test/ui/print_type_sizes/multiple_types.stderr0
-rw-r--r--src/test/ui/print_type_sizes/packed.stderr0
-rw-r--r--src/test/ui/print_type_sizes/repr-align.stderr0
-rw-r--r--src/test/ui/reachable/expr_andand.stderr0
-rw-r--r--src/test/ui/reachable/expr_oror.stderr0
-rw-r--r--src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr0
-rw-r--r--src/test/ui/rfc1598-generic-associated-types/shadowing.stdout0
-rw-r--r--src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr0
-rw-r--r--src/test/ui/union/union-const-eval.stderr0
-rw-r--r--src/tools/tidy/src/ui_tests.rs63
59 files changed, 550 insertions, 77 deletions
diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs
index d8cf147e3ee..7e03288f572 100644
--- a/src/librustc/cfg/construct.rs
+++ b/src/librustc/cfg/construct.rs
@@ -179,7 +179,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
 
     fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex {
         match expr.node {
-            hir::ExprBlock(ref blk) => {
+            hir::ExprBlock(ref blk, _) => {
                 let blk_exit = self.block(&blk, pred);
                 self.add_ast_node(expr.hir_id.local_id, &[blk_exit])
             }
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 1fb496cca62..59b058e9861 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -1015,7 +1015,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
                              expression.span,
                              expression.id)
         }
-        ExprBlock(ref block) => visitor.visit_block(block),
+        ExprBlock(ref block, ref opt_label) => {
+            walk_list!(visitor, visit_label, opt_label);
+            visitor.visit_block(block);
+        }
         ExprAssign(ref left_hand_expression, ref right_hand_expression) => {
             visitor.visit_expr(right_hand_expression);
             visitor.visit_expr(left_hand_expression)
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 7a360f7ee43..0f4871954d6 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -3048,7 +3048,7 @@ impl<'a> LoweringContext<'a> {
                     );
                     block.expr = Some(this.wrap_in_try_constructor(
                         "from_ok", tail, unstable_span));
-                    hir::ExprBlock(P(block))
+                    hir::ExprBlock(P(block), None)
                 })
             }
             ExprKind::Match(ref expr, ref arms) => hir::ExprMatch(
@@ -3100,7 +3100,11 @@ impl<'a> LoweringContext<'a> {
                     })
                 })
             }
-            ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, false)),
+            ExprKind::Block(ref blk, opt_label) => {
+                hir::ExprBlock(self.lower_block(blk,
+                                                opt_label.is_some()),
+                                                self.lower_label(opt_label))
+            }
             ExprKind::Assign(ref el, ref er) => {
                 hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
             }
@@ -3843,7 +3847,7 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr {
-        self.expr(b.span, hir::ExprBlock(b), attrs)
+        self.expr(b.span, hir::ExprBlock(b, None), attrs)
     }
 
     fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec<hir::Expr>) -> P<hir::Expr> {
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 3f49e0bfd19..c48fb7ab7eb 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -778,9 +778,8 @@ pub struct Block {
     pub rules: BlockCheckMode,
     pub span: Span,
     /// 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.
+    /// break out of this block early.
+    /// Used by `'label: {}` blocks and by `catch` statements.
     pub targeted_by_break: bool,
     /// If true, don't emit return value type errors as the parser had
     /// to recover from a parse error so this block will not have an
@@ -1381,8 +1380,8 @@ pub enum Expr_ {
     /// This may also be a generator literal, indicated by the final boolean,
     /// in that case there is an GeneratorClause.
     ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, Option<GeneratorMovability>),
-    /// A block (`{ ... }`)
-    ExprBlock(P<Block>),
+    /// A block (`'label: { ... }`)
+    ExprBlock(P<Block>, Option<Label>),
 
     /// An assignment (`a = foo()`)
     ExprAssign(P<Expr>, P<Expr>),
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 3943c30127d..9cd9e0dce54 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -1047,7 +1047,7 @@ impl<'a> State<'a> {
                         self.print_else(e.as_ref().map(|e| &**e))
                     }
                     // "final else"
-                    hir::ExprBlock(ref b) => {
+                    hir::ExprBlock(ref b, _) => {
                         self.cbox(indent_unit - 1)?;
                         self.ibox(0)?;
                         self.s.word(" else ")?;
@@ -1377,7 +1377,11 @@ impl<'a> State<'a> {
                 // empty box to satisfy the close.
                 self.ibox(0)?;
             }
-            hir::ExprBlock(ref blk) => {
+            hir::ExprBlock(ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_name(label.name)?;
+                    self.word_space(":")?;
+                }
                 // containing cbox, will be closed by print-block at }
                 self.cbox(indent_unit)?;
                 // head-box, will be closed by print-block after {
@@ -1893,7 +1897,11 @@ impl<'a> State<'a> {
         self.word_space("=>")?;
 
         match arm.body.node {
-            hir::ExprBlock(ref blk) => {
+            hir::ExprBlock(ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_name(label.name)?;
+                    self.word_space(":")?;
+                }
                 // the block will close the pattern's ibox
                 self.print_block_unclosed_indent(&blk, indent_unit)?;
 
@@ -2299,7 +2307,7 @@ fn expr_requires_semi_to_be_stmt(e: &hir::Expr) -> bool {
     match e.node {
         hir::ExprIf(..) |
         hir::ExprMatch(..) |
-        hir::ExprBlock(_) |
+        hir::ExprBlock(..) |
         hir::ExprWhile(..) |
         hir::ExprLoop(..) => false,
         _ => true,
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index 6edf1b35bdd..ed01704e6a4 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -589,7 +589,7 @@ impl_stable_hash_for!(enum hir::Expr_ {
     ExprLoop(body, label, loop_src),
     ExprMatch(matchee, arms, match_src),
     ExprClosure(capture_clause, decl, body_id, span, gen),
-    ExprBlock(blk),
+    ExprBlock(blk, label),
     ExprAssign(lhs, rhs),
     ExprAssignOp(op, lhs, rhs),
     ExprField(owner, field_name),
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 614ae17fa46..3ba230fe55a 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -499,7 +499,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                 self.consume_expr(&rhs);
             }
 
-            hir::ExprBlock(ref blk) => {
+            hir::ExprBlock(ref blk, _) => {
                 self.walk_block(&blk);
             }
 
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 2b1663bed2f..b39311a7471 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -1187,7 +1187,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             succ
           }
 
-          hir::ExprBlock(ref blk) => {
+          // Note that labels have been resolved, so we don't need to look
+          // at the label ident
+          hir::ExprBlock(ref blk, _) => {
             self.propagate_through_block(&blk, succ)
           }
         }
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index bfc9ff6660d..52217600759 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -1247,7 +1247,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
             hir::ExprCast(ref subexpr, _) => {
                 record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id)
             }
-            hir::ExprBlock(ref block) => {
+            hir::ExprBlock(ref block, _) => {
                 if let Some(ref subexpr) = block.expr {
                     record_rvalue_scope_if_borrow_expr(
                         visitor, &subexpr, blk_id);
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 0b1e9081a72..19280372d55 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -228,7 +228,7 @@ impl UnsafeCode {
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode {
     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
-        if let hir::ExprBlock(ref blk) = e.node {
+        if let hir::ExprBlock(ref blk, _) = e.node {
             // Don't warn about generated blocks, that'll just pollute the output.
             if blk.rules == hir::UnsafeBlock(hir::UserProvided) {
                 self.report_unsafe(cx, blk.span, "usage of an `unsafe` block");
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index fae06db3162..c9f86d43998 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -36,7 +36,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), block, move |this| {
             this.in_scope((region_scope, source_info), LintLevel::Inherited, block, move |this| {
                 if targeted_by_break {
-                    // This is a `break`-able block (currently only `catch { ... }`)
+                    // This is a `break`-able block
                     let exit_block = this.cfg.start_new_block();
                     let block_exit = this.in_breakable_scope(
                         None, exit_block, destination.clone(), |this| {
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 3e432d84c69..97a4e0f3724 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -292,7 +292,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
             }
         }
 
-        hir::ExprBlock(ref blk) => ExprKind::Block { body: &blk },
+        hir::ExprBlock(ref blk, _) => ExprKind::Block { body: &blk },
 
         hir::ExprAssign(ref lhs, ref rhs) => {
             ExprKind::Assign {
diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs
index 584887d0373..85af00956c9 100644
--- a/src/librustc_mir/transform/add_validation.rs
+++ b/src/librustc_mir/transform/add_validation.rs
@@ -141,7 +141,7 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
             }
             // Check if this is an unsafe block, or an item
             match node {
-                Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block), ..}) => {
+                Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block, _), ..}) => {
                     if block_is_unsafe(&*block) {
                         // Found an unsafe block, we can bail out here.
                         return true;
diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs
index 7a54fc72d53..d031694d853 100644
--- a/src/librustc_passes/diagnostics.rs
+++ b/src/librustc_passes/diagnostics.rs
@@ -259,6 +259,44 @@ let result = loop { // ok!
     i += 1;
 };
 ```
+"##,
+
+E0695: r##"
+A `break` statement without a label appeared inside a labeled block.
+
+Example of erroneous code:
+
+```compile_fail,E0695
+# #![feature(label_break_value)]
+loop {
+    'a: {
+        break;
+    }
+}
+```
+
+Make sure to always label the `break`:
+
+```
+# #![feature(label_break_value)]
+'l: loop {
+    'a: {
+        break 'l;
+    }
+}
+```
+
+Or if you want to `break` the labeled block:
+
+```
+# #![feature(label_break_value)]
+loop {
+    'a: {
+        break 'a;
+    }
+    break;
+}
+```
 "##
 }
 
@@ -271,4 +309,5 @@ register_diagnostics! {
     E0642, // patterns aren't allowed in methods without bodies
     E0666, // nested `impl Trait` is illegal
     E0667, // `impl Trait` in projections
+    E0696, // `continue` pointing to a labeled block
 }
diff --git a/src/librustc_passes/loops.rs b/src/librustc_passes/loops.rs
index 81299f4ba9f..2368b1aca69 100644
--- a/src/librustc_passes/loops.rs
+++ b/src/librustc_passes/loops.rs
@@ -13,7 +13,7 @@ use rustc::session::Session;
 
 use rustc::hir::map::Map;
 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
-use rustc::hir;
+use rustc::hir::{self, Destination};
 use syntax::ast;
 use syntax_pos::Span;
 
@@ -39,6 +39,7 @@ enum Context {
     Normal,
     Loop(LoopKind),
     Closure,
+    LabeledBlock,
 }
 
 #[derive(Copy, Clone)]
@@ -84,7 +85,16 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
             hir::ExprClosure(.., b, _, _) => {
                 self.with_context(Closure, |v| v.visit_nested_body(b));
             }
+            hir::ExprBlock(ref b, Some(_label)) => {
+                self.with_context(LabeledBlock, |v| v.visit_block(&b));
+            }
             hir::ExprBreak(label, ref opt_expr) => {
+                if self.require_label_in_labeled_block(e.span, &label, "break") {
+                    // If we emitted an error about an unlabeled break in a labeled
+                    // block, we don't need any further checking for this break any more
+                    return;
+                }
+
                 let loop_id = match label.target_id.into() {
                     Ok(loop_id) => loop_id,
                     Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID,
@@ -94,6 +104,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     },
                     Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
                 };
+
                 if loop_id != ast::DUMMY_NODE_ID {
                     match self.hir_map.find(loop_id).unwrap() {
                         hir::map::NodeBlock(_) => return,
@@ -113,13 +124,15 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                         })
                     };
                     match loop_kind {
-                        None | Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
+                        None |
+                        Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
                         Some(kind) => {
                             struct_span_err!(self.sess, e.span, E0571,
                                              "`break` with value from a `{}` loop",
                                              kind.name())
                                 .span_label(e.span,
-                                            "can only break with a value inside `loop`")
+                                            "can only break with a value inside \
+                                            `loop` or breakable block")
                                 .span_suggestion(e.span,
                                                  &format!("instead, use `break` on its own \
                                                            without a value inside this `{}` loop",
@@ -130,13 +143,29 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     }
                 }
 
-                self.require_loop("break", e.span);
+                self.require_break_cx("break", e.span);
             }
             hir::ExprAgain(label) => {
-                if let Err(hir::LoopIdError::UnlabeledCfInWhileCondition) = label.target_id {
-                    self.emit_unlabled_cf_in_while_condition(e.span, "continue");
+                self.require_label_in_labeled_block(e.span, &label, "continue");
+
+                match label.target_id {
+                    Ok(loop_id) => {
+                        if let hir::map::NodeBlock(block) = self.hir_map.find(loop_id).unwrap() {
+                            struct_span_err!(self.sess, e.span, E0696,
+                                            "`continue` pointing to a labeled block")
+                                .span_label(e.span,
+                                            "labeled blocks cannot be `continue`'d")
+                                .span_note(block.span,
+                                            "labeled block the continue points to")
+                                .emit();
+                        }
+                    }
+                    Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
+                        self.emit_unlabled_cf_in_while_condition(e.span, "continue");
+                    }
+                    _ => {}
                 }
-                self.require_loop("continue", e.span)
+                self.require_break_cx("continue", e.span)
             },
             _ => intravisit::walk_expr(self, e),
         }
@@ -153,8 +182,9 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
         self.cx = old_cx;
     }
 
-    fn require_loop(&self, name: &str, span: Span) {
+    fn require_break_cx(&self, name: &str, span: Span) {
         match self.cx {
+            LabeledBlock |
             Loop(_) => {}
             Closure => {
                 struct_span_err!(self.sess, span, E0267, "`{}` inside of a closure", name)
@@ -169,6 +199,22 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
         }
     }
 
+    fn require_label_in_labeled_block(&mut self, span: Span, label: &Destination, cf_type: &str)
+        -> bool
+    {
+        if self.cx == LabeledBlock {
+            if label.label.is_none() {
+                struct_span_err!(self.sess, span, E0695,
+                                "unlabeled `{}` inside of a labeled block", cf_type)
+                    .span_label(span,
+                                format!("`{}` statements that would diverge to or through \
+                                a labeled block need to bear a label", cf_type))
+                    .emit();
+                return true;
+            }
+        }
+        return false;
+    }
     fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) {
         struct_span_err!(self.sess, span, E0590,
                          "`break` or `continue` with no label in the condition of a `while` loop")
diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs
index f20262c1333..2e737a016da 100644
--- a/src/librustc_passes/rvalue_promotion.rs
+++ b/src/librustc_passes/rvalue_promotion.rs
@@ -342,7 +342,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
             let mut callee = &**callee;
             loop {
                 callee = match callee.node {
-                    hir::ExprBlock(ref block) => match block.expr {
+                    hir::ExprBlock(ref block, _) => match block.expr {
                         Some(ref tail) => &tail,
                         None => break
                     },
@@ -404,7 +404,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
             }
         }
 
-        hir::ExprBlock(_) |
+        hir::ExprBlock(..) |
         hir::ExprIndex(..) |
         hir::ExprField(..) |
         hir::ExprArray(_) |
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 0f931d4374e..d2934bc9ee8 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -3753,6 +3753,8 @@ impl<'a> Resolver<'a> {
                 self.ribs[ValueNS].pop();
             }
 
+            ExprKind::Block(ref block, label) => self.resolve_labeled_block(label, block.id, block),
+
             // Equivalent to `visit::walk_expr` + passing some context to children.
             ExprKind::Field(ref subexpression, _) => {
                 self.resolve_expr(subexpression, Some(expr));
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 877d6b289c9..39de09fe34f 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3536,7 +3536,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         // Warn for non-block expressions with diverging children.
         match expr.node {
-            hir::ExprBlock(_) |
+            hir::ExprBlock(..) |
             hir::ExprLoop(..) | hir::ExprWhile(..) |
             hir::ExprIf(..) | hir::ExprMatch(..) => {}
 
@@ -3912,7 +3912,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
           hir::ExprClosure(capture, ref decl, body_id, _, gen) => {
               self.check_expr_closure(expr, capture, &decl, body_id, gen, expected)
           }
-          hir::ExprBlock(ref body) => {
+          hir::ExprBlock(ref body, _) => {
             self.check_block_with_expected(&body, expected)
           }
           hir::ExprCall(ref callee, ref args) => {
@@ -4326,8 +4326,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         };
 
         // 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
+        // can be targeted by multiple breaks. This can happen both
+        // with labeled blocks as well as when we desugar
         // a `do catch { ... }` expression.
         //
         // Example 1:
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index f8cd6103bdf..2b6635ec783 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -934,7 +934,7 @@ impl Expr {
     /// Whether this expression would be valid somewhere that expects a value, for example, an `if`
     /// condition.
     pub fn returns(&self) -> bool {
-        if let ExprKind::Block(ref block) = self.node {
+        if let ExprKind::Block(ref block, _) = self.node {
             match block.stmts.last().map(|last_stmt| &last_stmt.node) {
                 // implicit return
                 Some(&StmtKind::Expr(_)) => true,
@@ -1121,8 +1121,8 @@ pub enum ExprKind {
     ///
     /// The final span is the span of the argument block `|...|`
     Closure(CaptureBy, Movability, P<FnDecl>, P<Expr>, Span),
-    /// A block (`{ ... }`)
-    Block(P<Block>),
+    /// A block (`'label: { ... }`)
+    Block(P<Block>, Option<Label>),
     /// A catch block (`catch { ... }`)
     Catch(P<Block>),
 
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 0b64189b2bc..a4f9ebcf418 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -668,7 +668,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         self.expr(span, ast::ExprKind::MethodCall(segment, args))
     }
     fn expr_block(&self, b: P<ast::Block>) -> P<ast::Expr> {
-        self.expr(b.span, ast::ExprKind::Block(b))
+        self.expr(b.span, ast::ExprKind::Block(b, None))
     }
     fn field_imm(&self, span: Span, ident: Ident, e: P<ast::Expr>) -> ast::Field {
         ast::Field {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index bf78723e413..f1229520c77 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -466,6 +466,9 @@ declare_features! (
 
     // inconsistent bounds in where clauses
     (active, trivial_bounds, "1.28.0", Some(48214), None),
+
+    // 'a: { break 'a; }
+    (active, label_break_value, "1.28.0", Some(48594), None),
 );
 
 declare_features! (
@@ -1696,6 +1699,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                                     "multiple patterns in `if let` and `while let` are unstable");
                 }
             }
+            ast::ExprKind::Block(_, opt_label) => {
+                if let Some(label) = opt_label {
+                    gate_feature_post!(&self, label_break_value, label.ident.span,
+                                    "labels on blocks are unstable");
+                }
+            }
             _ => {}
         }
         visit::walk_expr(self, e);
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index d67995761f6..29cc208c06b 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1256,7 +1256,10 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
                                   folder.fold_expr(body),
                                   folder.new_span(span))
             }
-            ExprKind::Block(blk) => ExprKind::Block(folder.fold_block(blk)),
+            ExprKind::Block(blk, opt_label) => {
+                ExprKind::Block(folder.fold_block(blk),
+                                opt_label.map(|label| folder.fold_label(label)))
+            }
             ExprKind::Assign(el, er) => {
                 ExprKind::Assign(folder.fold_expr(el), folder.fold_expr(er))
             }
diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs
index b8e02556625..531483e7de1 100644
--- a/src/libsyntax/parse/classify.rs
+++ b/src/libsyntax/parse/classify.rs
@@ -26,7 +26,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
         ast::ExprKind::If(..) |
         ast::ExprKind::IfLet(..) |
         ast::ExprKind::Match(..) |
-        ast::ExprKind::Block(_) |
+        ast::ExprKind::Block(..) |
         ast::ExprKind::While(..) |
         ast::ExprKind::WhileLet(..) |
         ast::ExprKind::Loop(..) |
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 3f0df6d055b..7b91c491700 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -128,7 +128,7 @@ macro_rules! maybe_whole_expr {
                 token::NtBlock(ref block) => {
                     $p.bump();
                     let span = $p.span;
-                    let kind = ExprKind::Block((*block).clone());
+                    let kind = ExprKind::Block((*block).clone(), None);
                     return Ok($p.mk_expr(span, kind, ThinVec::new()));
                 }
                 _ => {},
@@ -2244,7 +2244,7 @@ impl<'a> Parser<'a> {
                 };
             }
             token::OpenDelim(token::Brace) => {
-                return self.parse_block_expr(lo, BlockCheckMode::Default, attrs);
+                return self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs);
             }
             token::BinOp(token::Or) | token::OrOr => {
                 return self.parse_lambda_expr(attrs);
@@ -2318,7 +2318,13 @@ impl<'a> Parser<'a> {
                     if self.eat_keyword(keywords::Loop) {
                         return self.parse_loop_expr(Some(label), lo, attrs)
                     }
-                    let msg = "expected `while`, `for`, or `loop` after a label";
+                    if self.token == token::OpenDelim(token::Brace) {
+                        return self.parse_block_expr(Some(label),
+                                                     lo,
+                                                     BlockCheckMode::Default,
+                                                     attrs);
+                    }
+                    let msg = "expected `while`, `for`, `loop` or `{` after a label";
                     let mut err = self.fatal(msg);
                     err.span_label(self.span, msg);
                     return Err(err);
@@ -2338,6 +2344,7 @@ impl<'a> Parser<'a> {
                 }
                 if self.eat_keyword(keywords::Unsafe) {
                     return self.parse_block_expr(
+                        None,
                         lo,
                         BlockCheckMode::Unsafe(ast::UserProvided),
                         attrs);
@@ -2502,7 +2509,8 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a block or unsafe block
-    pub fn parse_block_expr(&mut self, lo: Span, blk_mode: BlockCheckMode,
+    pub fn parse_block_expr(&mut self, opt_label: Option<Label>,
+                            lo: Span, blk_mode: BlockCheckMode,
                             outer_attrs: ThinVec<Attribute>)
                             -> PResult<'a, P<Expr>> {
         self.expect(&token::OpenDelim(token::Brace))?;
@@ -2511,7 +2519,7 @@ impl<'a> Parser<'a> {
         attrs.extend(self.parse_inner_attributes()?);
 
         let blk = self.parse_block_tail(lo, blk_mode)?;
-        return Ok(self.mk_expr(blk.span, ExprKind::Block(blk), attrs));
+        return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs));
     }
 
     /// parse a.b or a(13) or a[4] or just a
@@ -3261,7 +3269,7 @@ impl<'a> Parser<'a> {
                 // If an explicit return type is given, require a
                 // block to appear (RFC 968).
                 let body_lo = self.span;
-                self.parse_block_expr(body_lo, BlockCheckMode::Default, ThinVec::new())?
+                self.parse_block_expr(None, body_lo, BlockCheckMode::Default, ThinVec::new())?
             }
         };
 
@@ -3277,7 +3285,7 @@ impl<'a> Parser<'a> {
             return self.parse_if_expr(ThinVec::new());
         } else {
             let blk = self.parse_block()?;
-            return Ok(self.mk_expr(blk.span, ExprKind::Block(blk), ThinVec::new()));
+            return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), ThinVec::new()));
         }
     }
 
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 8e33fa08083..a700799cde5 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1792,7 +1792,7 @@ impl<'a> State<'a> {
                         self.print_else(e.as_ref().map(|e| &**e))
                     }
                     // "final else"
-                    ast::ExprKind::Block(ref b) => {
+                    ast::ExprKind::Block(ref b, _) => {
                         self.cbox(INDENT_UNIT - 1)?;
                         self.ibox(0)?;
                         self.s.word(" else ")?;
@@ -2182,7 +2182,11 @@ impl<'a> State<'a> {
                 // empty box to satisfy the close.
                 self.ibox(0)?;
             }
-            ast::ExprKind::Block(ref blk) => {
+            ast::ExprKind::Block(ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident)?;
+                    self.word_space(":")?;
+                }
                 // containing cbox, will be closed by print-block at }
                 self.cbox(INDENT_UNIT)?;
                 // head-box, will be closed by print-block after {
@@ -2695,7 +2699,12 @@ impl<'a> State<'a> {
         self.word_space("=>")?;
 
         match arm.body.node {
-            ast::ExprKind::Block(ref blk) => {
+            ast::ExprKind::Block(ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident)?;
+                    self.word_space(":")?;
+                }
+
                 // the block will close the pattern's ibox
                 self.print_block_unclosed_indent(blk, INDENT_UNIT)?;
 
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 40d59d3ff8b..2013e838c05 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -736,7 +736,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
                              expression.span,
                              expression.id)
         }
-        ExprKind::Block(ref block) => visitor.visit_block(block),
+        ExprKind::Block(ref block, ref opt_label) => {
+            walk_list!(visitor, visit_label, opt_label);
+            visitor.visit_block(block);
+        }
         ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => {
             visitor.visit_expr(left_hand_expression);
             visitor.visit_expr(right_hand_expression);
diff --git a/src/test/run-pass/label_break_value.rs b/src/test/run-pass/label_break_value.rs
new file mode 100644
index 00000000000..444845e24dc
--- /dev/null
+++ b/src/test/run-pass/label_break_value.rs
@@ -0,0 +1,123 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(label_break_value)]
+
+// Test control flow to follow label_break_value semantics
+fn label_break(a: bool, b: bool) -> u32 {
+    let mut v = 0;
+    'b: {
+        v = 1;
+        if a {
+            break 'b;
+        }
+        v = 2;
+        if b {
+            break 'b;
+        }
+        v = 3;
+    }
+    return v;
+}
+
+// Test that values can be returned
+fn break_value(a: bool, b: bool) -> u32 {
+    let result = 'block: {
+        if a { break 'block 1; }
+        if b { break 'block 2; }
+        3
+    };
+    result
+}
+
+// Test nesting of labeled blocks
+// here we only check that it compiles
+fn label_break_nested() {
+    'b: {
+        println!("hi");
+        if false {
+            break 'b;
+        }
+        'c: {
+            if false {
+                break 'b;
+            }
+            break 'c;
+        }
+        println!("hello");
+        if true {
+            break 'b;
+        }
+    }
+}
+
+// Tests for mixing labeled blocks with loop constructs
+// This function should be the identity function
+fn label_break_mixed(v: u32) -> u32 {
+    let mut r = 0;
+    'b: {
+        // Unlabeled break still works
+        // (only crossing boundaries is an error)
+        loop {
+            break;
+        }
+        if v == 0 {
+            break 'b;
+        }
+        // Labeled breaking an inner loop still works
+        'c: loop {
+            if r == 1 {
+                break 'c;
+            }
+            r += 1;
+        }
+        assert_eq!(r, 1);
+        if v == 1 {
+            break 'b;
+        }
+        // Labeled breaking an outer loop still works
+        'd: loop {
+            'e: {
+                if v == r {
+                    break 'b;
+                }
+                if r == 5 {
+                    break 'd;
+                }
+                r += 1;
+            }
+        }
+        assert_eq!(r, 5);
+        assert!(v > r);
+        // Here we test return from inside a labeled block
+        return v;
+    }
+    r
+}
+
+pub fn main() {
+    assert_eq!(label_break(true, false), 1);
+    assert_eq!(label_break(false, true), 2);
+    assert_eq!(label_break(false, false), 3);
+
+    assert_eq!(break_value(true, false), 1);
+    assert_eq!(break_value(false, true), 2);
+    assert_eq!(break_value(false, false), 3);
+
+    assert_eq!(label_break_mixed(0), 0);
+    assert_eq!(label_break_mixed(1), 1);
+    assert_eq!(label_break_mixed(2), 2);
+    assert_eq!(label_break_mixed(3), 3);
+    assert_eq!(label_break_mixed(4), 4);
+    assert_eq!(label_break_mixed(5), 5);
+    assert_eq!(label_break_mixed(6), 6);
+
+    // FIXME: ensure that labeled blocks work if produced by macros and in match arms
+}
diff --git a/src/test/ui/compare-method/proj-outlives-region.stdout b/src/test/ui/compare-method/proj-outlives-region.stdout
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/compare-method/proj-outlives-region.stdout
+++ /dev/null
diff --git a/src/test/ui/compare-method/region-extra.stdout b/src/test/ui/compare-method/region-extra.stdout
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/compare-method/region-extra.stdout
+++ /dev/null
diff --git a/src/test/ui/compare-method/region-unrelated.stdout b/src/test/ui/compare-method/region-unrelated.stdout
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/compare-method/region-unrelated.stdout
+++ /dev/null
diff --git a/src/test/ui/const-eval/const_transmute.stderr b/src/test/ui/const-eval/const_transmute.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/const-eval/const_transmute.stderr
+++ /dev/null
diff --git a/src/test/ui/const-expr-addr-operator.stderr b/src/test/ui/const-expr-addr-operator.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/const-expr-addr-operator.stderr
+++ /dev/null
diff --git a/src/test/ui/const-pattern-not-const-evaluable.stderr b/src/test/ui/const-pattern-not-const-evaluable.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/const-pattern-not-const-evaluable.stderr
+++ /dev/null
diff --git a/src/test/ui/feature-gate-const-indexing.stderr b/src/test/ui/feature-gate-const-indexing.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/feature-gate-const-indexing.stderr
+++ /dev/null
diff --git a/src/test/ui/feature-gate-label_break_value.rs b/src/test/ui/feature-gate-label_break_value.rs
new file mode 100644
index 00000000000..dcda7580bda
--- /dev/null
+++ b/src/test/ui/feature-gate-label_break_value.rs
@@ -0,0 +1,15 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub fn main() {
+    'a: { //~ ERROR labels on blocks are unstable
+        break 'a;
+    }
+}
diff --git a/src/test/ui/feature-gate-label_break_value.stderr b/src/test/ui/feature-gate-label_break_value.stderr
new file mode 100644
index 00000000000..8e8f31e7871
--- /dev/null
+++ b/src/test/ui/feature-gate-label_break_value.stderr
@@ -0,0 +1,11 @@
+error[E0658]: labels on blocks are unstable (see issue #48594)
+  --> $DIR/feature-gate-label_break_value.rs:12:5
+   |
+LL |     'a: { //~ ERROR labels on blocks are unstable
+   |     ^^
+   |
+   = help: add #![feature(label_break_value)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/issue-38875/issue_38875.stderr b/src/test/ui/issue-38875/issue_38875.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/issue-38875/issue_38875.stderr
+++ /dev/null
diff --git a/src/test/ui/label_break_value_continue.rs b/src/test/ui/label_break_value_continue.rs
new file mode 100644
index 00000000000..52e24b759d1
--- /dev/null
+++ b/src/test/ui/label_break_value_continue.rs
@@ -0,0 +1,36 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(label_break_value)]
+
+// Simple continue pointing to an unlabeled break should yield in an error
+fn continue_simple() {
+    'b: {
+        continue; //~ ERROR unlabeled `continue` inside of a labeled block
+    }
+}
+
+// Labeled continue pointing to an unlabeled break should yield in an error
+fn continue_labeled() {
+    'b: {
+        continue 'b; //~ ERROR `continue` pointing to a labeled block
+    }
+}
+
+// Simple continue that would cross a labeled block should yield in an error
+fn continue_crossing() {
+    loop {
+        'b: {
+            continue; //~ ERROR unlabeled `continue` inside of a labeled block
+        }
+    }
+}
+
+pub fn main() {}
diff --git a/src/test/ui/label_break_value_continue.stderr b/src/test/ui/label_break_value_continue.stderr
new file mode 100644
index 00000000000..24c2d1a22d0
--- /dev/null
+++ b/src/test/ui/label_break_value_continue.stderr
@@ -0,0 +1,30 @@
+error[E0695]: unlabeled `continue` inside of a labeled block
+  --> $DIR/label_break_value_continue.rs:16:9
+   |
+LL |         continue; //~ ERROR unlabeled `continue` inside of a labeled block
+   |         ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label
+
+error[E0696]: `continue` pointing to a labeled block
+  --> $DIR/label_break_value_continue.rs:23:9
+   |
+LL |         continue 'b; //~ ERROR `continue` pointing to a labeled block
+   |         ^^^^^^^^^^^ labeled blocks cannot be `continue`'d
+   |
+note: labeled block the continue points to
+  --> $DIR/label_break_value_continue.rs:22:5
+   |
+LL | /     'b: {
+LL | |         continue 'b; //~ ERROR `continue` pointing to a labeled block
+LL | |     }
+   | |_____^
+
+error[E0695]: unlabeled `continue` inside of a labeled block
+  --> $DIR/label_break_value_continue.rs:31:13
+   |
+LL |             continue; //~ ERROR unlabeled `continue` inside of a labeled block
+   |             ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label
+
+error: aborting due to 3 previous errors
+
+Some errors occurred: E0695, E0696.
+For more information about an error, try `rustc --explain E0695`.
diff --git a/src/test/ui/label_break_value_illegal_uses.rs b/src/test/ui/label_break_value_illegal_uses.rs
new file mode 100644
index 00000000000..18644033e4f
--- /dev/null
+++ b/src/test/ui/label_break_value_illegal_uses.rs
@@ -0,0 +1,31 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(label_break_value)]
+
+// These are forbidden occurences of label-break-value
+
+fn labeled_unsafe() {
+    unsafe 'b: {} //~ ERROR expected one of `extern`, `fn`, or `{`
+}
+
+fn labeled_if() {
+    if true 'b: {} //~ ERROR expected `{`, found `'b`
+}
+
+fn labeled_else() {
+    if true {} else 'b: {} //~ ERROR expected `{`, found `'b`
+}
+
+fn labeled_match() {
+    match false 'b: {} //~ ERROR expected one of `.`, `?`, `{`, or an operator
+}
+
+pub fn main() {}
diff --git a/src/test/ui/label_break_value_illegal_uses.stderr b/src/test/ui/label_break_value_illegal_uses.stderr
new file mode 100644
index 00000000000..0ab1ad2c242
--- /dev/null
+++ b/src/test/ui/label_break_value_illegal_uses.stderr
@@ -0,0 +1,31 @@
+error: expected one of `extern`, `fn`, or `{`, found `'b`
+  --> $DIR/label_break_value_illegal_uses.rs:16:12
+   |
+LL |     unsafe 'b: {} //~ ERROR expected one of `extern`, `fn`, or `{`
+   |            ^^ expected one of `extern`, `fn`, or `{` here
+
+error: expected `{`, found `'b`
+  --> $DIR/label_break_value_illegal_uses.rs:20:13
+   |
+LL |     if true 'b: {} //~ ERROR expected `{`, found `'b`
+   |     --      ^^----
+   |     |       |
+   |     |       help: try placing this code inside a block: `{ 'b: { } }`
+   |     this `if` statement has a condition, but no block
+
+error: expected `{`, found `'b`
+  --> $DIR/label_break_value_illegal_uses.rs:24:21
+   |
+LL |     if true {} else 'b: {} //~ ERROR expected `{`, found `'b`
+   |                     ^^----
+   |                     |
+   |                     help: try placing this code inside a block: `{ 'b: { } }`
+
+error: expected one of `.`, `?`, `{`, or an operator, found `'b`
+  --> $DIR/label_break_value_illegal_uses.rs:28:17
+   |
+LL |     match false 'b: {} //~ ERROR expected one of `.`, `?`, `{`, or an operator
+   |                 ^^ expected one of `.`, `?`, `{`, or an operator here
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/label_break_value_unlabeled_break.rs b/src/test/ui/label_break_value_unlabeled_break.rs
new file mode 100644
index 00000000000..38918da291c
--- /dev/null
+++ b/src/test/ui/label_break_value_unlabeled_break.rs
@@ -0,0 +1,29 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(label_break_value)]
+
+// Simple unlabeled break should yield in an error
+fn unlabeled_break_simple() {
+    'b: {
+        break; //~ ERROR unlabeled `break` inside of a labeled block
+    }
+}
+
+// Unlabeled break that would cross a labeled block should yield in an error
+fn unlabeled_break_crossing() {
+    loop {
+        'b: {
+            break; //~ ERROR unlabeled `break` inside of a labeled block
+        }
+    }
+}
+
+pub fn main() {}
diff --git a/src/test/ui/label_break_value_unlabeled_break.stderr b/src/test/ui/label_break_value_unlabeled_break.stderr
new file mode 100644
index 00000000000..8a25975a7bd
--- /dev/null
+++ b/src/test/ui/label_break_value_unlabeled_break.stderr
@@ -0,0 +1,15 @@
+error[E0695]: unlabeled `break` inside of a labeled block
+  --> $DIR/label_break_value_unlabeled_break.rs:16:9
+   |
+LL |         break; //~ ERROR unlabeled `break` inside of a labeled block
+   |         ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
+
+error[E0695]: unlabeled `break` inside of a labeled block
+  --> $DIR/label_break_value_unlabeled_break.rs:24:13
+   |
+LL |             break; //~ ERROR unlabeled `break` inside of a labeled block
+   |             ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0695`.
diff --git a/src/test/ui/loop-break-value-no-repeat.stderr b/src/test/ui/loop-break-value-no-repeat.stderr
index 4421f557e42..68a2bab1674 100644
--- a/src/test/ui/loop-break-value-no-repeat.stderr
+++ b/src/test/ui/loop-break-value-no-repeat.stderr
@@ -2,7 +2,7 @@ error[E0571]: `break` with value from a `for` loop
   --> $DIR/loop-break-value-no-repeat.rs:22:9
    |
 LL |         break 22 //~ ERROR `break` with value from a `for` loop
-   |         ^^^^^^^^ can only break with a value inside `loop`
+   |         ^^^^^^^^ can only break with a value inside `loop` or breakable block
 help: instead, use `break` on its own without a value inside this `for` loop
    |
 LL |         break //~ ERROR `break` with value from a `for` loop
diff --git a/src/test/ui/nll/drop-may-dangle.stderr b/src/test/ui/nll/drop-may-dangle.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/nll/drop-may-dangle.stderr
+++ /dev/null
diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr b/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr
+++ /dev/null
diff --git a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr
+++ /dev/null
diff --git a/src/test/ui/print_type_sizes/anonymous.stderr b/src/test/ui/print_type_sizes/anonymous.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/print_type_sizes/anonymous.stderr
+++ /dev/null
diff --git a/src/test/ui/print_type_sizes/anonymous.stdout b/src/test/ui/print_type_sizes/anonymous.stdout
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/print_type_sizes/anonymous.stdout
+++ /dev/null
diff --git a/src/test/ui/print_type_sizes/multiple_types.stderr b/src/test/ui/print_type_sizes/multiple_types.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/print_type_sizes/multiple_types.stderr
+++ /dev/null
diff --git a/src/test/ui/print_type_sizes/packed.stderr b/src/test/ui/print_type_sizes/packed.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/print_type_sizes/packed.stderr
+++ /dev/null
diff --git a/src/test/ui/print_type_sizes/repr-align.stderr b/src/test/ui/print_type_sizes/repr-align.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/print_type_sizes/repr-align.stderr
+++ /dev/null
diff --git a/src/test/ui/reachable/expr_andand.stderr b/src/test/ui/reachable/expr_andand.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/reachable/expr_andand.stderr
+++ /dev/null
diff --git a/src/test/ui/reachable/expr_oror.stderr b/src/test/ui/reachable/expr_oror.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/reachable/expr_oror.stderr
+++ /dev/null
diff --git a/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr b/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr
+++ /dev/null
diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout b/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout
+++ /dev/null
diff --git a/src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr b/src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr
+++ /dev/null
diff --git a/src/test/ui/union/union-const-eval.stderr b/src/test/ui/union/union-const-eval.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/test/ui/union/union-const-eval.stderr
+++ /dev/null
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 351005ff4b8..c1574407807 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -10,32 +10,49 @@
 
 //! Tidy check to ensure that there are no stray `.stderr` files in UI test directories.
 
+use std::fs;
 use std::path::Path;
 
 pub fn check(path: &Path, bad: &mut bool) {
-    super::walk_many(&[&path.join("test/ui"), &path.join("test/ui-fulldeps")],
-                     &mut |_| false,
-                     &mut |file_path| {
-        if let Some(ext) = file_path.extension() {
-            if ext == "stderr" || ext == "stdout" {
-                // Test output filenames have the format:
-                // $testname.stderr
-                // $testname.$mode.stderr
-                // $testname.$revision.stderr
-                // $testname.$revision.$mode.stderr
-                //
-                // For now, just make sure that there is a corresponding
-                // $testname.rs file.
-                let testname = file_path.file_name().unwrap()
-                                        .to_str().unwrap()
-                                        .splitn(2, '.').next().unwrap();
-                if !file_path.with_file_name(testname)
-                             .with_extension("rs")
-                             .exists() {
-                    println!("Stray file with UI testing output: {:?}", file_path);
-                    *bad = true;
+    super::walk_many(
+        &[&path.join("test/ui"), &path.join("test/ui-fulldeps")],
+        &mut |_| false,
+        &mut |file_path| {
+            if let Some(ext) = file_path.extension() {
+                if ext == "stderr" || ext == "stdout" {
+                    // Test output filenames have the format:
+                    // $testname.stderr
+                    // $testname.$mode.stderr
+                    // $testname.$revision.stderr
+                    // $testname.$revision.$mode.stderr
+                    //
+                    // For now, just make sure that there is a corresponding
+                    // $testname.rs file.
+                    let testname = file_path
+                        .file_name()
+                        .unwrap()
+                        .to_str()
+                        .unwrap()
+                        .splitn(2, '.')
+                        .next()
+                        .unwrap();
+                    if !file_path
+                        .with_file_name(testname)
+                        .with_extension("rs")
+                        .exists()
+                    {
+                        println!("Stray file with UI testing output: {:?}", file_path);
+                        *bad = true;
+                    }
+
+                    if let Ok(metadata) = fs::metadata(file_path) {
+                        if metadata.len() == 0 {
+                            println!("Empty file with UI testing output: {:?}", file_path);
+                            *bad = true;
+                        }
+                    }
                 }
             }
-        }
-    });
+        },
+    );
 }