Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
97 changes: 67 additions & 30 deletions compiler/rustc_mir_build/src/builder/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use rustc_middle::thir::*;
use rustc_middle::ty::CanonicalUserTypeAnnotation;
use rustc_middle::ty::util::Discr;
use rustc_pattern_analysis::constructor::Constructor;
use rustc_pattern_analysis::rustc::DeconstructedPat;
use rustc_pattern_analysis::rustc::RustcPatCtxt;
use rustc_span::source_map::Spanned;
use tracing::{debug, instrument};
Expand Down Expand Up @@ -319,39 +320,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let mut arm_blocks = Vec::with_capacity(arms.len());
for &arm in arms {
let pat = &this.thir[arm].pattern;
let deconstructed_pat = cx.lower_pat(pat);

match deconstructed_pat.ctor() {
Constructor::Variant(variant_index) => {
let PatKind::Variant { adt_def, .. } = pat.kind else {
unreachable!()
};

let discr = adt_def
.discriminant_for_variant(this.tcx, *variant_index);

let block = this.cfg.start_new_block();
arm_blocks.push((*variant_index, discr, block, arm))
}
Constructor::IntRange(int_range) => {
assert!(int_range.is_singleton());

let bits = state_ty.primitive_size(this.tcx).bits();
let value = int_range.lo.as_finite_int(bits).unwrap();

let discr =
Discr { val: value, ty: **deconstructed_pat.ty() };

let block = this.cfg.start_new_block();
arm_blocks.push((VariantIdx::ZERO, discr, block, arm))
}
Constructor::Wildcard => {
otherwise = Some((this.cfg.start_new_block(), arm));
}
other => todo!("{:?}", other),
}
this.loop_match_patterns(
arm,
&cx.lower_pat(pat),
None,
&mut arm_blocks,
&mut otherwise,
);
}

// handle patterns like `None | None` or two different arms that
// have the same pattern.
//
// NOTE: why this works is a bit subtle: we always want to pick the
// first arm for a pattern, and because this is a stable sort that
// works out.
arm_blocks.sort_by_key(|(_, discr, _, _)| discr.val);
arm_blocks.dedup_by_key(|(_, discr, _, _)| discr.val);

// if we're matching on an enum, the discriminant order in the `SwitchInt`
// targets should match the order yielded by `AdtDef::discriminants`.
if state_ty.is_enum() {
Expand Down Expand Up @@ -400,6 +387,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.chain(otherwise);

for (mut block, arm) in it {
if this.cfg.block_data(block).terminator.is_some() {
continue; // this can occur with or-patterns
}

let empty_place = this.get_unit_temp();
unpack!(
block = this.expr_into_dest(
Expand Down Expand Up @@ -842,4 +833,50 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
_ => false,
}
}

fn loop_match_patterns(
&mut self,
arm_id: ArmId,
pat: &DeconstructedPat<'_, 'tcx>,
current_block: Option<BasicBlock>,
result: &mut Vec<(VariantIdx, Discr<'tcx>, BasicBlock, ArmId)>,
otherwise: &mut Option<(BasicBlock, ArmId)>,
) {
match pat.ctor() {
Constructor::Variant(variant_index) => {
let PatKind::Variant { adt_def, .. } = pat.data().kind else { unreachable!() };

let discr = adt_def.discriminant_for_variant(self.tcx, *variant_index);

let block = current_block.unwrap_or_else(|| self.cfg.start_new_block());
result.push((*variant_index, discr, block, arm_id));
}
Constructor::IntRange(int_range) => {
assert!(int_range.is_singleton());

let bits = pat.ty().primitive_size(self.tcx).bits();
let value = int_range.lo.as_finite_int(bits).unwrap();

let discr = Discr { val: value, ty: **pat.ty() };

let block = current_block.unwrap_or_else(|| self.cfg.start_new_block());
result.push((VariantIdx::ZERO, discr, block, arm_id));
}
Constructor::Wildcard => {
// the first wildcard wins
if otherwise.is_none() {
let block = current_block.unwrap_or_else(|| self.cfg.start_new_block());
*otherwise = Some((block, arm_id))
}
}
Constructor::Or => {
let block = current_block.unwrap_or_else(|| self.cfg.start_new_block());

for indexed in pat.iter_fields() {
self.loop_match_patterns(arm_id, &indexed.pat, Some(block), result, otherwise);
}
}
other => todo!("{:?}", other),
}
}
}
41 changes: 41 additions & 0 deletions tests/ui/match/loop-match-or-pattern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//@ run-pass

#![feature(loop_match)]

enum State {
A,
B,
C,
D,
}

fn main() {
let mut state = State::A;
#[loop_match]
'a: loop {
state = 'blk: {
match state {
State::A => {
if true {
#[const_continue]
break 'blk State::B;
} else {
#[const_continue]
break 'blk State::D;
}
}
State::B | State::D => {
if true {
#[const_continue]
break 'blk State::C;
} else {
// No drops allowed at this point
#[const_continue]
break 'blk State::A;
}
}
State::C => break 'a,
}
}
}
}
Loading