mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-29 12:36:35 +03:00
Auto merge of #50045 - est31:label_break_value, r=eddyb
Implement label break value (RFC 2046) Implement label-break-value (#48594).
This commit is contained in:
@@ -179,7 +179,7 @@ fn pats_all<'b, I: Iterator<Item=&'b P<hir::Pat>>>(&mut self,
|
||||
|
||||
fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex {
|
||||
match expr.node {
|
||||
hir::ExprBlock(ref blk) => {
|
||||
hir::ExprBlock(ref blk, _) => {
|
||||
let blk_exit = self.block(&blk, pred);
|
||||
self.add_ast_node(expr.hir_id.local_id, &[blk_exit])
|
||||
}
|
||||
|
||||
@@ -1015,7 +1015,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
|
||||
expression.span,
|
||||
expression.id)
|
||||
}
|
||||
ExprBlock(ref block) => visitor.visit_block(block),
|
||||
ExprBlock(ref block, ref opt_label) => {
|
||||
walk_list!(visitor, visit_label, opt_label);
|
||||
visitor.visit_block(block);
|
||||
}
|
||||
ExprAssign(ref left_hand_expression, ref right_hand_expression) => {
|
||||
visitor.visit_expr(right_hand_expression);
|
||||
visitor.visit_expr(left_hand_expression)
|
||||
|
||||
@@ -3048,7 +3048,7 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||
);
|
||||
block.expr = Some(this.wrap_in_try_constructor(
|
||||
"from_ok", tail, unstable_span));
|
||||
hir::ExprBlock(P(block))
|
||||
hir::ExprBlock(P(block), None)
|
||||
})
|
||||
}
|
||||
ExprKind::Match(ref expr, ref arms) => hir::ExprMatch(
|
||||
@@ -3100,7 +3100,11 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||
})
|
||||
})
|
||||
}
|
||||
ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, false)),
|
||||
ExprKind::Block(ref blk, 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)))
|
||||
}
|
||||
@@ -3843,7 +3847,7 @@ fn expr_match(
|
||||
}
|
||||
|
||||
fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr {
|
||||
self.expr(b.span, hir::ExprBlock(b), attrs)
|
||||
self.expr(b.span, hir::ExprBlock(b, None), attrs)
|
||||
}
|
||||
|
||||
fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec<hir::Expr>) -> P<hir::Expr> {
|
||||
|
||||
@@ -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
|
||||
@@ -1381,8 +1380,8 @@ pub enum Expr_ {
|
||||
/// This may also be a generator literal, indicated by the final boolean,
|
||||
/// in that case there is an GeneratorClause.
|
||||
ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, Option<GeneratorMovability>),
|
||||
/// A block (`{ ... }`)
|
||||
ExprBlock(P<Block>),
|
||||
/// A block (`'label: { ... }`)
|
||||
ExprBlock(P<Block>, Option<Label>),
|
||||
|
||||
/// An assignment (`a = foo()`)
|
||||
ExprAssign(P<Expr>, P<Expr>),
|
||||
|
||||
@@ -1047,7 +1047,7 @@ fn print_else(&mut self, els: Option<&hir::Expr>) -> io::Result<()> {
|
||||
self.print_else(e.as_ref().map(|e| &**e))
|
||||
}
|
||||
// "final else"
|
||||
hir::ExprBlock(ref b) => {
|
||||
hir::ExprBlock(ref b, _) => {
|
||||
self.cbox(indent_unit - 1)?;
|
||||
self.ibox(0)?;
|
||||
self.s.word(" else ")?;
|
||||
@@ -1377,7 +1377,11 @@ pub fn print_expr(&mut self, expr: &hir::Expr) -> io::Result<()> {
|
||||
// empty box to satisfy the close.
|
||||
self.ibox(0)?;
|
||||
}
|
||||
hir::ExprBlock(ref blk) => {
|
||||
hir::ExprBlock(ref blk, opt_label) => {
|
||||
if let Some(label) = opt_label {
|
||||
self.print_name(label.name)?;
|
||||
self.word_space(":")?;
|
||||
}
|
||||
// containing cbox, will be closed by print-block at }
|
||||
self.cbox(indent_unit)?;
|
||||
// head-box, will be closed by print-block after {
|
||||
@@ -1893,7 +1897,11 @@ fn print_arm(&mut self, arm: &hir::Arm) -> io::Result<()> {
|
||||
self.word_space("=>")?;
|
||||
|
||||
match arm.body.node {
|
||||
hir::ExprBlock(ref blk) => {
|
||||
hir::ExprBlock(ref blk, opt_label) => {
|
||||
if let Some(label) = opt_label {
|
||||
self.print_name(label.name)?;
|
||||
self.word_space(":")?;
|
||||
}
|
||||
// the block will close the pattern's ibox
|
||||
self.print_block_unclosed_indent(&blk, indent_unit)?;
|
||||
|
||||
@@ -2299,7 +2307,7 @@ fn expr_requires_semi_to_be_stmt(e: &hir::Expr) -> bool {
|
||||
match e.node {
|
||||
hir::ExprIf(..) |
|
||||
hir::ExprMatch(..) |
|
||||
hir::ExprBlock(_) |
|
||||
hir::ExprBlock(..) |
|
||||
hir::ExprWhile(..) |
|
||||
hir::ExprLoop(..) => false,
|
||||
_ => true,
|
||||
|
||||
@@ -589,7 +589,7 @@ fn hash_stable<W: StableHasherResult>(&self,
|
||||
ExprLoop(body, label, loop_src),
|
||||
ExprMatch(matchee, arms, match_src),
|
||||
ExprClosure(capture_clause, decl, body_id, span, gen),
|
||||
ExprBlock(blk),
|
||||
ExprBlock(blk, label),
|
||||
ExprAssign(lhs, rhs),
|
||||
ExprAssignOp(op, lhs, rhs),
|
||||
ExprField(owner, field_name),
|
||||
|
||||
@@ -499,7 +499,7 @@ pub fn walk_expr(&mut self, expr: &hir::Expr) {
|
||||
self.consume_expr(&rhs);
|
||||
}
|
||||
|
||||
hir::ExprBlock(ref blk) => {
|
||||
hir::ExprBlock(ref blk, _) => {
|
||||
self.walk_block(&blk);
|
||||
}
|
||||
|
||||
|
||||
@@ -1187,7 +1187,9 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
|
||||
succ
|
||||
}
|
||||
|
||||
hir::ExprBlock(ref blk) => {
|
||||
// Note that labels have been resolved, so we don't need to look
|
||||
// at the label ident
|
||||
hir::ExprBlock(ref blk, _) => {
|
||||
self.propagate_through_block(&blk, succ)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1247,7 +1247,7 @@ fn record_rvalue_scope_if_borrow_expr<'a, 'tcx>(
|
||||
hir::ExprCast(ref subexpr, _) => {
|
||||
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id)
|
||||
}
|
||||
hir::ExprBlock(ref block) => {
|
||||
hir::ExprBlock(ref block, _) => {
|
||||
if let Some(ref subexpr) = block.expr {
|
||||
record_rvalue_scope_if_borrow_expr(
|
||||
visitor, &subexpr, blk_id);
|
||||
|
||||
@@ -228,7 +228,7 @@ fn report_unsafe(&self, cx: &LateContext, span: Span, desc: &'static str) {
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
|
||||
if let hir::ExprBlock(ref blk) = e.node {
|
||||
if let hir::ExprBlock(ref blk, _) = e.node {
|
||||
// Don't warn about generated blocks, that'll just pollute the output.
|
||||
if blk.rules == hir::UnsafeBlock(hir::UserProvided) {
|
||||
self.report_unsafe(cx, blk.span, "usage of an `unsafe` block");
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -292,7 +292,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprBlock(ref blk) => ExprKind::Block { body: &blk },
|
||||
hir::ExprBlock(ref blk, _) => ExprKind::Block { body: &blk },
|
||||
|
||||
hir::ExprAssign(ref lhs, ref rhs) => {
|
||||
ExprKind::Assign {
|
||||
|
||||
@@ -141,7 +141,7 @@ fn fn_is_closure<'a>(fn_like: FnLikeNode<'a>) -> bool {
|
||||
}
|
||||
// Check if this is an unsafe block, or an item
|
||||
match node {
|
||||
Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block), ..}) => {
|
||||
Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block, _), ..}) => {
|
||||
if block_is_unsafe(&*block) {
|
||||
// Found an unsafe block, we can bail out here.
|
||||
return true;
|
||||
|
||||
@@ -259,6 +259,44 @@ fn foo() {}
|
||||
i += 1;
|
||||
};
|
||||
```
|
||||
"##,
|
||||
|
||||
E0695: r##"
|
||||
A `break` statement without a label appeared inside a labeled block.
|
||||
|
||||
Example of erroneous code:
|
||||
|
||||
```compile_fail,E0695
|
||||
# #![feature(label_break_value)]
|
||||
loop {
|
||||
'a: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Make sure to always label the `break`:
|
||||
|
||||
```
|
||||
# #![feature(label_break_value)]
|
||||
'l: loop {
|
||||
'a: {
|
||||
break 'l;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or if you want to `break` the labeled block:
|
||||
|
||||
```
|
||||
# #![feature(label_break_value)]
|
||||
loop {
|
||||
'a: {
|
||||
break 'a;
|
||||
}
|
||||
break;
|
||||
}
|
||||
```
|
||||
"##
|
||||
}
|
||||
|
||||
@@ -271,4 +309,5 @@ fn foo() {}
|
||||
E0642, // patterns aren't allowed in methods without bodies
|
||||
E0666, // nested `impl Trait` is illegal
|
||||
E0667, // `impl Trait` in projections
|
||||
E0696, // `continue` pointing to a labeled block
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
use rustc::hir::map::Map;
|
||||
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
|
||||
use rustc::hir;
|
||||
use rustc::hir::{self, Destination};
|
||||
use syntax::ast;
|
||||
use syntax_pos::Span;
|
||||
|
||||
@@ -39,6 +39,7 @@ enum Context {
|
||||
Normal,
|
||||
Loop(LoopKind),
|
||||
Closure,
|
||||
LabeledBlock,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -84,7 +85,16 @@ 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) => {
|
||||
if self.require_label_in_labeled_block(e.span, &label, "break") {
|
||||
// If we emitted an error about an unlabeled break in a labeled
|
||||
// block, we don't need any further checking for this break any more
|
||||
return;
|
||||
}
|
||||
|
||||
let loop_id = match label.target_id.into() {
|
||||
Ok(loop_id) => loop_id,
|
||||
Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID,
|
||||
@@ -94,6 +104,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,
|
||||
@@ -113,13 +124,15 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) {
|
||||
})
|
||||
};
|
||||
match loop_kind {
|
||||
None | Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
|
||||
None |
|
||||
Some(LoopKind::Loop(hir::LoopSource::Loop)) => (),
|
||||
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 +143,29 @@ 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_label_in_labeled_block(e.span, &label, "continue");
|
||||
|
||||
match label.target_id {
|
||||
Ok(loop_id) => {
|
||||
if let hir::map::NodeBlock(block) = self.hir_map.find(loop_id).unwrap() {
|
||||
struct_span_err!(self.sess, e.span, E0696,
|
||||
"`continue` pointing to a labeled block")
|
||||
.span_label(e.span,
|
||||
"labeled blocks cannot be `continue`'d")
|
||||
.span_note(block.span,
|
||||
"labeled block the continue points to")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
|
||||
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 +182,9 @@ fn with_context<F>(&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)
|
||||
@@ -169,6 +199,22 @@ fn require_loop(&self, name: &str, span: Span) {
|
||||
}
|
||||
}
|
||||
|
||||
fn require_label_in_labeled_block(&mut self, span: Span, label: &Destination, cf_type: &str)
|
||||
-> bool
|
||||
{
|
||||
if self.cx == LabeledBlock {
|
||||
if label.label.is_none() {
|
||||
struct_span_err!(self.sess, span, E0695,
|
||||
"unlabeled `{}` inside of a labeled block", cf_type)
|
||||
.span_label(span,
|
||||
format!("`{}` statements that would diverge to or through \
|
||||
a labeled block need to bear a label", cf_type))
|
||||
.emit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) {
|
||||
struct_span_err!(self.sess, span, E0590,
|
||||
"`break` or `continue` with no label in the condition of a `while` loop")
|
||||
|
||||
@@ -342,7 +342,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
|
||||
let mut callee = &**callee;
|
||||
loop {
|
||||
callee = match callee.node {
|
||||
hir::ExprBlock(ref block) => match block.expr {
|
||||
hir::ExprBlock(ref block, _) => match block.expr {
|
||||
Some(ref tail) => &tail,
|
||||
None => break
|
||||
},
|
||||
@@ -404,7 +404,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprBlock(_) |
|
||||
hir::ExprBlock(..) |
|
||||
hir::ExprIndex(..) |
|
||||
hir::ExprField(..) |
|
||||
hir::ExprArray(_) |
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -3536,7 +3536,7 @@ fn check_expr_with_expectation_and_needs(&self,
|
||||
|
||||
// Warn for non-block expressions with diverging children.
|
||||
match expr.node {
|
||||
hir::ExprBlock(_) |
|
||||
hir::ExprBlock(..) |
|
||||
hir::ExprLoop(..) | hir::ExprWhile(..) |
|
||||
hir::ExprIf(..) | hir::ExprMatch(..) => {}
|
||||
|
||||
@@ -3912,7 +3912,7 @@ fn check_expr_kind(&self,
|
||||
hir::ExprClosure(capture, ref decl, body_id, _, gen) => {
|
||||
self.check_expr_closure(expr, capture, &decl, body_id, gen, expected)
|
||||
}
|
||||
hir::ExprBlock(ref body) => {
|
||||
hir::ExprBlock(ref body, _) => {
|
||||
self.check_block_with_expected(&body, expected)
|
||||
}
|
||||
hir::ExprCall(ref callee, ref args) => {
|
||||
@@ -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:
|
||||
|
||||
@@ -934,7 +934,7 @@ impl Expr {
|
||||
/// Whether this expression would be valid somewhere that expects a value, for example, an `if`
|
||||
/// condition.
|
||||
pub fn returns(&self) -> bool {
|
||||
if let ExprKind::Block(ref block) = self.node {
|
||||
if let ExprKind::Block(ref block, _) = self.node {
|
||||
match block.stmts.last().map(|last_stmt| &last_stmt.node) {
|
||||
// implicit return
|
||||
Some(&StmtKind::Expr(_)) => true,
|
||||
@@ -1121,8 +1121,8 @@ pub enum ExprKind {
|
||||
///
|
||||
/// The final span is the span of the argument block `|...|`
|
||||
Closure(CaptureBy, Movability, P<FnDecl>, P<Expr>, Span),
|
||||
/// A block (`{ ... }`)
|
||||
Block(P<Block>),
|
||||
/// A block (`'label: { ... }`)
|
||||
Block(P<Block>, Option<Label>),
|
||||
/// A catch block (`catch { ... }`)
|
||||
Catch(P<Block>),
|
||||
|
||||
|
||||
@@ -668,7 +668,7 @@ fn expr_method_call(&self, span: Span,
|
||||
self.expr(span, ast::ExprKind::MethodCall(segment, args))
|
||||
}
|
||||
fn expr_block(&self, b: P<ast::Block>) -> P<ast::Expr> {
|
||||
self.expr(b.span, ast::ExprKind::Block(b))
|
||||
self.expr(b.span, ast::ExprKind::Block(b, None))
|
||||
}
|
||||
fn field_imm(&self, span: Span, ident: Ident, e: P<ast::Expr>) -> ast::Field {
|
||||
ast::Field {
|
||||
|
||||
@@ -466,6 +466,9 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
|
||||
|
||||
// inconsistent bounds in where clauses
|
||||
(active, trivial_bounds, "1.28.0", Some(48214), None),
|
||||
|
||||
// 'a: { break 'a; }
|
||||
(active, label_break_value, "1.28.0", Some(48594), None),
|
||||
);
|
||||
|
||||
declare_features! (
|
||||
@@ -1696,6 +1699,12 @@ fn visit_expr(&mut self, e: &'a ast::Expr) {
|
||||
"multiple patterns in `if let` and `while let` are unstable");
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Block(_, opt_label) => {
|
||||
if let Some(label) = opt_label {
|
||||
gate_feature_post!(&self, label_break_value, label.ident.span,
|
||||
"labels on blocks are unstable");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
visit::walk_expr(self, e);
|
||||
|
||||
@@ -1256,7 +1256,10 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
|
||||
folder.fold_expr(body),
|
||||
folder.new_span(span))
|
||||
}
|
||||
ExprKind::Block(blk) => ExprKind::Block(folder.fold_block(blk)),
|
||||
ExprKind::Block(blk, opt_label) => {
|
||||
ExprKind::Block(folder.fold_block(blk),
|
||||
opt_label.map(|label| folder.fold_label(label)))
|
||||
}
|
||||
ExprKind::Assign(el, er) => {
|
||||
ExprKind::Assign(folder.fold_expr(el), folder.fold_expr(er))
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
||||
ast::ExprKind::If(..) |
|
||||
ast::ExprKind::IfLet(..) |
|
||||
ast::ExprKind::Match(..) |
|
||||
ast::ExprKind::Block(_) |
|
||||
ast::ExprKind::Block(..) |
|
||||
ast::ExprKind::While(..) |
|
||||
ast::ExprKind::WhileLet(..) |
|
||||
ast::ExprKind::Loop(..) |
|
||||
|
||||
@@ -128,7 +128,7 @@ macro_rules! maybe_whole_expr {
|
||||
token::NtBlock(ref block) => {
|
||||
$p.bump();
|
||||
let span = $p.span;
|
||||
let kind = ExprKind::Block((*block).clone());
|
||||
let kind = ExprKind::Block((*block).clone(), None);
|
||||
return Ok($p.mk_expr(span, kind, ThinVec::new()));
|
||||
}
|
||||
_ => {},
|
||||
@@ -2244,7 +2244,7 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
|
||||
};
|
||||
}
|
||||
token::OpenDelim(token::Brace) => {
|
||||
return self.parse_block_expr(lo, BlockCheckMode::Default, attrs);
|
||||
return self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs);
|
||||
}
|
||||
token::BinOp(token::Or) | token::OrOr => {
|
||||
return self.parse_lambda_expr(attrs);
|
||||
@@ -2318,7 +2318,13 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
|
||||
if self.eat_keyword(keywords::Loop) {
|
||||
return self.parse_loop_expr(Some(label), lo, attrs)
|
||||
}
|
||||
let msg = "expected `while`, `for`, or `loop` after a label";
|
||||
if self.token == token::OpenDelim(token::Brace) {
|
||||
return self.parse_block_expr(Some(label),
|
||||
lo,
|
||||
BlockCheckMode::Default,
|
||||
attrs);
|
||||
}
|
||||
let msg = "expected `while`, `for`, `loop` or `{` after a label";
|
||||
let mut err = self.fatal(msg);
|
||||
err.span_label(self.span, msg);
|
||||
return Err(err);
|
||||
@@ -2338,6 +2344,7 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
|
||||
}
|
||||
if self.eat_keyword(keywords::Unsafe) {
|
||||
return self.parse_block_expr(
|
||||
None,
|
||||
lo,
|
||||
BlockCheckMode::Unsafe(ast::UserProvided),
|
||||
attrs);
|
||||
@@ -2502,7 +2509,8 @@ fn parse_or_use_outer_attributes(&mut self,
|
||||
}
|
||||
|
||||
/// Parse a block or unsafe block
|
||||
pub fn parse_block_expr(&mut self, lo: Span, blk_mode: BlockCheckMode,
|
||||
pub fn parse_block_expr(&mut self, opt_label: Option<Label>,
|
||||
lo: Span, blk_mode: BlockCheckMode,
|
||||
outer_attrs: ThinVec<Attribute>)
|
||||
-> PResult<'a, P<Expr>> {
|
||||
self.expect(&token::OpenDelim(token::Brace))?;
|
||||
@@ -2511,7 +2519,7 @@ pub fn parse_block_expr(&mut self, lo: Span, blk_mode: BlockCheckMode,
|
||||
attrs.extend(self.parse_inner_attributes()?);
|
||||
|
||||
let blk = self.parse_block_tail(lo, blk_mode)?;
|
||||
return Ok(self.mk_expr(blk.span, ExprKind::Block(blk), attrs));
|
||||
return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs));
|
||||
}
|
||||
|
||||
/// parse a.b or a(13) or a[4] or just a
|
||||
@@ -3261,7 +3269,7 @@ pub fn parse_lambda_expr(&mut self,
|
||||
// If an explicit return type is given, require a
|
||||
// block to appear (RFC 968).
|
||||
let body_lo = self.span;
|
||||
self.parse_block_expr(body_lo, BlockCheckMode::Default, ThinVec::new())?
|
||||
self.parse_block_expr(None, body_lo, BlockCheckMode::Default, ThinVec::new())?
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3277,7 +3285,7 @@ pub fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
|
||||
return self.parse_if_expr(ThinVec::new());
|
||||
} else {
|
||||
let blk = self.parse_block()?;
|
||||
return Ok(self.mk_expr(blk.span, ExprKind::Block(blk), ThinVec::new()));
|
||||
return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), ThinVec::new()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1792,7 +1792,7 @@ fn print_else(&mut self, els: Option<&ast::Expr>) -> io::Result<()> {
|
||||
self.print_else(e.as_ref().map(|e| &**e))
|
||||
}
|
||||
// "final else"
|
||||
ast::ExprKind::Block(ref b) => {
|
||||
ast::ExprKind::Block(ref b, _) => {
|
||||
self.cbox(INDENT_UNIT - 1)?;
|
||||
self.ibox(0)?;
|
||||
self.s.word(" else ")?;
|
||||
@@ -2182,7 +2182,11 @@ fn print_expr_outer_attr_style(&mut self,
|
||||
// empty box to satisfy the close.
|
||||
self.ibox(0)?;
|
||||
}
|
||||
ast::ExprKind::Block(ref blk) => {
|
||||
ast::ExprKind::Block(ref blk, opt_label) => {
|
||||
if let Some(label) = opt_label {
|
||||
self.print_ident(label.ident)?;
|
||||
self.word_space(":")?;
|
||||
}
|
||||
// containing cbox, will be closed by print-block at }
|
||||
self.cbox(INDENT_UNIT)?;
|
||||
// head-box, will be closed by print-block after {
|
||||
@@ -2695,7 +2699,12 @@ fn print_arm(&mut self, arm: &ast::Arm) -> io::Result<()> {
|
||||
self.word_space("=>")?;
|
||||
|
||||
match arm.body.node {
|
||||
ast::ExprKind::Block(ref blk) => {
|
||||
ast::ExprKind::Block(ref blk, opt_label) => {
|
||||
if let Some(label) = opt_label {
|
||||
self.print_ident(label.ident)?;
|
||||
self.word_space(":")?;
|
||||
}
|
||||
|
||||
// the block will close the pattern's ibox
|
||||
self.print_block_unclosed_indent(blk, INDENT_UNIT)?;
|
||||
|
||||
|
||||
@@ -736,7 +736,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
||||
expression.span,
|
||||
expression.id)
|
||||
}
|
||||
ExprKind::Block(ref block) => visitor.visit_block(block),
|
||||
ExprKind::Block(ref block, ref opt_label) => {
|
||||
walk_list!(visitor, visit_label, opt_label);
|
||||
visitor.visit_block(block);
|
||||
}
|
||||
ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => {
|
||||
visitor.visit_expr(left_hand_expression);
|
||||
visitor.visit_expr(right_hand_expression);
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(label_break_value)]
|
||||
|
||||
// Test control flow to follow label_break_value semantics
|
||||
fn label_break(a: bool, b: bool) -> u32 {
|
||||
let mut v = 0;
|
||||
'b: {
|
||||
v = 1;
|
||||
if a {
|
||||
break 'b;
|
||||
}
|
||||
v = 2;
|
||||
if b {
|
||||
break 'b;
|
||||
}
|
||||
v = 3;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// Test that values can be returned
|
||||
fn break_value(a: bool, b: bool) -> u32 {
|
||||
let result = 'block: {
|
||||
if a { break 'block 1; }
|
||||
if b { break 'block 2; }
|
||||
3
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
// Test nesting of labeled blocks
|
||||
// here we only check that it compiles
|
||||
fn label_break_nested() {
|
||||
'b: {
|
||||
println!("hi");
|
||||
if false {
|
||||
break 'b;
|
||||
}
|
||||
'c: {
|
||||
if false {
|
||||
break 'b;
|
||||
}
|
||||
break 'c;
|
||||
}
|
||||
println!("hello");
|
||||
if true {
|
||||
break 'b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for mixing labeled blocks with loop constructs
|
||||
// This function should be the identity function
|
||||
fn label_break_mixed(v: u32) -> u32 {
|
||||
let mut r = 0;
|
||||
'b: {
|
||||
// Unlabeled break still works
|
||||
// (only crossing boundaries is an error)
|
||||
loop {
|
||||
break;
|
||||
}
|
||||
if v == 0 {
|
||||
break 'b;
|
||||
}
|
||||
// Labeled breaking an inner loop still works
|
||||
'c: loop {
|
||||
if r == 1 {
|
||||
break 'c;
|
||||
}
|
||||
r += 1;
|
||||
}
|
||||
assert_eq!(r, 1);
|
||||
if v == 1 {
|
||||
break 'b;
|
||||
}
|
||||
// Labeled breaking an outer loop still works
|
||||
'd: loop {
|
||||
'e: {
|
||||
if v == r {
|
||||
break 'b;
|
||||
}
|
||||
if r == 5 {
|
||||
break 'd;
|
||||
}
|
||||
r += 1;
|
||||
}
|
||||
}
|
||||
assert_eq!(r, 5);
|
||||
assert!(v > r);
|
||||
// Here we test return from inside a labeled block
|
||||
return v;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!(label_break(true, false), 1);
|
||||
assert_eq!(label_break(false, true), 2);
|
||||
assert_eq!(label_break(false, false), 3);
|
||||
|
||||
assert_eq!(break_value(true, false), 1);
|
||||
assert_eq!(break_value(false, true), 2);
|
||||
assert_eq!(break_value(false, false), 3);
|
||||
|
||||
assert_eq!(label_break_mixed(0), 0);
|
||||
assert_eq!(label_break_mixed(1), 1);
|
||||
assert_eq!(label_break_mixed(2), 2);
|
||||
assert_eq!(label_break_mixed(3), 3);
|
||||
assert_eq!(label_break_mixed(4), 4);
|
||||
assert_eq!(label_break_mixed(5), 5);
|
||||
assert_eq!(label_break_mixed(6), 6);
|
||||
|
||||
// FIXME: ensure that labeled blocks work if produced by macros and in match arms
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub fn main() {
|
||||
'a: { //~ ERROR labels on blocks are unstable
|
||||
break 'a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
error[E0658]: labels on blocks are unstable (see issue #48594)
|
||||
--> $DIR/feature-gate-label_break_value.rs:12:5
|
||||
|
|
||||
LL | 'a: { //~ ERROR labels on blocks are unstable
|
||||
| ^^
|
||||
|
|
||||
= help: add #![feature(label_break_value)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(label_break_value)]
|
||||
|
||||
// Simple continue pointing to an unlabeled break should yield in an error
|
||||
fn continue_simple() {
|
||||
'b: {
|
||||
continue; //~ ERROR unlabeled `continue` inside of a labeled block
|
||||
}
|
||||
}
|
||||
|
||||
// Labeled continue pointing to an unlabeled break should yield in an error
|
||||
fn continue_labeled() {
|
||||
'b: {
|
||||
continue 'b; //~ ERROR `continue` pointing to a labeled block
|
||||
}
|
||||
}
|
||||
|
||||
// Simple continue that would cross a labeled block should yield in an error
|
||||
fn continue_crossing() {
|
||||
loop {
|
||||
'b: {
|
||||
continue; //~ ERROR unlabeled `continue` inside of a labeled block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {}
|
||||
@@ -0,0 +1,30 @@
|
||||
error[E0695]: unlabeled `continue` inside of a labeled block
|
||||
--> $DIR/label_break_value_continue.rs:16:9
|
||||
|
|
||||
LL | continue; //~ ERROR unlabeled `continue` inside of a labeled block
|
||||
| ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label
|
||||
|
||||
error[E0696]: `continue` pointing to a labeled block
|
||||
--> $DIR/label_break_value_continue.rs:23:9
|
||||
|
|
||||
LL | continue 'b; //~ ERROR `continue` pointing to a labeled block
|
||||
| ^^^^^^^^^^^ labeled blocks cannot be `continue`'d
|
||||
|
|
||||
note: labeled block the continue points to
|
||||
--> $DIR/label_break_value_continue.rs:22:5
|
||||
|
|
||||
LL | / 'b: {
|
||||
LL | | continue 'b; //~ ERROR `continue` pointing to a labeled block
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
||||
error[E0695]: unlabeled `continue` inside of a labeled block
|
||||
--> $DIR/label_break_value_continue.rs:31:13
|
||||
|
|
||||
LL | continue; //~ ERROR unlabeled `continue` inside of a labeled block
|
||||
| ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors occurred: E0695, E0696.
|
||||
For more information about an error, try `rustc --explain E0695`.
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(label_break_value)]
|
||||
|
||||
// These are forbidden occurences of label-break-value
|
||||
|
||||
fn labeled_unsafe() {
|
||||
unsafe 'b: {} //~ ERROR expected one of `extern`, `fn`, or `{`
|
||||
}
|
||||
|
||||
fn labeled_if() {
|
||||
if true 'b: {} //~ ERROR expected `{`, found `'b`
|
||||
}
|
||||
|
||||
fn labeled_else() {
|
||||
if true {} else 'b: {} //~ ERROR expected `{`, found `'b`
|
||||
}
|
||||
|
||||
fn labeled_match() {
|
||||
match false 'b: {} //~ ERROR expected one of `.`, `?`, `{`, or an operator
|
||||
}
|
||||
|
||||
pub fn main() {}
|
||||
@@ -0,0 +1,31 @@
|
||||
error: expected one of `extern`, `fn`, or `{`, found `'b`
|
||||
--> $DIR/label_break_value_illegal_uses.rs:16:12
|
||||
|
|
||||
LL | unsafe 'b: {} //~ ERROR expected one of `extern`, `fn`, or `{`
|
||||
| ^^ expected one of `extern`, `fn`, or `{` here
|
||||
|
||||
error: expected `{`, found `'b`
|
||||
--> $DIR/label_break_value_illegal_uses.rs:20:13
|
||||
|
|
||||
LL | if true 'b: {} //~ ERROR expected `{`, found `'b`
|
||||
| -- ^^----
|
||||
| | |
|
||||
| | help: try placing this code inside a block: `{ 'b: { } }`
|
||||
| this `if` statement has a condition, but no block
|
||||
|
||||
error: expected `{`, found `'b`
|
||||
--> $DIR/label_break_value_illegal_uses.rs:24:21
|
||||
|
|
||||
LL | if true {} else 'b: {} //~ ERROR expected `{`, found `'b`
|
||||
| ^^----
|
||||
| |
|
||||
| help: try placing this code inside a block: `{ 'b: { } }`
|
||||
|
||||
error: expected one of `.`, `?`, `{`, or an operator, found `'b`
|
||||
--> $DIR/label_break_value_illegal_uses.rs:28:17
|
||||
|
|
||||
LL | match false 'b: {} //~ ERROR expected one of `.`, `?`, `{`, or an operator
|
||||
| ^^ expected one of `.`, `?`, `{`, or an operator here
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(label_break_value)]
|
||||
|
||||
// Simple unlabeled break should yield in an error
|
||||
fn unlabeled_break_simple() {
|
||||
'b: {
|
||||
break; //~ ERROR unlabeled `break` inside of a labeled block
|
||||
}
|
||||
}
|
||||
|
||||
// Unlabeled break that would cross a labeled block should yield in an error
|
||||
fn unlabeled_break_crossing() {
|
||||
loop {
|
||||
'b: {
|
||||
break; //~ ERROR unlabeled `break` inside of a labeled block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {}
|
||||
@@ -0,0 +1,15 @@
|
||||
error[E0695]: unlabeled `break` inside of a labeled block
|
||||
--> $DIR/label_break_value_unlabeled_break.rs:16:9
|
||||
|
|
||||
LL | break; //~ ERROR unlabeled `break` inside of a labeled block
|
||||
| ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
|
||||
|
||||
error[E0695]: unlabeled `break` inside of a labeled block
|
||||
--> $DIR/label_break_value_unlabeled_break.rs:24:13
|
||||
|
|
||||
LL | break; //~ ERROR unlabeled `break` inside of a labeled block
|
||||
| ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0695`.
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user