loop match: error on #[const_continue] outside #[loop_match]
This commit is contained in:
parent
adcb3d3b4c
commit
040f71e812
9 changed files with 107 additions and 34 deletions
|
|
@ -3016,7 +3016,7 @@ impl fmt::Display for LoopIdError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, HashStable_Generic)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, HashStable_Generic)]
|
||||
pub struct Destination {
|
||||
/// This is `Some(_)` iff there is an explicit user-specified 'label
|
||||
pub label: Option<Label>,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use std::collections::BTreeMap;
|
|||
use std::fmt;
|
||||
|
||||
use Context::*;
|
||||
use rustc_ast::Label;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::DefKind;
|
||||
|
|
@ -42,8 +41,8 @@ enum Context {
|
|||
ConstBlock,
|
||||
/// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
|
||||
LoopMatch {
|
||||
/// The label of the labeled block (not of the loop itself).
|
||||
labeled_block: Label,
|
||||
/// The destination pointing to the labeled block (not to the loop itself).
|
||||
labeled_block: Destination,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -186,18 +185,18 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
|
|||
{
|
||||
self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b));
|
||||
}
|
||||
hir::ExprKind::Break(break_label, ref opt_expr) => {
|
||||
hir::ExprKind::Break(break_destination, ref opt_expr) => {
|
||||
if let Some(e) = opt_expr {
|
||||
self.visit_expr(e);
|
||||
}
|
||||
|
||||
if self.require_label_in_labeled_block(e.span, &break_label, "break") {
|
||||
if self.require_label_in_labeled_block(e.span, &break_destination, "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 break_label.target_id {
|
||||
let loop_id = match break_destination.target_id {
|
||||
Ok(loop_id) => Some(loop_id),
|
||||
Err(hir::LoopIdError::OutsideLoopScope) => None,
|
||||
Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
|
||||
|
|
@ -212,18 +211,25 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
|
|||
|
||||
// A `#[const_continue]` must break to a block in a `#[loop_match]`.
|
||||
if find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::ConstContinue(_)) {
|
||||
if let Some(break_label) = break_label.label {
|
||||
let is_target_label = |cx: &Context| match cx {
|
||||
Context::LoopMatch { labeled_block } => {
|
||||
break_label.ident.name == labeled_block.ident.name
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
let Some(label) = break_destination.label else {
|
||||
let span = e.span;
|
||||
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
|
||||
};
|
||||
|
||||
if !self.cx_stack.iter().rev().any(is_target_label) {
|
||||
let span = break_label.ident.span;
|
||||
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
|
||||
let is_target_label = |cx: &Context| match cx {
|
||||
Context::LoopMatch { labeled_block } => {
|
||||
// NOTE: with macro expansion, the label's span might be different here
|
||||
// even though it does still refer to the same HIR node. A block
|
||||
// can't have two labels, so the hir_id is a unique identifier.
|
||||
assert!(labeled_block.target_id.is_ok()); // see `is_loop_match`.
|
||||
break_destination.target_id == labeled_block.target_id
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !self.cx_stack.iter().rev().any(is_target_label) {
|
||||
let span = label.ident.span;
|
||||
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -249,7 +255,7 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
|
|||
Some(kind) => {
|
||||
let suggestion = format!(
|
||||
"break{}",
|
||||
break_label
|
||||
break_destination
|
||||
.label
|
||||
.map_or_else(String::new, |l| format!(" {}", l.ident))
|
||||
);
|
||||
|
|
@ -259,7 +265,7 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
|
|||
kind: kind.name(),
|
||||
suggestion,
|
||||
loop_label,
|
||||
break_label: break_label.label,
|
||||
break_label: break_destination.label,
|
||||
break_expr_kind: &break_expr.kind,
|
||||
break_expr_span: break_expr.span,
|
||||
});
|
||||
|
|
@ -268,7 +274,7 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
|
|||
}
|
||||
|
||||
let sp_lo = e.span.with_lo(e.span.lo() + BytePos("break".len() as u32));
|
||||
let label_sp = match break_label.label {
|
||||
let label_sp = match break_destination.label {
|
||||
Some(label) => sp_lo.with_hi(label.ident.span.hi()),
|
||||
None => sp_lo.shrink_to_lo(),
|
||||
};
|
||||
|
|
@ -416,7 +422,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
|
|||
&self,
|
||||
e: &'hir hir::Expr<'hir>,
|
||||
body: &'hir hir::Block<'hir>,
|
||||
) -> Option<Label> {
|
||||
) -> Option<Destination> {
|
||||
if !find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::LoopMatch(_)) {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -438,8 +444,8 @@ impl<'hir> CheckLoopVisitor<'hir> {
|
|||
|
||||
let hir::ExprKind::Assign(_, rhs_expr, _) = loop_body_expr.kind else { return None };
|
||||
|
||||
let hir::ExprKind::Block(_, label) = rhs_expr.kind else { return None };
|
||||
let hir::ExprKind::Block(block, label) = rhs_expr.kind else { return None };
|
||||
|
||||
label
|
||||
Some(Destination { label, target_id: Ok(block.hir_id) })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ mir_build_confused = missing patterns are not covered because `{$variable}` is i
|
|||
mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
|
||||
.label = this value is too generic
|
||||
|
||||
mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
|
||||
mir_build_const_continue_missing_label_or_value = a `#[const_continue]` must break to a label with a value
|
||||
|
||||
mir_build_const_continue_not_const = could not determine the target branch for this `#[const_continue]`
|
||||
.help = try extracting the expression into a `const` item
|
||||
|
|
|
|||
|
|
@ -1254,8 +1254,8 @@ pub(crate) struct ConstContinueBadConst {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_const_continue_missing_value)]
|
||||
pub(crate) struct ConstContinueMissingValue {
|
||||
#[diag(mir_build_const_continue_missing_label_or_value)]
|
||||
pub(crate) struct ConstContinueMissingLabelOrValue {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -852,9 +852,9 @@ impl<'tcx> ThirBuildCx<'tcx> {
|
|||
if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::ConstContinue(_)) {
|
||||
match dest.target_id {
|
||||
Ok(target_id) => {
|
||||
let Some(value) = value else {
|
||||
let (Some(value), Some(_)) = (value, dest.label) else {
|
||||
let span = expr.span;
|
||||
self.tcx.dcx().emit_fatal(ConstContinueMissingValue { span })
|
||||
self.tcx.dcx().emit_fatal(ConstContinueMissingLabelOrValue { span })
|
||||
};
|
||||
|
||||
ExprKind::ConstContinue {
|
||||
|
|
|
|||
|
|
@ -24,3 +24,24 @@ fn const_continue_to_block() -> u8 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn const_continue_to_shadowed_block() -> u8 {
|
||||
let state = 0;
|
||||
#[loop_match]
|
||||
loop {
|
||||
state = 'blk: {
|
||||
match state {
|
||||
0 => {
|
||||
#[const_continue]
|
||||
break 'blk 1;
|
||||
}
|
||||
_ => 'blk: {
|
||||
//~^ WARN label name `'blk` shadows a label name that is already in scope
|
||||
#[const_continue]
|
||||
break 'blk 2;
|
||||
//~^ ERROR `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,23 @@
|
|||
warning: label name `'blk` shadows a label name that is already in scope
|
||||
--> $DIR/const-continue-to-block.rs:38:22
|
||||
|
|
||||
LL | state = 'blk: {
|
||||
| ---- first declared here
|
||||
...
|
||||
LL | _ => 'blk: {
|
||||
| ^^^^ label `'blk` already in scope
|
||||
|
||||
error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
|
||||
--> $DIR/const-continue-to-block.rs:20:27
|
||||
|
|
||||
LL | break 'b 2;
|
||||
| ^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
|
||||
--> $DIR/const-continue-to-block.rs:41:27
|
||||
|
|
||||
LL | break 'blk 2;
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to 2 previous errors; 1 warning emitted
|
||||
|
||||
|
|
|
|||
|
|
@ -142,6 +142,25 @@ fn break_without_value_unit() {
|
|||
}
|
||||
}
|
||||
|
||||
fn break_without_label() {
|
||||
let mut state = State::A;
|
||||
let _ = {
|
||||
#[loop_match]
|
||||
loop {
|
||||
state = 'blk: {
|
||||
match state {
|
||||
_ => {
|
||||
#[const_continue]
|
||||
break State::A;
|
||||
//~^ ERROR unlabeled `break` inside of a labeled block
|
||||
//~| ERROR a `#[const_continue]` must break to a label with a value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn arm_has_guard(cond: bool) {
|
||||
let mut state = State::A;
|
||||
#[loop_match]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@ help: give the `break` a value of the expected type
|
|||
LL | break 'blk /* value */;
|
||||
| +++++++++++
|
||||
|
||||
error[E0695]: unlabeled `break` inside of a labeled block
|
||||
--> $DIR/invalid.rs:154:25
|
||||
|
|
||||
LL | break State::A;
|
||||
| ^^^^^^^^^^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
|
||||
|
||||
error: invalid update of the `#[loop_match]` state
|
||||
--> $DIR/invalid.rs:18:9
|
||||
|
|
||||
|
|
@ -80,14 +86,20 @@ error: a `#[const_continue]` must break to a label with a value
|
|||
LL | break 'blk;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: a `#[const_continue]` must break to a label with a value
|
||||
--> $DIR/invalid.rs:154:25
|
||||
|
|
||||
LL | break State::A;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: match arms that are part of a `#[loop_match]` cannot have guards
|
||||
--> $DIR/invalid.rs:155:29
|
||||
--> $DIR/invalid.rs:174:29
|
||||
|
|
||||
LL | State::B if cond => break 'a,
|
||||
| ^^^^
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `State::B` and `State::C` not covered
|
||||
--> $DIR/invalid.rs:168:19
|
||||
--> $DIR/invalid.rs:187:19
|
||||
|
|
||||
LL | match state {
|
||||
| ^^^^^ patterns `State::B` and `State::C` not covered
|
||||
|
|
@ -110,12 +122,12 @@ LL ~ State::B | State::C => todo!(),
|
|||
|
|
||||
|
||||
error[E0579]: lower range bound must be less than upper
|
||||
--> $DIR/invalid.rs:185:17
|
||||
--> $DIR/invalid.rs:204:17
|
||||
|
|
||||
LL | 4.0..3.0 => {
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
error: aborting due to 16 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0004, E0308, E0579.
|
||||
Some errors have detailed explanations: E0004, E0308, E0579, E0695.
|
||||
For more information about an error, try `rustc --explain E0004`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue