Skip to content

Commit 5665980

Browse files
committed
Make the compiler support the label-break-value feature
No error checking or feature gating yet
1 parent ebca9c6 commit 5665980

File tree

7 files changed

+32
-13
lines changed

7 files changed

+32
-13
lines changed

src/librustc/hir/lowering.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3101,7 +3101,9 @@ impl<'a> LoweringContext<'a> {
31013101
})
31023102
}
31033103
ExprKind::Block(ref blk, opt_label) => {
3104-
hir::ExprBlock(self.lower_block(blk, false), self.lower_label(opt_label))
3104+
hir::ExprBlock(self.lower_block(blk,
3105+
opt_label.is_some()),
3106+
self.lower_label(opt_label))
31053107
}
31063108
ExprKind::Assign(ref el, ref er) => {
31073109
hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))

src/librustc/hir/mod.rs

Lines changed: 2 additions & 3 deletions
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

src/librustc_mir/build/block.rs

Lines changed: 1 addition & 1 deletion
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_passes/loops.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use syntax_pos::Span;
2121
enum LoopKind {
2222
Loop(hir::LoopSource),
2323
WhileLoop,
24+
Block,
2425
}
2526

2627
impl LoopKind {
@@ -30,6 +31,7 @@ impl LoopKind {
3031
LoopKind::Loop(hir::LoopSource::WhileLet) => "while let",
3132
LoopKind::Loop(hir::LoopSource::ForLoop) => "for",
3233
LoopKind::WhileLoop => "while",
34+
LoopKind::Block => "block",
3335
}
3436
}
3537
}
@@ -39,6 +41,7 @@ enum Context {
3941
Normal,
4042
Loop(LoopKind),
4143
Closure,
44+
LabeledBlock,
4245
}
4346

4447
#[derive(Copy, Clone)]
@@ -84,6 +87,9 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
8487
hir::ExprClosure(.., b, _, _) => {
8588
self.with_context(Closure, |v| v.visit_nested_body(b));
8689
}
90+
hir::ExprBlock(ref b, Some(_label)) => {
91+
self.with_context(LabeledBlock, |v| v.visit_block(&b));
92+
}
8793
hir::ExprBreak(label, ref opt_expr) => {
8894
let loop_id = match label.target_id.into() {
8995
Ok(loop_id) => loop_id,
@@ -94,32 +100,41 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
94100
},
95101
Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
96102
};
103+
97104
if loop_id != ast::DUMMY_NODE_ID {
98105
match self.hir_map.find(loop_id).unwrap() {
99106
hir::map::NodeBlock(_) => return,
100107
_=> (),
101108
}
102109
}
103110

111+
if self.cx == LabeledBlock {
112+
return;
113+
}
114+
104115
if opt_expr.is_some() {
105116
let loop_kind = if loop_id == ast::DUMMY_NODE_ID {
106117
None
107118
} else {
108119
Some(match self.hir_map.expect_expr(loop_id).node {
109120
hir::ExprWhile(..) => LoopKind::WhileLoop,
110121
hir::ExprLoop(_, _, source) => LoopKind::Loop(source),
122+
hir::ExprBlock(..) => LoopKind::Block,
111123
ref r => span_bug!(e.span,
112124
"break label resolved to a non-loop: {:?}", r),
113125
})
114126
};
115127
match loop_kind {
116-
None | Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
128+
None |
129+
Some(LoopKind::Loop(hir::LoopSource::Loop)) |
130+
Some(LoopKind::Block) => (),
117131
Some(kind) => {
118132
struct_span_err!(self.sess, e.span, E0571,
119133
"`break` with value from a `{}` loop",
120134
kind.name())
121135
.span_label(e.span,
122-
"can only break with a value inside `loop`")
136+
"can only break with a value inside \
137+
`loop` or breakable block")
123138
.span_suggestion(e.span,
124139
&format!("instead, use `break` on its own \
125140
without a value inside this `{}` loop",
@@ -130,13 +145,13 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
130145
}
131146
}
132147

133-
self.require_loop("break", e.span);
148+
self.require_break_cx("break", e.span);
134149
}
135150
hir::ExprAgain(label) => {
136151
if let Err(hir::LoopIdError::UnlabeledCfInWhileCondition) = label.target_id {
137152
self.emit_unlabled_cf_in_while_condition(e.span, "continue");
138153
}
139-
self.require_loop("continue", e.span)
154+
self.require_break_cx("continue", e.span)
140155
},
141156
_ => intravisit::walk_expr(self, e),
142157
}
@@ -153,8 +168,9 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
153168
self.cx = old_cx;
154169
}
155170

156-
fn require_loop(&self, name: &str, span: Span) {
171+
fn require_break_cx(&self, name: &str, span: Span) {
157172
match self.cx {
173+
LabeledBlock |
158174
Loop(_) => {}
159175
Closure => {
160176
struct_span_err!(self.sess, span, E0267, "`{}` inside of a closure", name)

src/librustc_resolve/lib.rs

Lines changed: 2 additions & 0 deletions
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));

src/librustc_typeck/check/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4326,8 +4326,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
43264326
};
43274327

43284328
// In some cases, blocks have just one exit, but other blocks
4329-
// can be targeted by multiple breaks. This cannot happen in
4330-
// normal Rust syntax today, but it can happen when we desugar
4329+
// can be targeted by multiple breaks. This can happen both
4330+
// with labeled blocks as well as when we desugar
43314331
// a `do catch { ... }` expression.
43324332
//
43334333
// Example 1:

src/test/ui/loop-break-value-no-repeat.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0571]: `break` with value from a `for` loop
22
--> $DIR/loop-break-value-no-repeat.rs:22:9
33
|
44
LL | break 22 //~ ERROR `break` with value from a `for` loop
5-
| ^^^^^^^^ can only break with a value inside `loop`
5+
| ^^^^^^^^ can only break with a value inside `loop` or breakable block
66
help: instead, use `break` on its own without a value inside this `for` loop
77
|
88
LL | break //~ ERROR `break` with value from a `for` loop

0 commit comments

Comments
 (0)