Make the compiler support the label-break-value feature
No error checking or feature gating yet
This commit is contained in:
parent
ebca9c6ab9
commit
5665980ad8
7 changed files with 32 additions and 13 deletions
|
@ -3101,7 +3101,9 @@ impl<'a> LoweringContext<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ExprKind::Block(ref blk, opt_label) => {
|
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) => {
|
ExprKind::Assign(ref el, ref er) => {
|
||||||
hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
|
hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
|
||||||
|
|
|
@ -778,9 +778,8 @@ pub struct Block {
|
||||||
pub rules: BlockCheckMode,
|
pub rules: BlockCheckMode,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
/// If true, then there may exist `break 'a` values that aim to
|
/// 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
|
/// break out of this block early.
|
||||||
/// currently permitted in Rust itself, but it is generated as
|
/// Used by `'label {}` blocks and by `catch` statements.
|
||||||
/// part of `catch` statements.
|
|
||||||
pub targeted_by_break: bool,
|
pub targeted_by_break: bool,
|
||||||
/// If true, don't emit return value type errors as the parser had
|
/// 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
|
/// to recover from a parse error so this block will not have an
|
||||||
|
|
|
@ -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| {
|
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| {
|
this.in_scope((region_scope, source_info), LintLevel::Inherited, block, move |this| {
|
||||||
if targeted_by_break {
|
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 exit_block = this.cfg.start_new_block();
|
||||||
let block_exit = this.in_breakable_scope(
|
let block_exit = this.in_breakable_scope(
|
||||||
None, exit_block, destination.clone(), |this| {
|
None, exit_block, destination.clone(), |this| {
|
||||||
|
|
|
@ -21,6 +21,7 @@ use syntax_pos::Span;
|
||||||
enum LoopKind {
|
enum LoopKind {
|
||||||
Loop(hir::LoopSource),
|
Loop(hir::LoopSource),
|
||||||
WhileLoop,
|
WhileLoop,
|
||||||
|
Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoopKind {
|
impl LoopKind {
|
||||||
|
@ -30,6 +31,7 @@ impl LoopKind {
|
||||||
LoopKind::Loop(hir::LoopSource::WhileLet) => "while let",
|
LoopKind::Loop(hir::LoopSource::WhileLet) => "while let",
|
||||||
LoopKind::Loop(hir::LoopSource::ForLoop) => "for",
|
LoopKind::Loop(hir::LoopSource::ForLoop) => "for",
|
||||||
LoopKind::WhileLoop => "while",
|
LoopKind::WhileLoop => "while",
|
||||||
|
LoopKind::Block => "block",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +41,7 @@ enum Context {
|
||||||
Normal,
|
Normal,
|
||||||
Loop(LoopKind),
|
Loop(LoopKind),
|
||||||
Closure,
|
Closure,
|
||||||
|
LabeledBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -84,6 +87,9 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
|
||||||
hir::ExprClosure(.., b, _, _) => {
|
hir::ExprClosure(.., b, _, _) => {
|
||||||
self.with_context(Closure, |v| v.visit_nested_body(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) => {
|
hir::ExprBreak(label, ref opt_expr) => {
|
||||||
let loop_id = match label.target_id.into() {
|
let loop_id = match label.target_id.into() {
|
||||||
Ok(loop_id) => loop_id,
|
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,
|
Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
if loop_id != ast::DUMMY_NODE_ID {
|
if loop_id != ast::DUMMY_NODE_ID {
|
||||||
match self.hir_map.find(loop_id).unwrap() {
|
match self.hir_map.find(loop_id).unwrap() {
|
||||||
hir::map::NodeBlock(_) => return,
|
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() {
|
if opt_expr.is_some() {
|
||||||
let loop_kind = if loop_id == ast::DUMMY_NODE_ID {
|
let loop_kind = if loop_id == ast::DUMMY_NODE_ID {
|
||||||
None
|
None
|
||||||
|
@ -108,18 +119,22 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
|
||||||
Some(match self.hir_map.expect_expr(loop_id).node {
|
Some(match self.hir_map.expect_expr(loop_id).node {
|
||||||
hir::ExprWhile(..) => LoopKind::WhileLoop,
|
hir::ExprWhile(..) => LoopKind::WhileLoop,
|
||||||
hir::ExprLoop(_, _, source) => LoopKind::Loop(source),
|
hir::ExprLoop(_, _, source) => LoopKind::Loop(source),
|
||||||
|
hir::ExprBlock(..) => LoopKind::Block,
|
||||||
ref r => span_bug!(e.span,
|
ref r => span_bug!(e.span,
|
||||||
"break label resolved to a non-loop: {:?}", r),
|
"break label resolved to a non-loop: {:?}", r),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
match loop_kind {
|
match loop_kind {
|
||||||
None | Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
|
None |
|
||||||
|
Some(LoopKind::Loop(hir::LoopSource::Loop)) |
|
||||||
|
Some(LoopKind::Block) => (),
|
||||||
Some(kind) => {
|
Some(kind) => {
|
||||||
struct_span_err!(self.sess, e.span, E0571,
|
struct_span_err!(self.sess, e.span, E0571,
|
||||||
"`break` with value from a `{}` loop",
|
"`break` with value from a `{}` loop",
|
||||||
kind.name())
|
kind.name())
|
||||||
.span_label(e.span,
|
.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,
|
.span_suggestion(e.span,
|
||||||
&format!("instead, use `break` on its own \
|
&format!("instead, use `break` on its own \
|
||||||
without a value inside this `{}` loop",
|
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) => {
|
hir::ExprAgain(label) => {
|
||||||
if let Err(hir::LoopIdError::UnlabeledCfInWhileCondition) = label.target_id {
|
if let Err(hir::LoopIdError::UnlabeledCfInWhileCondition) = label.target_id {
|
||||||
self.emit_unlabled_cf_in_while_condition(e.span, "continue");
|
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),
|
_ => intravisit::walk_expr(self, e),
|
||||||
}
|
}
|
||||||
|
@ -153,8 +168,9 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
|
||||||
self.cx = old_cx;
|
self.cx = old_cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_loop(&self, name: &str, span: Span) {
|
fn require_break_cx(&self, name: &str, span: Span) {
|
||||||
match self.cx {
|
match self.cx {
|
||||||
|
LabeledBlock |
|
||||||
Loop(_) => {}
|
Loop(_) => {}
|
||||||
Closure => {
|
Closure => {
|
||||||
struct_span_err!(self.sess, span, E0267, "`{}` inside of a closure", name)
|
struct_span_err!(self.sess, span, E0267, "`{}` inside of a closure", name)
|
||||||
|
|
|
@ -3753,6 +3753,8 @@ impl<'a> Resolver<'a> {
|
||||||
self.ribs[ValueNS].pop();
|
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.
|
// Equivalent to `visit::walk_expr` + passing some context to children.
|
||||||
ExprKind::Field(ref subexpression, _) => {
|
ExprKind::Field(ref subexpression, _) => {
|
||||||
self.resolve_expr(subexpression, Some(expr));
|
self.resolve_expr(subexpression, Some(expr));
|
||||||
|
|
|
@ -4326,8 +4326,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// In some cases, blocks have just one exit, but other blocks
|
// In some cases, blocks have just one exit, but other blocks
|
||||||
// can be targeted by multiple breaks. This cannot happen in
|
// can be targeted by multiple breaks. This can happen both
|
||||||
// normal Rust syntax today, but it can happen when we desugar
|
// with labeled blocks as well as when we desugar
|
||||||
// a `do catch { ... }` expression.
|
// a `do catch { ... }` expression.
|
||||||
//
|
//
|
||||||
// Example 1:
|
// Example 1:
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0571]: `break` with value from a `for` loop
|
||||||
--> $DIR/loop-break-value-no-repeat.rs:22:9
|
--> $DIR/loop-break-value-no-repeat.rs:22:9
|
||||||
|
|
|
|
||||||
LL | break 22 //~ ERROR `break` with value from a `for` loop
|
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
|
help: instead, use `break` on its own without a value inside this `for` loop
|
||||||
|
|
|
|
||||||
LL | break //~ ERROR `break` with value from a `for` loop
|
LL | break //~ ERROR `break` with value from a `for` loop
|
||||||
|
|
Loading…
Add table
Reference in a new issue