Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add #[loop_match] for improved DFA codegen #138780

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
72d0703
Add `#[loop_match]` for improved DFA codegen
bjorn3 Feb 18, 2025
3c7722d
Apply suggestions from code review
folkertdev Mar 22, 2025
eb4ed17
add comments to tests
folkertdev Mar 22, 2025
f777efa
remove some comments that are now inaccurate
folkertdev Mar 22, 2025
1b93888
static pattern matching when lowering `#[const_continue]`
folkertdev Mar 21, 2025
5e73b2d
clarify an unreachable branch
folkertdev Mar 24, 2025
b8c5752
add error for unknown jump target
folkertdev Mar 24, 2025
044acdf
emit an error when a match arm has a guard
folkertdev Mar 24, 2025
fc982a2
store the span of the match expression, and use it for diagnostics
folkertdev Mar 24, 2025
5eb72b7
add comments
folkertdev Mar 24, 2025
3716bed
use `Size::truncate`
folkertdev Mar 28, 2025
74e482e
refactor `break_scope`
folkertdev Mar 28, 2025
5e59ca1
Handle drop in unwind paths for #[const_continue]
bjorn3 Apr 1, 2025
e19b5f1
fix comment
folkertdev Mar 28, 2025
3833aed
emit error when #[const_continue] jumps to an invalid label
folkertdev Mar 28, 2025
8524273
delay most error handing until later
folkertdev Mar 28, 2025
17f837e
Fix some comments
folkertdev Apr 1, 2025
271b4e0
fix docs for `ConstContinuableScope`
folkertdev Apr 1, 2025
70db07c
[WIP] Support const blocks in const_continue
bjorn3 Mar 25, 2025
c3a1340
Handle plain enum values
bjorn3 Mar 25, 2025
b94a8e2
refactor valtree logic
folkertdev Apr 2, 2025
fbc9f9b
error on unsupported state type
folkertdev Apr 2, 2025
e99d54a
remove an unwrap
folkertdev Apr 2, 2025
ba2f37b
use `span_bug` instead of `todo`
folkertdev Apr 2, 2025
157cc9f
simplify logic for when a #[loop_match] state type is valid
folkertdev Apr 3, 2025
7d88da4
fix rustc updating under our feet
folkertdev Apr 3, 2025
332e511
support boolean and character patterns
folkertdev Apr 4, 2025
043be33
Apply suggestions from code review
folkertdev Apr 6, 2025
bbe7547
support float patterns
folkertdev Apr 4, 2025
70b2f11
emit an error for constants that are too generic
folkertdev Apr 4, 2025
77558c8
simplify thir visitor
folkertdev Apr 8, 2025
78c76c3
add thir print test
folkertdev Apr 8, 2025
6adefc3
make the drop have a side-effect
folkertdev Apr 8, 2025
ac721f8
tweak a comment
folkertdev Apr 8, 2025
0654c0a
Add assertion to loop-match/drop-in-march-arm.rs
bjorn3 Apr 8, 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
13 changes: 13 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,19 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const),
),

// The `#[loop_match]` and `#[const_continue]` attributes are part of the
// lang experiment for RFC 3720 tracked in:
//
// - https://github.com/rust-lang/rust/issues/132306
gated!(
const_continue, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::No, loop_match, experimental!(const_continue)
),
gated!(
loop_match, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::No, loop_match, experimental!(loop_match)
),

// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,8 @@ declare_features! (
/// Allows using `#[link(kind = "link-arg", name = "...")]`
/// to pass custom arguments to the linker.
(unstable, link_arg_attribute, "1.76.0", Some(99427)),
/// Allows fused `loop`/`match` for direct intraprocedural jumps.
(incomplete, loop_match, "CURRENT_RUSTC_VERSION", Some(132306)),
/// Give access to additional metadata about declarative macro meta-variables.
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
/// Provides a way to concatenate identifiers using metavariable expressions.
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,14 @@ pub enum ExprKind<'tcx> {
Loop {
body: ExprId,
},
/// A `#[loop_match] loop { state = 'blk: { match state { ... } } }` expression.
LoopMatch {
/// The state variable that is updated, and also the scrutinee of the match.
state: ExprId,
region_scope: region::Scope,
arms: Box<[ArmId]>,
match_span: Span,
},
/// Special expression representing the `let` part of an `if let` or similar construct
/// (including `if let` guards in match arms, and let-chains formed by `&&`).
///
Expand Down Expand Up @@ -451,6 +459,11 @@ pub enum ExprKind<'tcx> {
Continue {
label: region::Scope,
},
/// A `#[const_continue] break` expression.
ConstContinue {
label: region::Scope,
value: ExprId,
},
/// A `return` expression.
Return {
value: Option<ExprId>,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/thir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
visitor.visit_pat(pat);
}
Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
Match { scrutinee, ref arms, .. } => {
LoopMatch { state: scrutinee, ref arms, .. } | Match { scrutinee, ref arms, .. } => {
visitor.visit_expr(&visitor.thir()[scrutinee]);
for &arm in &**arms {
visitor.visit_arm(&visitor.thir()[arm]);
Expand All @@ -104,6 +104,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
}
}
Continue { label: _ } => {}
ConstContinue { value, label: _ } => visitor.visit_expr(&visitor.thir()[value]),
Return { value } => {
if let Some(value) = value {
visitor.visit_expr(&visitor.thir()[value])
Expand Down
33 changes: 33 additions & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =

mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable

mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
.label = this value is too generic
.note = the value must be a literal or a monomorphic const

mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value

mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
.label = this value must be a literal or a monomorphic const

mir_build_const_defined_here = constant defined here

mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns
Expand Down Expand Up @@ -212,6 +221,30 @@ mir_build_literal_in_range_out_of_bounds =
literal out of range for `{$ty}`
.label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}`

mir_build_loop_match_arm_with_guard =
match arms that are part of a `#[loop_match]` cannot have guards

mir_build_loop_match_bad_rhs =
this expression must be a single `match` wrapped in a labeled block

mir_build_loop_match_bad_statements =
statements are not allowed in this position within a `#[loop_match]`

mir_build_loop_match_invalid_match =
invalid match on `#[loop_match]` state
.note = a local variable must be the scrutinee within a `#[loop_match]`

mir_build_loop_match_invalid_update =
invalid update of the `#[loop_match]` state
.label = the assignment must update this variable

mir_build_loop_match_missing_assignment =
expected a single assignment expression

mir_build_loop_match_unsupported_type =
this `#[loop_match]` state value has type `{$ty}`, which is not supported
.note = only integers, floats, bool, char, and enums without fields are supported

mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
lower range bound must be less than or equal to upper
.label = lower bound larger than upper bound
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/builder/expr/as_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,12 +562,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Match { .. }
| ExprKind::If { .. }
| ExprKind::Loop { .. }
| ExprKind::LoopMatch { .. }
| ExprKind::Block { .. }
| ExprKind::Let { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. }
| ExprKind::Literal { .. }
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::RawBorrow { .. }
| ExprKind::Adt { .. }
| ExprKind::Loop { .. }
| ExprKind::LoopMatch { .. }
| ExprKind::LogicalOp { .. }
| ExprKind::Call { .. }
| ExprKind::Field { .. }
Expand All @@ -548,6 +549,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::UpvarRef { .. }
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. }
| ExprKind::InlineAsm { .. }
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/builder/expr/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,11 @@ impl Category {
| ExprKind::NamedConst { .. } => Some(Category::Constant),

ExprKind::Loop { .. }
| ExprKind::LoopMatch { .. }
| ExprKind::Block { .. }
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. } =>
// FIXME(#27840) these probably want their own
Expand Down
122 changes: 120 additions & 2 deletions compiler/rustc_mir_build/src/builder/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
use rustc_middle::thir::*;
use rustc_middle::ty::{CanonicalUserTypeAnnotation, Ty};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc_span::DUMMY_SP;
use rustc_span::source_map::Spanned;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::{debug, instrument};

use crate::builder::expr::category::{Category, RvalueFunc};
use crate::builder::matches::DeclareLetBindings;
use crate::builder::matches::{DeclareLetBindings, HasMatchGuard};
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
use crate::errors::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};

impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, storing the result into `destination`, which
Expand Down Expand Up @@ -244,6 +245,122 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None
})
}
ExprKind::LoopMatch { state, region_scope, match_span, ref arms } => {
// Intuitively, this is a combination of a loop containing a labeled block
// containing a match.
//
// The only new bit here is that the lowering of the match is wrapped in a
// `in_const_continuable_scope`, which makes the match arms and their target basic
// block available to the lowering of `#[const_continue]`.

fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
match ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => true,
ty::Adt(adt_def, _) => match adt_def.adt_kind() {
ty::AdtKind::Struct | ty::AdtKind::Union => false,
ty::AdtKind::Enum => {
adt_def.variants().iter().all(|v| v.fields.is_empty())
}
},
_ => false,
}
}

let state_ty = this.thir.exprs[state].ty;
if !is_supported_loop_match_type(state_ty) {
let span = this.thir.exprs[state].span;
this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType { span, ty: state_ty })
}

let loop_block = this.cfg.start_new_block();

// Start the loop.
this.cfg.goto(block, source_info, loop_block);

this.in_breakable_scope(Some(loop_block), destination, expr_span, |this| {
// Logic for `loop`.
let mut body_block = this.cfg.start_new_block();
this.cfg.terminate(
loop_block,
source_info,
TerminatorKind::FalseUnwind {
real_target: body_block,
unwind: UnwindAction::Continue,
},
);
this.diverge_from(loop_block);

// Logic for `match`.
let scrutinee_place_builder =
unpack!(body_block = this.as_place_builder(body_block, state));
let scrutinee_span = this.thir.exprs[state].span;
let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);

let mut patterns = Vec::with_capacity(arms.len());
for &arm_id in arms.iter() {
let arm = &this.thir[arm_id];

if let Some(guard) = arm.guard {
let span = this.thir.exprs[guard].span;
this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
}

patterns.push((&*arm.pattern, HasMatchGuard::No));
}

// The `built_tree` maps match arms to their basic block (where control flow
// jumps to when a value matches the arm). This structure is stored so that a
// `#[const_continue]` can figure out what basic block to jump to.
let built_tree = this.lower_match_tree(
body_block,
scrutinee_span,
&scrutinee_place_builder,
match_start_span,
patterns,
false,
);

let state_place = scrutinee_place_builder.to_place(this);

// This is logic for the labeled block: a block is a drop scope, hence
// `in_scope`, and a labeled block can be broken out of with a `break 'label`,
// hence the `in_breakable_scope`.
//
// Then `in_const_continuable_scope` stores information for the lowering of
// `#[const_continue]`, and finally the match is lowered in the standard way.
unpack!(
body_block = this.in_scope(
(region_scope, source_info),
LintLevel::Inherited,
move |this| {
this.in_breakable_scope(None, state_place, expr_span, |this| {
Some(this.in_const_continuable_scope(
arms.clone(),
built_tree.clone(),
state_place,
expr_span,
|this| {
this.lower_match_arms(
destination,
scrutinee_place_builder,
scrutinee_span,
arms,
built_tree,
this.source_info(match_span),
)
},
))
})
}
)
);

this.cfg.goto(body_block, source_info, loop_block);

// Loops are only exited by `break` expressions.
None
})
}
ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
let fun = unpack!(block = this.as_local_operand(block, fun));
let args: Box<[_]> = args
Expand Down Expand Up @@ -601,6 +718,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}

ExprKind::Continue { .. }
| ExprKind::ConstContinue { .. }
| ExprKind::Break { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. } => {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/builder/expr/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::Break { label, value } => {
this.break_scope(block, value, BreakableTarget::Break(label), source_info)
}
ExprKind::ConstContinue { label, value } => {
this.break_const_continuable_scope(block, value, label, source_info)
}
ExprKind::Return { value } => {
this.break_scope(block, value, BreakableTarget::Return, source_info)
}
Expand Down
Loading
Loading