Skip to content

Commit 0e325d0

Browse files
committed
Auto merge of #50045 - est31:label_break_value, r=eddyb
Implement label break value (RFC 2046) Implement label-break-value (#48594).
2 parents 2a3f536 + ae1553a commit 0e325d0

36 files changed

+510
-54
lines changed

src/librustc/cfg/construct.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
179179

180180
fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex {
181181
match expr.node {
182-
hir::ExprBlock(ref blk) => {
182+
hir::ExprBlock(ref blk, _) => {
183183
let blk_exit = self.block(&blk, pred);
184184
self.add_ast_node(expr.hir_id.local_id, &[blk_exit])
185185
}

src/librustc/hir/intravisit.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
10151015
expression.span,
10161016
expression.id)
10171017
}
1018-
ExprBlock(ref block) => visitor.visit_block(block),
1018+
ExprBlock(ref block, ref opt_label) => {
1019+
walk_list!(visitor, visit_label, opt_label);
1020+
visitor.visit_block(block);
1021+
}
10191022
ExprAssign(ref left_hand_expression, ref right_hand_expression) => {
10201023
visitor.visit_expr(right_hand_expression);
10211024
visitor.visit_expr(left_hand_expression)

src/librustc/hir/lowering.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -3048,7 +3048,7 @@ impl<'a> LoweringContext<'a> {
30483048
);
30493049
block.expr = Some(this.wrap_in_try_constructor(
30503050
"from_ok", tail, unstable_span));
3051-
hir::ExprBlock(P(block))
3051+
hir::ExprBlock(P(block), None)
30523052
})
30533053
}
30543054
ExprKind::Match(ref expr, ref arms) => hir::ExprMatch(
@@ -3100,7 +3100,11 @@ impl<'a> LoweringContext<'a> {
31003100
})
31013101
})
31023102
}
3103-
ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, false)),
3103+
ExprKind::Block(ref blk, opt_label) => {
3104+
hir::ExprBlock(self.lower_block(blk,
3105+
opt_label.is_some()),
3106+
self.lower_label(opt_label))
3107+
}
31043108
ExprKind::Assign(ref el, ref er) => {
31053109
hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
31063110
}
@@ -3843,7 +3847,7 @@ impl<'a> LoweringContext<'a> {
38433847
}
38443848

38453849
fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr {
3846-
self.expr(b.span, hir::ExprBlock(b), attrs)
3850+
self.expr(b.span, hir::ExprBlock(b, None), attrs)
38473851
}
38483852

38493853
fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec<hir::Expr>) -> P<hir::Expr> {

src/librustc/hir/mod.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -778,9 +778,8 @@ pub struct Block {
778778
pub rules: BlockCheckMode,
779779
pub span: Span,
780780
/// If true, then there may exist `break 'a` values that aim to
781-
/// break out of this block early. As of this writing, this is not
782-
/// currently permitted in Rust itself, but it is generated as
783-
/// part of `catch` statements.
781+
/// break out of this block early.
782+
/// Used by `'label: {}` blocks and by `catch` statements.
784783
pub targeted_by_break: bool,
785784
/// If true, don't emit return value type errors as the parser had
786785
/// to recover from a parse error so this block will not have an
@@ -1381,8 +1380,8 @@ pub enum Expr_ {
13811380
/// This may also be a generator literal, indicated by the final boolean,
13821381
/// in that case there is an GeneratorClause.
13831382
ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, Option<GeneratorMovability>),
1384-
/// A block (`{ ... }`)
1385-
ExprBlock(P<Block>),
1383+
/// A block (`'label: { ... }`)
1384+
ExprBlock(P<Block>, Option<Label>),
13861385

13871386
/// An assignment (`a = foo()`)
13881387
ExprAssign(P<Expr>, P<Expr>),

src/librustc/hir/print.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@ impl<'a> State<'a> {
10471047
self.print_else(e.as_ref().map(|e| &**e))
10481048
}
10491049
// "final else"
1050-
hir::ExprBlock(ref b) => {
1050+
hir::ExprBlock(ref b, _) => {
10511051
self.cbox(indent_unit - 1)?;
10521052
self.ibox(0)?;
10531053
self.s.word(" else ")?;
@@ -1377,7 +1377,11 @@ impl<'a> State<'a> {
13771377
// empty box to satisfy the close.
13781378
self.ibox(0)?;
13791379
}
1380-
hir::ExprBlock(ref blk) => {
1380+
hir::ExprBlock(ref blk, opt_label) => {
1381+
if let Some(label) = opt_label {
1382+
self.print_name(label.name)?;
1383+
self.word_space(":")?;
1384+
}
13811385
// containing cbox, will be closed by print-block at }
13821386
self.cbox(indent_unit)?;
13831387
// head-box, will be closed by print-block after {
@@ -1893,7 +1897,11 @@ impl<'a> State<'a> {
18931897
self.word_space("=>")?;
18941898

18951899
match arm.body.node {
1896-
hir::ExprBlock(ref blk) => {
1900+
hir::ExprBlock(ref blk, opt_label) => {
1901+
if let Some(label) = opt_label {
1902+
self.print_name(label.name)?;
1903+
self.word_space(":")?;
1904+
}
18971905
// the block will close the pattern's ibox
18981906
self.print_block_unclosed_indent(&blk, indent_unit)?;
18991907

@@ -2299,7 +2307,7 @@ fn expr_requires_semi_to_be_stmt(e: &hir::Expr) -> bool {
22992307
match e.node {
23002308
hir::ExprIf(..) |
23012309
hir::ExprMatch(..) |
2302-
hir::ExprBlock(_) |
2310+
hir::ExprBlock(..) |
23032311
hir::ExprWhile(..) |
23042312
hir::ExprLoop(..) => false,
23052313
_ => true,

src/librustc/ich/impls_hir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ impl_stable_hash_for!(enum hir::Expr_ {
589589
ExprLoop(body, label, loop_src),
590590
ExprMatch(matchee, arms, match_src),
591591
ExprClosure(capture_clause, decl, body_id, span, gen),
592-
ExprBlock(blk),
592+
ExprBlock(blk, label),
593593
ExprAssign(lhs, rhs),
594594
ExprAssignOp(op, lhs, rhs),
595595
ExprField(owner, field_name),

src/librustc/middle/expr_use_visitor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
499499
self.consume_expr(&rhs);
500500
}
501501

502-
hir::ExprBlock(ref blk) => {
502+
hir::ExprBlock(ref blk, _) => {
503503
self.walk_block(&blk);
504504
}
505505

src/librustc/middle/liveness.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
11871187
succ
11881188
}
11891189

1190-
hir::ExprBlock(ref blk) => {
1190+
// Note that labels have been resolved, so we don't need to look
1191+
// at the label ident
1192+
hir::ExprBlock(ref blk, _) => {
11911193
self.propagate_through_block(&blk, succ)
11921194
}
11931195
}

src/librustc/middle/region.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1247,7 +1247,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
12471247
hir::ExprCast(ref subexpr, _) => {
12481248
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id)
12491249
}
1250-
hir::ExprBlock(ref block) => {
1250+
hir::ExprBlock(ref block, _) => {
12511251
if let Some(ref subexpr) = block.expr {
12521252
record_rvalue_scope_if_borrow_expr(
12531253
visitor, &subexpr, blk_id);

src/librustc_lint/builtin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ impl UnsafeCode {
228228

229229
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode {
230230
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
231-
if let hir::ExprBlock(ref blk) = e.node {
231+
if let hir::ExprBlock(ref blk, _) = e.node {
232232
// Don't warn about generated blocks, that'll just pollute the output.
233233
if blk.rules == hir::UnsafeBlock(hir::UserProvided) {
234234
self.report_unsafe(cx, blk.span, "usage of an `unsafe` block");

src/librustc_mir/build/block.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
3636
self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), block, move |this| {
3737
this.in_scope((region_scope, source_info), LintLevel::Inherited, block, move |this| {
3838
if targeted_by_break {
39-
// This is a `break`-able block (currently only `catch { ... }`)
39+
// This is a `break`-able block
4040
let exit_block = this.cfg.start_new_block();
4141
let block_exit = this.in_breakable_scope(
4242
None, exit_block, destination.clone(), |this| {

src/librustc_mir/hair/cx/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
292292
}
293293
}
294294

295-
hir::ExprBlock(ref blk) => ExprKind::Block { body: &blk },
295+
hir::ExprBlock(ref blk, _) => ExprKind::Block { body: &blk },
296296

297297
hir::ExprAssign(ref lhs, ref rhs) => {
298298
ExprKind::Assign {

src/librustc_mir/transform/add_validation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
141141
}
142142
// Check if this is an unsafe block, or an item
143143
match node {
144-
Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block), ..}) => {
144+
Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block, _), ..}) => {
145145
if block_is_unsafe(&*block) {
146146
// Found an unsafe block, we can bail out here.
147147
return true;

src/librustc_passes/diagnostics.rs

+39
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,44 @@ let result = loop { // ok!
259259
i += 1;
260260
};
261261
```
262+
"##,
263+
264+
E0695: r##"
265+
A `break` statement without a label appeared inside a labeled block.
266+
267+
Example of erroneous code:
268+
269+
```compile_fail,E0695
270+
# #![feature(label_break_value)]
271+
loop {
272+
'a: {
273+
break;
274+
}
275+
}
276+
```
277+
278+
Make sure to always label the `break`:
279+
280+
```
281+
# #![feature(label_break_value)]
282+
'l: loop {
283+
'a: {
284+
break 'l;
285+
}
286+
}
287+
```
288+
289+
Or if you want to `break` the labeled block:
290+
291+
```
292+
# #![feature(label_break_value)]
293+
loop {
294+
'a: {
295+
break 'a;
296+
}
297+
break;
298+
}
299+
```
262300
"##
263301
}
264302

@@ -271,4 +309,5 @@ register_diagnostics! {
271309
E0642, // patterns aren't allowed in methods without bodies
272310
E0666, // nested `impl Trait` is illegal
273311
E0667, // `impl Trait` in projections
312+
E0696, // `continue` pointing to a labeled block
274313
}

src/librustc_passes/loops.rs

+54-8
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc::session::Session;
1313

1414
use rustc::hir::map::Map;
1515
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
16-
use rustc::hir;
16+
use rustc::hir::{self, Destination};
1717
use syntax::ast;
1818
use syntax_pos::Span;
1919

@@ -39,6 +39,7 @@ enum Context {
3939
Normal,
4040
Loop(LoopKind),
4141
Closure,
42+
LabeledBlock,
4243
}
4344

4445
#[derive(Copy, Clone)]
@@ -84,7 +85,16 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
8485
hir::ExprClosure(.., b, _, _) => {
8586
self.with_context(Closure, |v| v.visit_nested_body(b));
8687
}
88+
hir::ExprBlock(ref b, Some(_label)) => {
89+
self.with_context(LabeledBlock, |v| v.visit_block(&b));
90+
}
8791
hir::ExprBreak(label, ref opt_expr) => {
92+
if self.require_label_in_labeled_block(e.span, &label, "break") {
93+
// If we emitted an error about an unlabeled break in a labeled
94+
// block, we don't need any further checking for this break any more
95+
return;
96+
}
97+
8898
let loop_id = match label.target_id.into() {
8999
Ok(loop_id) => loop_id,
90100
Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID,
@@ -94,6 +104,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
94104
},
95105
Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
96106
};
107+
97108
if loop_id != ast::DUMMY_NODE_ID {
98109
match self.hir_map.find(loop_id).unwrap() {
99110
hir::map::NodeBlock(_) => return,
@@ -113,13 +124,15 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
113124
})
114125
};
115126
match loop_kind {
116-
None | Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
127+
None |
128+
Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
117129
Some(kind) => {
118130
struct_span_err!(self.sess, e.span, E0571,
119131
"`break` with value from a `{}` loop",
120132
kind.name())
121133
.span_label(e.span,
122-
"can only break with a value inside `loop`")
134+
"can only break with a value inside \
135+
`loop` or breakable block")
123136
.span_suggestion(e.span,
124137
&format!("instead, use `break` on its own \
125138
without a value inside this `{}` loop",
@@ -130,13 +143,29 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
130143
}
131144
}
132145

133-
self.require_loop("break", e.span);
146+
self.require_break_cx("break", e.span);
134147
}
135148
hir::ExprAgain(label) => {
136-
if let Err(hir::LoopIdError::UnlabeledCfInWhileCondition) = label.target_id {
137-
self.emit_unlabled_cf_in_while_condition(e.span, "continue");
149+
self.require_label_in_labeled_block(e.span, &label, "continue");
150+
151+
match label.target_id {
152+
Ok(loop_id) => {
153+
if let hir::map::NodeBlock(block) = self.hir_map.find(loop_id).unwrap() {
154+
struct_span_err!(self.sess, e.span, E0696,
155+
"`continue` pointing to a labeled block")
156+
.span_label(e.span,
157+
"labeled blocks cannot be `continue`'d")
158+
.span_note(block.span,
159+
"labeled block the continue points to")
160+
.emit();
161+
}
162+
}
163+
Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
164+
self.emit_unlabled_cf_in_while_condition(e.span, "continue");
165+
}
166+
_ => {}
138167
}
139-
self.require_loop("continue", e.span)
168+
self.require_break_cx("continue", e.span)
140169
},
141170
_ => intravisit::walk_expr(self, e),
142171
}
@@ -153,8 +182,9 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
153182
self.cx = old_cx;
154183
}
155184

156-
fn require_loop(&self, name: &str, span: Span) {
185+
fn require_break_cx(&self, name: &str, span: Span) {
157186
match self.cx {
187+
LabeledBlock |
158188
Loop(_) => {}
159189
Closure => {
160190
struct_span_err!(self.sess, span, E0267, "`{}` inside of a closure", name)
@@ -169,6 +199,22 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
169199
}
170200
}
171201

202+
fn require_label_in_labeled_block(&mut self, span: Span, label: &Destination, cf_type: &str)
203+
-> bool
204+
{
205+
if self.cx == LabeledBlock {
206+
if label.label.is_none() {
207+
struct_span_err!(self.sess, span, E0695,
208+
"unlabeled `{}` inside of a labeled block", cf_type)
209+
.span_label(span,
210+
format!("`{}` statements that would diverge to or through \
211+
a labeled block need to bear a label", cf_type))
212+
.emit();
213+
return true;
214+
}
215+
}
216+
return false;
217+
}
172218
fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) {
173219
struct_span_err!(self.sess, span, E0590,
174220
"`break` or `continue` with no label in the condition of a `while` loop")

src/librustc_passes/rvalue_promotion.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
342342
let mut callee = &**callee;
343343
loop {
344344
callee = match callee.node {
345-
hir::ExprBlock(ref block) => match block.expr {
345+
hir::ExprBlock(ref block, _) => match block.expr {
346346
Some(ref tail) => &tail,
347347
None => break
348348
},
@@ -404,7 +404,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
404404
}
405405
}
406406

407-
hir::ExprBlock(_) |
407+
hir::ExprBlock(..) |
408408
hir::ExprIndex(..) |
409409
hir::ExprField(..) |
410410
hir::ExprArray(_) |

src/librustc_resolve/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3753,6 +3753,8 @@ impl<'a> Resolver<'a> {
37533753
self.ribs[ValueNS].pop();
37543754
}
37553755

3756+
ExprKind::Block(ref block, label) => self.resolve_labeled_block(label, block.id, block),
3757+
37563758
// Equivalent to `visit::walk_expr` + passing some context to children.
37573759
ExprKind::Field(ref subexpression, _) => {
37583760
self.resolve_expr(subexpression, Some(expr));

0 commit comments

Comments
 (0)