Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
870f848
Add support for trait associated items
GuillaumeGomez Jan 20, 2025
ef39e42
Better handling of paths in link to def feature
GuillaumeGomez Jan 26, 2025
9457a32
Update to last rustc_hir Visitor changes
GuillaumeGomez Jan 26, 2025
687ac3f
Fix panic if an item does not have a body
GuillaumeGomez May 4, 2025
45231fa
[rustdoc] Display unsafe attrs with edition 2024 `unsafe()` wrappers.
obi1kenobi Jul 23, 2025
5ae2d42
get rid of some false negatives in rustdoc::broken_intra_doc_links
lolbinarycat Nov 7, 2024
87d7d80
adjust more unit tests to reflect more aggressive intra-doc linting
lolbinarycat Nov 8, 2024
a7da4b8
rustdoc::broken_intra_doc_links: no backticks = use old behavior
lolbinarycat Apr 18, 2025
0413481
rustdoc: update tests to match new lint behavior
lolbinarycat Apr 18, 2025
6a7d488
rustdoc::broken_intra_doc_links: only be lenient with shortcut links
lolbinarycat Apr 19, 2025
bd85df1
move bad-intra-doc test into intra-doc dir
lolbinarycat Apr 19, 2025
a73d7e3
fix up issues with internal compiler docs revealed by stricter lint
lolbinarycat Jul 24, 2025
0e53d85
Fortify RemoveUnneededDrops test.
cjgillot Jun 16, 2025
ee0118f
[test][AIX] ignore extern_weak linkage test
daltenty Jul 30, 2025
38d5f43
Correctly handle `--no-run` rustdoc test option
GuillaumeGomez Jul 13, 2025
234b8ac
Correctly handle `should_panic` doctest attribute
GuillaumeGomez Jul 7, 2025
631ade8
Add regression test for #143009
GuillaumeGomez Jul 4, 2025
3f7b989
Update std doctests
GuillaumeGomez Jul 7, 2025
c365e6e
Add regression test for #143858
GuillaumeGomez Jul 13, 2025
5aec437
detect infinite recursion with tail calls in ctfe
WaffleLapkin Jul 31, 2025
040f71e
loop match: error on `#[const_continue]` outside `#[loop_match]`
folkertdev Jul 25, 2025
f909918
Add human readable name "Cygwin"
Berrysoft Aug 1, 2025
dbf52ba
Remove the omit_gdb_pretty_printer_section attribute
bjorn3 Jul 31, 2025
61c5343
Add FIXME comments to use `test::ERROR_EXIT_CODE` once public and fix…
GuillaumeGomez Aug 1, 2025
dd9eb3d
Rollup merge of #132748 - lolbinarycat:rustdoc-intra-doc-link-warn-mo…
GuillaumeGomez Aug 1, 2025
5fe6fa4
Rollup merge of #135771 - GuillaumeGomez:jump-to-def-perf, r=fmease
GuillaumeGomez Aug 1, 2025
5b47d0e
Rollup merge of #143360 - folkertdev:const-continue-outside-loop-matc…
GuillaumeGomez Aug 1, 2025
7262030
Rollup merge of #143662 - obi1kenobi:pg/unsafe-attribute-wrappers, r=…
GuillaumeGomez Aug 1, 2025
08058ee
Rollup merge of #143900 - GuillaumeGomez:fix-no-run, r=lolbinarycat,f…
GuillaumeGomez Aug 1, 2025
9d5bd91
Rollup merge of #144614 - cjgillot:fortify-unneeded, r=scottmcm
GuillaumeGomez Aug 1, 2025
7250ce7
Rollup merge of #144703 - daltenty:daltenty/extern-weak, r=Noratrieb
GuillaumeGomez Aug 1, 2025
1888740
Rollup merge of #144738 - bjorn3:remove_omit_gdb_pretty_printer_secti…
GuillaumeGomez Aug 1, 2025
939dee5
Rollup merge of #144756 - WaffleLapkin:inf-rec-etc-ctfe, r=lqd
GuillaumeGomez Aug 1, 2025
55bffb4
Rollup merge of #144766 - Berrysoft:patch-1, r=GuillaumeGomez
GuillaumeGomez Aug 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
loop match: error on #[const_continue] outside #[loop_match]
  • Loading branch information
folkertdev committed Jul 31, 2025
commit 040f71e8123b5994177f787e64aecd9b06cdfa7e
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
50 changes: 28 additions & 22 deletions compiler/rustc_hir_typeck/src/loops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
},
}

Expand Down Expand Up @@ -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) => {
Expand All @@ -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 });
}
}

Expand All @@ -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))
);
Expand All @@ -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,
});
Expand All @@ -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(),
};
Expand Down Expand Up @@ -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;
}
Expand All @@ -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) })
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
21 changes: 21 additions & 0 deletions tests/ui/loop-match/const-continue-to-block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]`
}
}
}
}
}
17 changes: 16 additions & 1 deletion tests/ui/loop-match/const-continue-to-block.stderr
Original file line number Diff line number Diff line change
@@ -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

19 changes: 19 additions & 0 deletions tests/ui/loop-match/invalid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
22 changes: 17 additions & 5 deletions tests/ui/loop-match/invalid.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand Down Expand Up @@ -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
Expand All @@ -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