Skip to content

Commit 0b43dab

Browse files
authored
Unrolled build for #138780
Rollup merge of #138780 - trifectatechfoundation:loop_match_attr, r=oli-obk,traviscross Add `#[loop_match]` for improved DFA codegen tracking issue: #132306 project goal: rust-lang/rust-project-goals#258 This PR adds the `#[loop_match]` attribute, which aims to improve code generation for state machines. For some (very exciting) benchmarks, see rust-lang/rust-project-goals#258 (comment) Currently, a very restricted syntax pattern is accepted. We'd like to get feedback and merge this now before we go too far in a direction that others have concerns with. ## current state We accept code that looks like this ```rust #[loop_match] loop { state = 'blk: { match state { State::A => { #[const_continue] break 'blk State::B } State::B => { /* ... */ } /* ... */ } } } ``` - a loop should have the same semantics with and without `#[loop_match]`: normal `continue` and `break` continue to work - `#[const_continue]` is only allowed in loops annotated with `#[loop_match]` - the loop body needs to have this particular shape (a single assignment to the match scrutinee, with the body a labelled block containing just a match) ## future work - perform const evaluation on the `break` value - support more state/scrutinee types ## maybe future work - allow `continue 'label value` syntax, which `#[const_continue]` could then use. - allow the match to be on an arbitrary expression (e.g. `State::Initial`) - attempt to also optimize `break`/`continue` expressions that are not marked with `#[const_continue]` r? ``@traviscross``
2 parents 2c2bb99 + ba5556d commit 0b43dab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2480
-45
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4116,6 +4116,7 @@ dependencies = [
41164116
"rustc_apfloat",
41174117
"rustc_arena",
41184118
"rustc_ast",
4119+
"rustc_attr_data_structures",
41194120
"rustc_data_structures",
41204121
"rustc_errors",
41214122
"rustc_fluent_macro",

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ pub enum AttributeKind {
212212
first_span: Span,
213213
},
214214

215+
/// Represents `#[const_continue]`.
216+
ConstContinue(Span),
217+
215218
/// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
216219
ConstStability {
217220
stability: PartialConstStability,
@@ -231,6 +234,9 @@ pub enum AttributeKind {
231234
/// Represents `#[inline]` and `#[rustc_force_inline]`.
232235
Inline(InlineAttr, Span),
233236

237+
/// Represents `#[loop_match]`.
238+
LoopMatch(Span),
239+
234240
/// Represents `#[rustc_macro_transparency]`.
235241
MacroTransparency(Transparency),
236242

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use rustc_attr_data_structures::AttributeKind;
2+
use rustc_feature::{AttributeTemplate, template};
3+
use rustc_span::{Symbol, sym};
4+
5+
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
6+
use crate::context::{AcceptContext, Stage};
7+
use crate::parser::ArgParser;
8+
9+
pub(crate) struct LoopMatchParser;
10+
impl<S: Stage> SingleAttributeParser<S> for LoopMatchParser {
11+
const PATH: &[Symbol] = &[sym::loop_match];
12+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
13+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
14+
const TEMPLATE: AttributeTemplate = template!(Word);
15+
16+
fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
17+
Some(AttributeKind::LoopMatch(cx.attr_span))
18+
}
19+
}
20+
21+
pub(crate) struct ConstContinueParser;
22+
impl<S: Stage> SingleAttributeParser<S> for ConstContinueParser {
23+
const PATH: &[Symbol] = &[sym::const_continue];
24+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
25+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
26+
const TEMPLATE: AttributeTemplate = template!(Word);
27+
28+
fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
29+
Some(AttributeKind::ConstContinue(cx.attr_span))
30+
}
31+
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub(crate) mod confusables;
3232
pub(crate) mod deprecation;
3333
pub(crate) mod inline;
3434
pub(crate) mod lint_helpers;
35+
pub(crate) mod loop_match;
3536
pub(crate) mod must_use;
3637
pub(crate) mod repr;
3738
pub(crate) mod semantics;

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::attributes::confusables::ConfusablesParser;
2020
use crate::attributes::deprecation::DeprecationParser;
2121
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
2222
use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
23+
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
2324
use crate::attributes::must_use::MustUseParser;
2425
use crate::attributes::repr::{AlignParser, ReprParser};
2526
use crate::attributes::semantics::MayDangleParser;
@@ -111,9 +112,11 @@ attribute_parsers!(
111112
// tidy-alphabetical-start
112113
Single<AsPtrParser>,
113114
Single<ColdParser>,
115+
Single<ConstContinueParser>,
114116
Single<ConstStabilityIndirectParser>,
115117
Single<DeprecationParser>,
116118
Single<InlineParser>,
119+
Single<LoopMatchParser>,
117120
Single<MayDangleParser>,
118121
Single<MustUseParser>,
119122
Single<NoMangleParser>,

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,19 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
657657
EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const),
658658
),
659659

660+
// The `#[loop_match]` and `#[const_continue]` attributes are part of the
661+
// lang experiment for RFC 3720 tracked in:
662+
//
663+
// - https://github.com/rust-lang/rust/issues/132306
664+
gated!(
665+
const_continue, Normal, template!(Word), ErrorFollowing,
666+
EncodeCrossCrate::No, loop_match, experimental!(const_continue)
667+
),
668+
gated!(
669+
loop_match, Normal, template!(Word), ErrorFollowing,
670+
EncodeCrossCrate::No, loop_match, experimental!(loop_match)
671+
),
672+
660673
// ==========================================================================
661674
// Internal attributes: Stability, deprecation, and unsafe:
662675
// ==========================================================================

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,8 @@ declare_features! (
557557
/// Allows using `#[link(kind = "link-arg", name = "...")]`
558558
/// to pass custom arguments to the linker.
559559
(unstable, link_arg_attribute, "1.76.0", Some(99427)),
560+
/// Allows fused `loop`/`match` for direct intraprocedural jumps.
561+
(incomplete, loop_match, "CURRENT_RUSTC_VERSION", Some(132306)),
560562
/// Give access to additional metadata about declarative macro meta-variables.
561563
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
562564
/// Provides a way to concatenate identifiers using metavariable expressions.

compiler/rustc_hir_typeck/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ hir_typeck_cast_unknown_pointer = cannot cast {$to ->
7979
.note = the type information given here is insufficient to check whether the pointer cast is valid
8080
.label_from = the type information given here is insufficient to check whether the pointer cast is valid
8181
82+
hir_typeck_const_continue_bad_label =
83+
`#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
84+
8285
hir_typeck_const_select_must_be_const = this argument must be a `const fn`
8386
.help = consult the documentation on `const_eval_select` for more information
8487

compiler/rustc_hir_typeck/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,3 +1167,10 @@ pub(crate) struct AbiCannotBeCalled {
11671167
pub span: Span,
11681168
pub abi: ExternAbi,
11691169
}
1170+
1171+
#[derive(Diagnostic)]
1172+
#[diag(hir_typeck_const_continue_bad_label)]
1173+
pub(crate) struct ConstContinueBadLabel {
1174+
#[primary_span]
1175+
pub span: Span,
1176+
}

compiler/rustc_hir_typeck/src/loops.rs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::collections::BTreeMap;
22
use std::fmt;
33

44
use Context::*;
5+
use rustc_ast::Label;
6+
use rustc_attr_data_structures::{AttributeKind, find_attr};
57
use rustc_hir as hir;
68
use rustc_hir::def::DefKind;
79
use rustc_hir::def_id::LocalDefId;
@@ -14,8 +16,9 @@ use rustc_span::hygiene::DesugaringKind;
1416
use rustc_span::{BytePos, Span};
1517

1618
use crate::errors::{
17-
BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
18-
OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
19+
BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ConstContinueBadLabel,
20+
ContinueLabeledBlock, OutsideLoop, OutsideLoopSuggestion, UnlabeledCfInWhileCondition,
21+
UnlabeledInLabeledBlock,
1922
};
2023

2124
/// The context in which a block is encountered.
@@ -37,6 +40,11 @@ enum Context {
3740
AnonConst,
3841
/// E.g. `const { ... }`.
3942
ConstBlock,
43+
/// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
44+
LoopMatch {
45+
/// The label of the labeled block (not of the loop itself).
46+
labeled_block: Label,
47+
},
4048
}
4149

4250
#[derive(Clone)]
@@ -141,7 +149,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
141149
}
142150
}
143151
hir::ExprKind::Loop(ref b, _, source, _) => {
144-
self.with_context(Loop(source), |v| v.visit_block(b));
152+
let cx = match self.is_loop_match(e, b) {
153+
Some(labeled_block) => LoopMatch { labeled_block },
154+
None => Loop(source),
155+
};
156+
157+
self.with_context(cx, |v| v.visit_block(b));
145158
}
146159
hir::ExprKind::Closure(&hir::Closure {
147160
ref fn_decl, body, fn_decl_span, kind, ..
@@ -197,6 +210,23 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
197210
Err(hir::LoopIdError::UnresolvedLabel) => None,
198211
};
199212

213+
// A `#[const_continue]` must break to a block in a `#[loop_match]`.
214+
if find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::ConstContinue(_)) {
215+
if let Some(break_label) = break_label.label {
216+
let is_target_label = |cx: &Context| match cx {
217+
Context::LoopMatch { labeled_block } => {
218+
break_label.ident.name == labeled_block.ident.name
219+
}
220+
_ => false,
221+
};
222+
223+
if !self.cx_stack.iter().rev().any(is_target_label) {
224+
let span = break_label.ident.span;
225+
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
226+
}
227+
}
228+
}
229+
200230
if let Some(Node::Block(_)) = loop_id.map(|id| self.tcx.hir_node(id)) {
201231
return;
202232
}
@@ -299,7 +329,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
299329
cx_pos: usize,
300330
) {
301331
match self.cx_stack[cx_pos] {
302-
LabeledBlock | Loop(_) => {}
332+
LabeledBlock | Loop(_) | LoopMatch { .. } => {}
303333
Closure(closure_span) => {
304334
self.tcx.dcx().emit_err(BreakInsideClosure {
305335
span,
@@ -380,4 +410,36 @@ impl<'hir> CheckLoopVisitor<'hir> {
380410
});
381411
}
382412
}
413+
414+
/// Is this a loop annotated with `#[loop_match]` that looks syntactically sound?
415+
fn is_loop_match(
416+
&self,
417+
e: &'hir hir::Expr<'hir>,
418+
body: &'hir hir::Block<'hir>,
419+
) -> Option<Label> {
420+
if !find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::LoopMatch(_)) {
421+
return None;
422+
}
423+
424+
// NOTE: Diagnostics are emitted during MIR construction.
425+
426+
// Accept either `state = expr` or `state = expr;`.
427+
let loop_body_expr = match body.stmts {
428+
[] => match body.expr {
429+
Some(expr) => expr,
430+
None => return None,
431+
},
432+
[single] if body.expr.is_none() => match single.kind {
433+
hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => expr,
434+
_ => return None,
435+
},
436+
[..] => return None,
437+
};
438+
439+
let hir::ExprKind::Assign(_, rhs_expr, _) = loop_body_expr.kind else { return None };
440+
441+
let hir::ExprKind::Block(_, label) = rhs_expr.kind else { return None };
442+
443+
label
444+
}
383445
}

0 commit comments

Comments
 (0)