From 5665980ad8d2399895ae141985f6776ab45c9ee5 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 25 Apr 2018 19:28:04 +0200 Subject: [PATCH] Make the compiler support the label-break-value feature No error checking or feature gating yet --- src/librustc/hir/lowering.rs | 4 ++- src/librustc/hir/mod.rs | 5 ++-- src/librustc_mir/build/block.rs | 2 +- src/librustc_passes/loops.rs | 26 +++++++++++++++---- src/librustc_resolve/lib.rs | 2 ++ src/librustc_typeck/check/mod.rs | 4 +-- src/test/ui/loop-break-value-no-repeat.stderr | 2 +- 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 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr { }) } 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 @@ pub fn ast_block(&mut self, 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 @@ enum LoopKind { Loop(hir::LoopSource), WhileLoop, + Block, } impl LoopKind { @@ -30,6 +31,7 @@ fn name(self) -> &'static str { 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 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) { 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 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) { }, 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 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) { } } + 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 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) { 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 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) { } } - 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 @@ fn with_context(&mut self, cx: Context, f: F) 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 @@ fn resolve_expr(&mut self, expr: &Expr, parent: Option<&Expr>) { 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 @@ fn check_block_with_expected(&self, }; // 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 -- 2.44.0