about summary refs log tree commit diff
diff options
context:
space:
mode:
authorest31 <MTest31@outlook.com>2018-04-25 19:28:04 +0200
committerest31 <MTest31@outlook.com>2018-05-16 13:56:24 +0200
commit5665980ad8d2399895ae141985f6776ab45c9ee5 (patch)
tree476805a843bb5ec13db391f0d1f9005643c6295b
parentebca9c6ab9332e0befea6ca57bd4789e606cd305 (diff)
downloadrust-5665980ad8d2399895ae141985f6776ab45c9ee5.tar.gz
rust-5665980ad8d2399895ae141985f6776ab45c9ee5.zip
Make the compiler support the label-break-value feature
No error checking or feature gating yet
-rw-r--r--src/librustc/hir/lowering.rs4
-rw-r--r--src/librustc/hir/mod.rs5
-rw-r--r--src/librustc_mir/build/block.rs2
-rw-r--r--src/librustc_passes/loops.rs26
-rw-r--r--src/librustc_resolve/lib.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs4
-rw-r--r--src/test/ui/loop-break-value-no-repeat.stderr2
7 files changed, 32 insertions, 13 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 09fff64094f..0f4871954d6 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -3101,7 +3101,9 @@ impl<'a> LoweringContext<'a> {
                 })
             }
             ExprKind::Block(ref blk, opt_label) => {
-                hir::ExprBlock(self.lower_block(blk, false), self.lower_label(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)))
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 722a920d91c..79c0f2de796 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
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_passes/loops.rs b/src/librustc_passes/loops.rs
index 81299f4ba9f..34a3f5e54b8 100644
--- a/src/librustc_passes/loops.rs
+++ b/src/librustc_passes/loops.rs
@@ -21,6 +21,7 @@ use syntax_pos::Span;
 enum LoopKind {
     Loop(hir::LoopSource),
     WhileLoop,
+    Block,
 }
 
 impl LoopKind {
@@ -30,6 +31,7 @@ impl LoopKind {
             LoopKind::Loop(hir::LoopSource::WhileLet) => "while let",
             LoopKind::Loop(hir::LoopSource::ForLoop) => "for",
             LoopKind::WhileLoop => "while",
+            LoopKind::Block => "block",
         }
     }
 }
@@ -39,6 +41,7 @@ enum Context {
     Normal,
     Loop(LoopKind),
     Closure,
+    LabeledBlock,
 }
 
 #[derive(Copy, Clone)]
@@ -84,6 +87,9 @@ 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) => {
                 let loop_id = match label.target_id.into() {
                     Ok(loop_id) => loop_id,
@@ -94,6 +100,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,
@@ -101,6 +108,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     }
                 }
 
+                if self.cx == LabeledBlock {
+                    return;
+                }
+
                 if opt_expr.is_some() {
                     let loop_kind = if loop_id == ast::DUMMY_NODE_ID {
                         None
@@ -108,18 +119,22 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                         Some(match self.hir_map.expect_expr(loop_id).node {
                             hir::ExprWhile(..) => LoopKind::WhileLoop,
                             hir::ExprLoop(_, _, source) => LoopKind::Loop(source),
+                            hir::ExprBlock(..) => LoopKind::Block,
                             ref r => span_bug!(e.span,
                                                "break label resolved to a non-loop: {:?}", r),
                         })
                     };
                     match loop_kind {
-                        None | Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
+                        None |
+                        Some(LoopKind::Loop(hir::LoopSource::Loop)) |
+                        Some(LoopKind::Block) => (),
                         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 +145,13 @@ 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_loop("continue", e.span)
+                self.require_break_cx("continue", e.span)
             },
             _ => intravisit::walk_expr(self, e),
         }
@@ -153,8 +168,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)
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 86c8ab66f77..e1798e26171 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -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/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