Skip to content

Commit 31e3944

Browse files
committed
Stabilize #![feature(label_break_value)]
# Stabilization proposal The feature was implemented in #50045 by est31 and has been in nightly since 2018-05-16 (over 4 years now). There are [no open issues][issue-label] other than the tracking issue. There is a strong consensus that `break` is the right keyword and we should not use `return`. There have been several concerns raised about this feature on the tracking issue (other than the one about tests, which has been fixed, and an interaction with try blocks, which has been fixed). 1. nrc's original comment about cost-benefit analysis: #48594 (comment) 2. joshtriplett's comments about seeing use cases: #48594 (comment) 3. withoutboats's comments that Rust does not need more control flow constructs: #48594 (comment) Many different examples of code that's simpler using this feature have been provided: - A lexer by rpjohnst which must repeat code without label-break-value: #48594 (comment) - A snippet by SergioBenitez which avoids using a new function and adding several new return points to a function: #48594 (comment). This particular case would also work if `try` blocks were stabilized (at the cost of making the code harder to optimize). - Several examples by JohnBSmith: #48594 (comment) - Several examples by Centril: #48594 (comment) - An example by petrochenkov where this is used in the compiler itself to avoid duplicating error checking code: #48594 (comment) - Amanieu recently provided another example related to complex conditions, where try blocks would not have helped: #48594 (comment) Additionally, petrochenkov notes that this is strictly more powerful than labelled loops due to macros which accidentally exit a loop instead of being consumed by the macro matchers: #48594 (comment) nrc later resolved their concern, mostly because of the aforementioned macro problems. joshtriplett suggested that macros could be able to generate IR directly (#48594 (comment)) but there are no open RFCs, and the design space seems rather speculative. joshtriplett later resolved his concerns, due to a symmetry between this feature and existing labelled break: #48594 (comment) withoutboats has regrettably left the language team. joshtriplett later posted that the lang team would consider starting an FCP given a stabilization report: #48594 (comment) [issue-label]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+label%3AF-label_break_value+ ## Report + Feature gate: - https://github.com/rust-lang/rust/blob/d695a497bbf4b20d2580b75075faa80230d41667/src/test/ui/feature-gates/feature-gate-label_break_value.rs + Diagnostics: - https://github.com/rust-lang/rust/blob/6b2d3d5f3cd1e553d87b5496632132565b6779d3/compiler/rustc_parse/src/parser/diagnostics.rs#L2629 - https://github.com/rust-lang/rust/blob/f65bf0b2bb1a99f73095c01a118f3c37d3ee614c/compiler/rustc_resolve/src/diagnostics.rs#L749 - https://github.com/rust-lang/rust/blob/f65bf0b2bb1a99f73095c01a118f3c37d3ee614c/compiler/rustc_resolve/src/diagnostics.rs#L1001 - https://github.com/rust-lang/rust/blob/111df9e6eda1d752233482c1309d00d20a4bbf98/compiler/rustc_passes/src/loops.rs#L254 - https://github.com/rust-lang/rust/blob/d695a497bbf4b20d2580b75075faa80230d41667/compiler/rustc_parse/src/parser/expr.rs#L2079 - https://github.com/rust-lang/rust/blob/d695a497bbf4b20d2580b75075faa80230d41667/compiler/rustc_parse/src/parser/expr.rs#L1569 + Tests: - https://github.com/rust-lang/rust/blob/master/src/test/ui/label/label_break_value_continue.rs - https://github.com/rust-lang/rust/blob/master/src/test/ui/label/label_break_value_unlabeled_break.rs - https://github.com/rust-lang/rust/blob/master/src/test/ui/label/label_break_value_illegal_uses.rs - https://github.com/rust-lang/rust/blob/master/src/test/ui/lint/unused_labels.rs - https://github.com/rust-lang/rust/blob/master/src/test/ui/run-pass/for-loop-while/label_break_value.rs ## Interactions with other features Labels follow the hygiene of local variables. label-break-value is permitted within `try` blocks: ```rust let _: Result<(), ()> = try { 'foo: { Err(())?; break 'foo; } }; ``` label-break-value is disallowed within closures, generators, and async blocks: ```rust 'a: { || break 'a //~^ ERROR use of unreachable label `'a` //~| ERROR `break` inside of a closure } ``` label-break-value is disallowed on [_BlockExpression_]; it can only occur as a [_LoopExpression_]: ```rust fn labeled_match() { match false 'b: { //~ ERROR block label not supported here _ => {} } } macro_rules! m { ($b:block) => { 'lab: $b; //~ ERROR cannot use a `block` macro fragment here unsafe $b; //~ ERROR cannot use a `block` macro fragment here |x: u8| -> () $b; //~ ERROR cannot use a `block` macro fragment here } } fn foo() { m!({}); } ``` [_BlockExpression_]: https://doc.rust-lang.org/nightly/reference/expressions/block-expr.html [_LoopExpression_]: https://doc.rust-lang.org/nightly/reference/expressions/loop-expr.html
1 parent 4b695f7 commit 31e3944

39 files changed

+61
-108
lines changed

compiler/rustc_ast/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#![feature(const_default_impls)]
1414
#![feature(const_trait_impl)]
1515
#![feature(if_let_guard)]
16-
#![feature(label_break_value)]
16+
#![cfg_attr(bootstrap, feature(label_break_value))]
1717
#![feature(min_specialization)]
1818
#![feature(negative_impls)]
1919
#![feature(slice_internals)]

compiler/rustc_ast_passes/src/feature_gate.rs

-9
Original file line numberDiff line numberDiff line change
@@ -647,14 +647,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
647647
ast::ExprKind::TryBlock(_) => {
648648
gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
649649
}
650-
ast::ExprKind::Block(_, Some(label)) => {
651-
gate_feature_post!(
652-
&self,
653-
label_break_value,
654-
label.ident.span,
655-
"labels on blocks are unstable"
656-
);
657-
}
658650
_ => {}
659651
}
660652
visit::walk_expr(self, e)
@@ -823,7 +815,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
823815
gate_all!(box_patterns, "box pattern syntax is experimental");
824816
gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
825817
gate_all!(try_blocks, "`try` blocks are unstable");
826-
gate_all!(label_break_value, "labels on blocks are unstable");
827818
gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
828819
gate_all!(type_ascription, "type ascription is experimental");
829820

compiler/rustc_error_codes/src/error_codes/E0695.md

-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ A `break` statement without a label appeared inside a labeled block.
33
Erroneous code example:
44

55
```compile_fail,E0695
6-
# #![feature(label_break_value)]
76
loop {
87
'a: {
98
break;
@@ -14,7 +13,6 @@ loop {
1413
Make sure to always label the `break`:
1514

1615
```
17-
# #![feature(label_break_value)]
1816
'l: loop {
1917
'a: {
2018
break 'l;
@@ -25,7 +23,6 @@ Make sure to always label the `break`:
2523
Or if you want to `break` the labeled block:
2624

2725
```
28-
# #![feature(label_break_value)]
2926
loop {
3027
'a: {
3128
break 'a;

compiler/rustc_feature/src/accepted.rs

+2
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ declare_features! (
186186
/// Allows some increased flexibility in the name resolution rules,
187187
/// especially around globs and shadowing (RFC 1560).
188188
(accepted, item_like_imports, "1.15.0", Some(35120), None),
189+
/// Allows `'a: { break 'a; }`.
190+
(accepted, label_break_value, "1.65.0", Some(48594), None),
189191
/// Allows `if/while p && let q = r && ...` chains.
190192
(accepted, let_chains, "1.64.0", Some(53667), None),
191193
/// Allows `break {expr}` with a value inside `loop`s.

compiler/rustc_feature/src/active.rs

-2
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,6 @@ declare_features! (
420420
(active, intra_doc_pointers, "1.51.0", Some(80896), None),
421421
/// Allows `#[instruction_set(_)]` attribute
422422
(active, isa_attribute, "1.48.0", Some(74727), None),
423-
/// Allows `'a: { break 'a; }`.
424-
(active, label_break_value, "1.28.0", Some(48594), None),
425423
// Allows setting the threshold for the `large_assignments` lint.
426424
(active, large_assignments, "1.52.0", Some(83518), None),
427425
/// Allows `let...else` statements.

compiler/rustc_infer/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#![feature(box_patterns)]
1818
#![feature(control_flow_enum)]
1919
#![feature(extend_one)]
20-
#![feature(label_break_value)]
20+
#![cfg_attr(bootstrap, feature(label_break_value))]
2121
#![feature(let_else)]
2222
#![feature(min_specialization)]
2323
#![feature(never_type)]

compiler/rustc_parse/src/parser/expr.rs

-4
Original file line numberDiff line numberDiff line change
@@ -2075,10 +2075,6 @@ impl<'a> Parser<'a> {
20752075
}
20762076
}
20772077

2078-
if let Some(label) = opt_label {
2079-
self.sess.gated_spans.gate(sym::label_break_value, label.ident.span);
2080-
}
2081-
20822078
if self.token.is_whole_block() {
20832079
self.struct_span_err(self.token.span, "cannot use a `block` macro fragment here")
20842080
.span_label(lo.to(self.token.span), "the `block` fragment is within this context")

compiler/rustc_trait_selection/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#![feature(control_flow_enum)]
1717
#![feature(drain_filter)]
1818
#![feature(hash_drain_filter)]
19-
#![feature(label_break_value)]
19+
#![cfg_attr(bootstrap, feature(label_break_value))]
2020
#![feature(let_else)]
2121
#![feature(if_let_guard)]
2222
#![feature(never_type)]

compiler/rustc_typeck/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ This API is completely unstable and subject to change.
6464
#![feature(if_let_guard)]
6565
#![feature(is_sorted)]
6666
#![feature(iter_intersperse)]
67-
#![feature(label_break_value)]
67+
#![cfg_attr(bootstrap, feature(label_break_value))]
6868
#![feature(let_else)]
6969
#![feature(min_specialization)]
7070
#![feature(never_type)]

library/std/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@
252252
#![feature(dropck_eyepatch)]
253253
#![feature(exhaustive_patterns)]
254254
#![feature(intra_doc_pointers)]
255-
#![feature(label_break_value)]
255+
#![cfg_attr(bootstrap, feature(label_break_value))]
256256
#![feature(lang_items)]
257257
#![feature(let_else)]
258258
#![feature(linkage)]

src/test/ui/feature-gates/feature-gate-label_break_value.rs

-5
This file was deleted.

src/test/ui/feature-gates/feature-gate-label_break_value.stderr

-12
This file was deleted.

src/test/ui/for-loop-while/label_break_value.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// run-pass
22
#![allow(dead_code)]
33
#![allow(unused_assignments)]
4-
#![feature(label_break_value)]
54

65
// Test control flow to follow label_break_value semantics
76
fn label_break(a: bool, b: bool) -> u32 {

src/test/ui/for-loop-while/label_break_value_invalid.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#![crate_type = "lib"]
2-
#![feature(label_break_value)]
32

43
fn lbv_macro_test_hygiene_respected() {
54
macro_rules! mac2 {

src/test/ui/for-loop-while/label_break_value_invalid.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0426]: use of undeclared label `'a`
2-
--> $DIR/label_break_value_invalid.rs:7:19
2+
--> $DIR/label_break_value_invalid.rs:6:19
33
|
44
LL | break 'a $val;
55
| ^^ undeclared label `'a`
@@ -10,7 +10,7 @@ LL | mac2!(2);
1010
= note: this error originates in the macro `mac2` (in Nightly builds, run with -Z macro-backtrace for more info)
1111

1212
error[E0426]: use of undeclared label `'a`
13-
--> $DIR/label_break_value_invalid.rs:29:19
13+
--> $DIR/label_break_value_invalid.rs:28:19
1414
|
1515
LL | let x: u8 = mac3!('b: {
1616
| -- a label with a similar name is reachable
@@ -22,7 +22,7 @@ LL | break 'a 3;
2222
| help: try using similarly named label: `'b`
2323

2424
error[E0426]: use of undeclared label `'a`
25-
--> $DIR/label_break_value_invalid.rs:34:29
25+
--> $DIR/label_break_value_invalid.rs:33:29
2626
|
2727
LL | let x: u8 = mac3!(break 'a 4);
2828
| ^^ undeclared label `'a`

src/test/ui/issues/issue-62480.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#![feature(label_break_value)]
2-
31
fn main() {
42
// This used to ICE during liveness check because `target_id` passed to
53
// `propagate_through_expr` would be the closure and not the `loop`, which wouldn't be found in

src/test/ui/issues/issue-62480.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0767]: use of unreachable label `'a`
2-
--> $DIR/issue-62480.rs:8:18
2+
--> $DIR/issue-62480.rs:6:18
33
|
44
LL | 'a: {
55
| -- unreachable label defined here
@@ -9,7 +9,7 @@ LL | || break 'a
99
= note: labels are unreachable through functions, closures, async blocks and modules
1010

1111
error[E0267]: `break` inside of a closure
12-
--> $DIR/issue-62480.rs:8:12
12+
--> $DIR/issue-62480.rs:6:12
1313
|
1414
LL | || break 'a
1515
| -- ^^^^^^^^ cannot `break` inside of a closure

src/test/ui/label/label_break_value_continue.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![feature(label_break_value)]
21
#![allow(unused_labels)]
32

43
// Simple continue pointing to an unlabeled break should yield in an error

src/test/ui/label/label_break_value_continue.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error[E0695]: unlabeled `continue` inside of a labeled block
2-
--> $DIR/label_break_value_continue.rs:7:9
2+
--> $DIR/label_break_value_continue.rs:6:9
33
|
44
LL | continue;
55
| ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label
66

77
error[E0696]: `continue` pointing to a labeled block
8-
--> $DIR/label_break_value_continue.rs:14:9
8+
--> $DIR/label_break_value_continue.rs:13:9
99
|
1010
LL | / 'b: {
1111
LL | | continue 'b;
@@ -14,7 +14,7 @@ LL | | }
1414
| |_____- labeled block the `continue` points to
1515

1616
error[E0695]: unlabeled `continue` inside of a labeled block
17-
--> $DIR/label_break_value_continue.rs:22:13
17+
--> $DIR/label_break_value_continue.rs:21:13
1818
|
1919
LL | continue;
2020
| ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label

src/test/ui/label/label_break_value_desugared_break.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// compile-flags: --edition 2018
2-
#![feature(label_break_value, try_blocks)]
2+
#![feature(try_blocks)]
33

44
// run-pass
55
fn main() {
@@ -9,4 +9,11 @@ fn main() {
99
break 'foo;
1010
}
1111
};
12+
13+
'foo: {
14+
let _: Result<(), ()> = try {
15+
Err(())?;
16+
break 'foo;
17+
};
18+
}
1219
}

src/test/ui/label/label_break_value_illegal_uses.fixed

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// run-rustfix
2-
#![feature(label_break_value)]
32

43
// These are forbidden occurrences of label-break-value
54

src/test/ui/label/label_break_value_illegal_uses.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// run-rustfix
2-
#![feature(label_break_value)]
32

43
// These are forbidden occurrences of label-break-value
54

src/test/ui/label/label_break_value_illegal_uses.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
error: block label not supported here
2-
--> $DIR/label_break_value_illegal_uses.rs:8:12
2+
--> $DIR/label_break_value_illegal_uses.rs:7:12
33
|
44
LL | unsafe 'b: {}
55
| ^^^ not supported here
66

77
error: block label not supported here
8-
--> $DIR/label_break_value_illegal_uses.rs:12:13
8+
--> $DIR/label_break_value_illegal_uses.rs:11:13
99
|
1010
LL | if true 'b: {}
1111
| ^^^ not supported here
1212

1313
error: block label not supported here
14-
--> $DIR/label_break_value_illegal_uses.rs:16:21
14+
--> $DIR/label_break_value_illegal_uses.rs:15:21
1515
|
1616
LL | if true {} else 'b: {}
1717
| ^^^ not supported here
1818

1919
error: block label not supported here
20-
--> $DIR/label_break_value_illegal_uses.rs:20:17
20+
--> $DIR/label_break_value_illegal_uses.rs:19:17
2121
|
2222
LL | match false 'b: {
2323
| ^^^ not supported here

src/test/ui/label/label_break_value_unlabeled_break.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![feature(label_break_value)]
21
#![allow(unused_labels)]
32

43
// Simple unlabeled break should yield in an error

src/test/ui/label/label_break_value_unlabeled_break.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error[E0695]: unlabeled `break` inside of a labeled block
2-
--> $DIR/label_break_value_unlabeled_break.rs:7:9
2+
--> $DIR/label_break_value_unlabeled_break.rs:6:9
33
|
44
LL | break;
55
| ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
66

77
error[E0695]: unlabeled `break` inside of a labeled block
8-
--> $DIR/label_break_value_unlabeled_break.rs:15:13
8+
--> $DIR/label_break_value_unlabeled_break.rs:14:13
99
|
1010
LL | break;
1111
| ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label

src/test/ui/lint/unused_labels.rs

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
// check-pass
66

7-
#![feature(label_break_value)]
87
#![warn(unused_labels)]
98

109
fn main() {

src/test/ui/lint/unused_labels.stderr

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: label name `'many_used_shadowed` shadows a label name that is already in scope
2-
--> $DIR/unused_labels.rs:62:9
2+
--> $DIR/unused_labels.rs:61:9
33
|
44
LL | 'many_used_shadowed: for _ in 0..10 {
55
| ------------------- first declared here
@@ -8,55 +8,55 @@ LL | 'many_used_shadowed: for _ in 0..10 {
88
| ^^^^^^^^^^^^^^^^^^^ label `'many_used_shadowed` already in scope
99

1010
warning: unused label
11-
--> $DIR/unused_labels.rs:11:5
11+
--> $DIR/unused_labels.rs:10:5
1212
|
1313
LL | 'unused_while_label: while 0 == 0 {
1414
| ^^^^^^^^^^^^^^^^^^^
1515
|
1616
note: the lint level is defined here
17-
--> $DIR/unused_labels.rs:8:9
17+
--> $DIR/unused_labels.rs:7:9
1818
|
1919
LL | #![warn(unused_labels)]
2020
| ^^^^^^^^^^^^^
2121

2222
warning: unused label
23-
--> $DIR/unused_labels.rs:16:5
23+
--> $DIR/unused_labels.rs:15:5
2424
|
2525
LL | 'unused_while_let_label: while let Some(_) = opt {
2626
| ^^^^^^^^^^^^^^^^^^^^^^^
2727

2828
warning: unused label
29-
--> $DIR/unused_labels.rs:20:5
29+
--> $DIR/unused_labels.rs:19:5
3030
|
3131
LL | 'unused_for_label: for _ in 0..10 {
3232
| ^^^^^^^^^^^^^^^^^
3333

3434
warning: unused label
35-
--> $DIR/unused_labels.rs:36:9
35+
--> $DIR/unused_labels.rs:35:9
3636
|
3737
LL | 'unused_loop_label_inner_2: for _ in 0..10 {
3838
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
3939

4040
warning: unused label
41-
--> $DIR/unused_labels.rs:42:5
41+
--> $DIR/unused_labels.rs:41:5
4242
|
4343
LL | 'unused_loop_label_outer_3: for _ in 0..10 {
4444
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
4545

4646
warning: unused label
47-
--> $DIR/unused_labels.rs:60:5
47+
--> $DIR/unused_labels.rs:59:5
4848
|
4949
LL | 'many_used_shadowed: for _ in 0..10 {
5050
| ^^^^^^^^^^^^^^^^^^^
5151

5252
warning: unused label
53-
--> $DIR/unused_labels.rs:72:5
53+
--> $DIR/unused_labels.rs:71:5
5454
|
5555
LL | 'unused_loop_label: loop {
5656
| ^^^^^^^^^^^^^^^^^^
5757

5858
warning: unused label
59-
--> $DIR/unused_labels.rs:78:5
59+
--> $DIR/unused_labels.rs:77:5
6060
|
6161
LL | 'unused_block_label: {
6262
| ^^^^^^^^^^^^^^^^^^^

src/test/ui/macros/stringify.rs

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#![feature(decl_macro)]
1010
#![feature(generators)]
1111
#![feature(half_open_range_patterns)]
12-
#![feature(label_break_value)]
1312
#![feature(more_qualified_paths)]
1413
#![feature(raw_ref_op)]
1514
#![feature(trait_alias)]

0 commit comments

Comments
 (0)