Rollup merge of #124777 - veera-sivarajan:bugfix-124495-identify-gen-block, r=compiler-errors

Fix Error Messages for `break` Inside Coroutines

Fixes #124495

Previously, `break` inside `gen` blocks and functions
were incorrectly identified to be enclosed by a closure.

This PR fixes it by displaying an appropriate error message
for async blocks, async closures, async functions, gen blocks,
gen closures, gen functions, async gen blocks, async gen closures
and async gen functions.

Note: gen closure and async gen closure are not supported by the
compiler yet but I have added an error message here assuming that
they might be implemented in the future.

~~Also, fixes grammar in a few places by replacing
`inside of a $coroutine` with `inside a $coroutine`.~~
This commit is contained in:
Matthias Krüger 2024-05-09 06:04:38 +02:00 committed by GitHub
commit 9b834d01e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 135 additions and 24 deletions

View File

@ -52,16 +52,16 @@ passes_attr_only_in_functions =
passes_both_ffi_const_and_pure =
`#[ffi_const]` function cannot be `#[ffi_pure]`
passes_break_inside_async_block =
`{$name}` inside of an `async` block
.label = cannot `{$name}` inside of an `async` block
.async_block_label = enclosing `async` block
passes_break_inside_closure =
`{$name}` inside of a closure
.label = cannot `{$name}` inside of a closure
.closure_label = enclosing closure
passes_break_inside_coroutine =
`{$name}` inside `{$kind}` {$source}
.label = cannot `{$name}` inside `{$kind}` {$source}
.coroutine_label = enclosing `{$kind}` {$source}
passes_break_non_loop =
`break` with value from a `{$kind}` loop
.label = can only break with a value inside `loop` or breakable block

View File

@ -1086,14 +1086,16 @@ pub struct BreakInsideClosure<'a> {
}
#[derive(Diagnostic)]
#[diag(passes_break_inside_async_block, code = E0267)]
pub struct BreakInsideAsyncBlock<'a> {
#[diag(passes_break_inside_coroutine, code = E0267)]
pub struct BreakInsideCoroutine<'a> {
#[primary_span]
#[label]
pub span: Span,
#[label(passes_async_block_label)]
pub closure_span: Span,
#[label(passes_coroutine_label)]
pub coroutine_span: Span,
pub name: &'a str,
pub kind: &'a str,
pub source: &'a str,
}
#[derive(Diagnostic)]

View File

@ -13,7 +13,7 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::{BytePos, Span};
use crate::errors::{
BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
};
@ -23,7 +23,7 @@ enum Context {
Fn,
Loop(hir::LoopSource),
Closure(Span),
AsyncClosure(Span),
Coroutine { coroutine_span: Span, kind: hir::CoroutineDesugaring, source: hir::CoroutineSource },
UnlabeledBlock(Span),
LabeledBlock,
Constant,
@ -89,12 +89,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
hir::ExprKind::Closure(&hir::Closure {
ref fn_decl, body, fn_decl_span, kind, ..
}) => {
// FIXME(coroutines): This doesn't handle coroutines correctly
let cx = match kind {
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Block,
)) => AsyncClosure(fn_decl_span),
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(kind, source)) => {
Coroutine { coroutine_span: fn_decl_span, kind, source }
}
_ => Closure(fn_decl_span),
};
self.visit_fn_decl(fn_decl);
@ -227,8 +225,24 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
Closure(closure_span) => {
self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name });
}
AsyncClosure(closure_span) => {
self.sess.dcx().emit_err(BreakInsideAsyncBlock { span, closure_span, name });
Coroutine { coroutine_span, kind, source } => {
let kind = match kind {
hir::CoroutineDesugaring::Async => "async",
hir::CoroutineDesugaring::Gen => "gen",
hir::CoroutineDesugaring::AsyncGen => "async gen",
};
let source = match source {
hir::CoroutineSource::Block => "block",
hir::CoroutineSource::Closure => "closure",
hir::CoroutineSource::Fn => "function",
};
self.sess.dcx().emit_err(BreakInsideCoroutine {
span,
coroutine_span,
name,
kind,
source,
});
}
UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => {
let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });

View File

@ -29,14 +29,14 @@ async fn return_targets_async_block_not_async_fn() -> u8 {
fn no_break_in_async_block() {
async {
break 0u8; //~ ERROR `break` inside of an `async` block
break 0u8; //~ ERROR `break` inside `async` block
};
}
fn no_break_in_async_block_even_with_outer_loop() {
loop {
async {
break 0u8; //~ ERROR `break` inside of an `async` block
break 0u8; //~ ERROR `break` inside `async` block
};
}
}

View File

@ -1,18 +1,18 @@
error[E0267]: `break` inside of an `async` block
error[E0267]: `break` inside `async` block
--> $DIR/async-block-control-flow-static-semantics.rs:32:9
|
LL | / async {
LL | | break 0u8;
| | ^^^^^^^^^ cannot `break` inside of an `async` block
| | ^^^^^^^^^ cannot `break` inside `async` block
LL | | };
| |_____- enclosing `async` block
error[E0267]: `break` inside of an `async` block
error[E0267]: `break` inside `async` block
--> $DIR/async-block-control-flow-static-semantics.rs:39:13
|
LL | / async {
LL | | break 0u8;
| | ^^^^^^^^^ cannot `break` inside of an `async` block
| | ^^^^^^^^^ cannot `break` inside `async` block
LL | | };
| |_________- enclosing `async` block

View File

@ -0,0 +1,26 @@
//@ edition: 2024
//@ compile-flags: -Z unstable-options
#![feature(gen_blocks)]
#![feature(async_closure)]
async fn async_fn() {
break; //~ ERROR `break` inside `async` function
}
gen fn gen_fn() {
break; //~ ERROR `break` inside `gen` function
}
async gen fn async_gen_fn() {
break; //~ ERROR `break` inside `async gen` function
}
fn main() {
let _ = async { break; }; //~ ERROR `break` inside `async` block
let _ = async || { break; }; //~ ERROR `break` inside `async` closure
let _ = gen { break; }; //~ ERROR `break` inside `gen` block
let _ = async gen { break; }; //~ ERROR `break` inside `async gen` block
}

View File

@ -0,0 +1,69 @@
error[E0267]: `break` inside `async` function
--> $DIR/break-inside-coroutine-issue-124495.rs:8:5
|
LL | async fn async_fn() {
| _____________________-
LL | | break;
| | ^^^^^ cannot `break` inside `async` function
LL | | }
| |_- enclosing `async` function
error[E0267]: `break` inside `gen` function
--> $DIR/break-inside-coroutine-issue-124495.rs:12:5
|
LL | gen fn gen_fn() {
| _________________-
LL | | break;
| | ^^^^^ cannot `break` inside `gen` function
LL | | }
| |_- enclosing `gen` function
error[E0267]: `break` inside `async gen` function
--> $DIR/break-inside-coroutine-issue-124495.rs:16:5
|
LL | async gen fn async_gen_fn() {
| _____________________________-
LL | | break;
| | ^^^^^ cannot `break` inside `async gen` function
LL | | }
| |_- enclosing `async gen` function
error[E0267]: `break` inside `async` block
--> $DIR/break-inside-coroutine-issue-124495.rs:20:21
|
LL | let _ = async { break; };
| --------^^^^^---
| | |
| | cannot `break` inside `async` block
| enclosing `async` block
error[E0267]: `break` inside `async` closure
--> $DIR/break-inside-coroutine-issue-124495.rs:21:24
|
LL | let _ = async || { break; };
| --^^^^^---
| | |
| | cannot `break` inside `async` closure
| enclosing `async` closure
error[E0267]: `break` inside `gen` block
--> $DIR/break-inside-coroutine-issue-124495.rs:23:19
|
LL | let _ = gen { break; };
| ------^^^^^---
| | |
| | cannot `break` inside `gen` block
| enclosing `gen` block
error[E0267]: `break` inside `async gen` block
--> $DIR/break-inside-coroutine-issue-124495.rs:25:25
|
LL | let _ = async gen { break; };
| ------------^^^^^---
| | |
| | cannot `break` inside `async gen` block
| enclosing `async gen` block
error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0267`.