Skip to content

Commit a774736

Browse files
bjorn3folkertdev
andcommitted
Add #[loop_match] for improved DFA codegen
Co-authored-by: Folkert de Vries <[email protected]>
1 parent 8da6239 commit a774736

Some content is hidden

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

51 files changed

+2445
-46
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

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

637+
// The `#[loop_match]` and `#[const_continue]` attributes are part of the
638+
// lang experiment for RFC 3720 tracked in:
639+
//
640+
// - https://github.com/rust-lang/rust/issues/132306
641+
gated!(
642+
const_continue, Normal, template!(Word), ErrorFollowing,
643+
EncodeCrossCrate::No, loop_match, experimental!(const_continue)
644+
),
645+
gated!(
646+
loop_match, Normal, template!(Word), ErrorFollowing,
647+
EncodeCrossCrate::No, loop_match, experimental!(loop_match)
648+
),
649+
637650
// ==========================================================================
638651
// Internal attributes: Stability, deprecation, and unsafe:
639652
// ==========================================================================

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
@@ -1170,3 +1170,10 @@ pub(crate) struct AbiCustomCall {
11701170
#[primary_span]
11711171
pub span: Span,
11721172
}
1173+
1174+
#[derive(Diagnostic)]
1175+
#[diag(hir_typeck_const_continue_bad_label)]
1176+
pub(crate) struct ConstContinueBadLabel {
1177+
#[primary_span]
1178+
pub span: Span,
1179+
}

compiler/rustc_hir_typeck/src/loops.rs

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

44
use Context::*;
5+
use rustc_ast::Label;
56
use rustc_hir as hir;
67
use rustc_hir::def::DefKind;
78
use rustc_hir::def_id::LocalDefId;
@@ -11,11 +12,12 @@ use rustc_middle::hir::nested_filter;
1112
use rustc_middle::span_bug;
1213
use rustc_middle::ty::TyCtxt;
1314
use rustc_span::hygiene::DesugaringKind;
14-
use rustc_span::{BytePos, Span};
15+
use rustc_span::{BytePos, Span, sym};
1516

1617
use crate::errors::{
17-
BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
18-
OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
18+
BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ConstContinueBadLabel,
19+
ContinueLabeledBlock, OutsideLoop, OutsideLoopSuggestion, UnlabeledCfInWhileCondition,
20+
UnlabeledInLabeledBlock,
1921
};
2022

2123
/// The context in which a block is encountered.
@@ -37,6 +39,11 @@ enum Context {
3739
AnonConst,
3840
/// E.g. `const { ... }`.
3941
ConstBlock,
42+
/// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
43+
LoopMatch {
44+
/// The label of the labeled block (not of the loop itself).
45+
labeled_block: Label,
46+
},
4047
}
4148

4249
#[derive(Clone)]
@@ -141,7 +148,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
141148
}
142149
}
143150
hir::ExprKind::Loop(ref b, _, source, _) => {
144-
self.with_context(Loop(source), |v| v.visit_block(b));
151+
let cx = match self.is_loop_match(e, b) {
152+
Some(labeled_block) => LoopMatch { labeled_block },
153+
None => Loop(source),
154+
};
155+
156+
self.with_context(cx, |v| v.visit_block(b));
145157
}
146158
hir::ExprKind::Closure(&hir::Closure {
147159
ref fn_decl, body, fn_decl_span, kind, ..
@@ -197,6 +209,24 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
197209
Err(hir::LoopIdError::UnresolvedLabel) => None,
198210
};
199211

212+
// A `#[const_continue]` must break to a block in a `#[loop_match]`.
213+
let attrs = self.tcx.hir_attrs(e.hir_id);
214+
if attrs.iter().any(|attr| attr.has_name(sym::const_continue)) {
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 !self.tcx.hir_attrs(e.hir_id).iter().any(|attr| attr.has_name(sym::loop_match)) {
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
}

compiler/rustc_middle/src/thir.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,14 @@ pub enum ExprKind<'tcx> {
378378
Loop {
379379
body: ExprId,
380380
},
381+
/// A `#[loop_match] loop { state = 'blk: { match state { ... } } }` expression.
382+
LoopMatch {
383+
/// The state variable that is updated, and also the scrutinee of the match.
384+
state: ExprId,
385+
region_scope: region::Scope,
386+
arms: Box<[ArmId]>,
387+
match_span: Span,
388+
},
381389
/// Special expression representing the `let` part of an `if let` or similar construct
382390
/// (including `if let` guards in match arms, and let-chains formed by `&&`).
383391
///
@@ -454,6 +462,11 @@ pub enum ExprKind<'tcx> {
454462
Continue {
455463
label: region::Scope,
456464
},
465+
/// A `#[const_continue] break` expression.
466+
ConstContinue {
467+
label: region::Scope,
468+
value: ExprId,
469+
},
457470
/// A `return` expression.
458471
Return {
459472
value: Option<ExprId>,

compiler/rustc_middle/src/thir/visit.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
8383
visitor.visit_pat(pat);
8484
}
8585
Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
86-
Match { scrutinee, ref arms, .. } => {
86+
LoopMatch { state: scrutinee, ref arms, .. } | Match { scrutinee, ref arms, .. } => {
8787
visitor.visit_expr(&visitor.thir()[scrutinee]);
8888
for &arm in &**arms {
8989
visitor.visit_arm(&visitor.thir()[arm]);
@@ -108,6 +108,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
108108
}
109109
}
110110
Continue { label: _ } => {}
111+
ConstContinue { value, label: _ } => visitor.visit_expr(&visitor.thir()[value]),
111112
Return { value } => {
112113
if let Some(value) = value {
113114
visitor.visit_expr(&visitor.thir()[value])

compiler/rustc_mir_build/messages.ftl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
8484
8585
mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable
8686
87+
mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
88+
.label = this value is too generic
89+
.note = the value must be a literal or a monomorphic const
90+
91+
mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
92+
93+
mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
94+
.label = this value must be a literal or a monomorphic const
95+
8796
mir_build_const_defined_here = constant defined here
8897
8998
mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns
@@ -212,6 +221,30 @@ mir_build_literal_in_range_out_of_bounds =
212221
literal out of range for `{$ty}`
213222
.label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}`
214223
224+
mir_build_loop_match_arm_with_guard =
225+
match arms that are part of a `#[loop_match]` cannot have guards
226+
227+
mir_build_loop_match_bad_rhs =
228+
this expression must be a single `match` wrapped in a labeled block
229+
230+
mir_build_loop_match_bad_statements =
231+
statements are not allowed in this position within a `#[loop_match]`
232+
233+
mir_build_loop_match_invalid_match =
234+
invalid match on `#[loop_match]` state
235+
.note = a local variable must be the scrutinee within a `#[loop_match]`
236+
237+
mir_build_loop_match_invalid_update =
238+
invalid update of the `#[loop_match]` state
239+
.label = the assignment must update this variable
240+
241+
mir_build_loop_match_missing_assignment =
242+
expected a single assignment expression
243+
244+
mir_build_loop_match_unsupported_type =
245+
this `#[loop_match]` state value has type `{$ty}`, which is not supported
246+
.note = only integers, floats, bool, char, and enums without fields are supported
247+
215248
mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
216249
lower range bound must be less than or equal to upper
217250
.label = lower bound larger than upper bound

compiler/rustc_mir_build/src/builder/expr/as_place.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,12 +565,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
565565
| ExprKind::Match { .. }
566566
| ExprKind::If { .. }
567567
| ExprKind::Loop { .. }
568+
| ExprKind::LoopMatch { .. }
568569
| ExprKind::Block { .. }
569570
| ExprKind::Let { .. }
570571
| ExprKind::Assign { .. }
571572
| ExprKind::AssignOp { .. }
572573
| ExprKind::Break { .. }
573574
| ExprKind::Continue { .. }
575+
| ExprKind::ConstContinue { .. }
574576
| ExprKind::Return { .. }
575577
| ExprKind::Become { .. }
576578
| ExprKind::Literal { .. }

compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
538538
| ExprKind::RawBorrow { .. }
539539
| ExprKind::Adt { .. }
540540
| ExprKind::Loop { .. }
541+
| ExprKind::LoopMatch { .. }
541542
| ExprKind::LogicalOp { .. }
542543
| ExprKind::Call { .. }
543544
| ExprKind::Field { .. }
@@ -548,6 +549,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
548549
| ExprKind::UpvarRef { .. }
549550
| ExprKind::Break { .. }
550551
| ExprKind::Continue { .. }
552+
| ExprKind::ConstContinue { .. }
551553
| ExprKind::Return { .. }
552554
| ExprKind::Become { .. }
553555
| ExprKind::InlineAsm { .. }

compiler/rustc_mir_build/src/builder/expr/category.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,11 @@ impl Category {
8383
| ExprKind::NamedConst { .. } => Some(Category::Constant),
8484

8585
ExprKind::Loop { .. }
86+
| ExprKind::LoopMatch { .. }
8687
| ExprKind::Block { .. }
8788
| ExprKind::Break { .. }
8889
| ExprKind::Continue { .. }
90+
| ExprKind::ConstContinue { .. }
8991
| ExprKind::Return { .. }
9092
| ExprKind::Become { .. } =>
9193
// FIXME(#27840) these probably want their own

0 commit comments

Comments
 (0)