diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 40a881a82d7d4..8873dc8391243 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -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}; @@ -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() { @@ -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( @@ -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, + 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), + } + } } diff --git a/tests/ui/match/loop-match-or-pattern.rs b/tests/ui/match/loop-match-or-pattern.rs new file mode 100644 index 0000000000000..bdfb4ddf86a71 --- /dev/null +++ b/tests/ui/match/loop-match-or-pattern.rs @@ -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, + } + } + } +}