1//! Code related to match expressions. These are sufficiently complex to2//! warrant their own module and submodules. :) This main module includes the3//! high-level algorithm, the submodules contain the details.4//!5//! This also includes code for pattern bindings in `let` statements and6//! function parameters.78use std::borrow::Borrow;9use std::sync::Arc;10use std::{debug_assert_matches, mem};1112use itertools::{Itertools, Position};13use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};14use rustc_data_structures::fx::FxIndexMap;15use rustc_data_structures::stack::ensure_sufficient_stack;16use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node};17use rustc_middle::middle::region::{self, TempLifetime};18use rustc_middle::mir::*;19use rustc_middle::thir::{self, *};20use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};21use rustc_middle::{bug, span_bug};22use rustc_pattern_analysis::constructor::RangeEnd;23use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};24use rustc_span::{BytePos, Pos, Span, Symbol, sym};25use tracing::{debug, instrument};2627use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard};28use crate::builder::expr::as_place::PlaceBuilder;29use crate::builder::matches::buckets::PartitionedCandidates;30use crate::builder::matches::user_ty::ProjectedUserTypesNode;31use crate::builder::scope::{DropKind, LintLevel};32use crate::builder::{33 BlockAnd, BlockAndExtension, Builder, GuardFrame, GuardFrameLocal, LocalsForNode,34};3536// helper functions, broken out by category:37mod buckets;38mod match_pair;39mod test;40mod user_ty;41mod util;4243/// Arguments to [`Builder::then_else_break_inner`] that are usually forwarded44/// to recursive invocations.45#[derive(Clone, Copy)]46struct ThenElseArgs {47 /// Used as the temp scope for lowering `expr`. If absent (for match guards),48 /// `self.local_scope()` is used.49 temp_scope_override: Option<region::Scope>,50 variable_source_info: SourceInfo,51 /// Determines how bindings should be handled when lowering `let` expressions.52 ///53 /// Forwarded to [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`].54 declare_let_bindings: DeclareLetBindings,55}5657/// Should lowering a `let` expression also declare its bindings?58///59/// Used by [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`].60#[derive(Clone, Copy)]61pub(crate) enum DeclareLetBindings {62 /// Yes, declare `let` bindings as normal for `if` conditions.63 Yes,64 /// No, don't declare `let` bindings, because the caller declares them65 /// separately due to special requirements.66 ///67 /// Used for match guards and let-else.68 No,69 /// Let expressions are not permitted in this context, so it is a bug to70 /// try to lower one (e.g inside lazy-boolean-or or boolean-not).71 LetNotPermitted,72}7374/// Used by [`Builder::storage_live_binding`] and [`Builder::bind_matched_candidate_for_arm_body`]75/// to decide whether to schedule drops.76#[derive(Clone, Copy, Debug)]77pub(crate) enum ScheduleDrops {78 /// Yes, the relevant functions should also schedule drops as appropriate.79 Yes,80 /// No, don't schedule drops. The caller has taken responsibility for any81 /// appropriate drops.82 No,83}8485impl<'a, 'tcx> Builder<'a, 'tcx> {86 /// Lowers a condition in a way that ensures that variables bound in any let87 /// expressions are definitely initialized in the if body.88 ///89 /// If `declare_let_bindings` is false then variables created in `let`90 /// expressions will not be declared. This is for if let guards on arms with91 /// an or pattern, where the guard is lowered multiple times.92 pub(crate) fn then_else_break(93 &mut self,94 block: BasicBlock,95 expr_id: ExprId,96 temp_scope_override: Option<region::Scope>,97 variable_source_info: SourceInfo,98 declare_let_bindings: DeclareLetBindings,99 ) -> BlockAnd<()> {100 self.then_else_break_inner(101 block,102 expr_id,103 ThenElseArgs { temp_scope_override, variable_source_info, declare_let_bindings },104 )105 }106107 fn then_else_break_inner(108 &mut self,109 block: BasicBlock, // Block that the condition and branch will be lowered into110 expr_id: ExprId, // Condition expression to lower111 args: ThenElseArgs,112 ) -> BlockAnd<()> {113 let this = self; // See "LET_THIS_SELF".114 let expr = &this.thir[expr_id];115 let expr_span = expr.span;116117 match expr.kind {118 ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {119 let lhs_then_block = this.then_else_break_inner(block, lhs, args).into_block();120 let rhs_then_block =121 this.then_else_break_inner(lhs_then_block, rhs, args).into_block();122 rhs_then_block.unit()123 }124 ExprKind::LogicalOp { op: LogicalOp::Or, lhs, rhs } => {125 let local_scope = this.local_scope();126 let (lhs_success_block, failure_block) =127 this.in_if_then_scope(local_scope, expr_span, |this| {128 this.then_else_break_inner(129 block,130 lhs,131 ThenElseArgs {132 declare_let_bindings: DeclareLetBindings::LetNotPermitted,133 ..args134 },135 )136 });137 let rhs_success_block = this138 .then_else_break_inner(139 failure_block,140 rhs,141 ThenElseArgs {142 declare_let_bindings: DeclareLetBindings::LetNotPermitted,143 ..args144 },145 )146 .into_block();147148 // Make the LHS and RHS success arms converge to a common block.149 // (We can't just make LHS goto RHS, because `rhs_success_block`150 // might contain statements that we don't want on the LHS path.)151 let success_block = this.cfg.start_new_block();152 this.cfg.goto(lhs_success_block, args.variable_source_info, success_block);153 this.cfg.goto(rhs_success_block, args.variable_source_info, success_block);154 success_block.unit()155 }156 ExprKind::Unary { op: UnOp::Not, arg } => {157 // Improve branch coverage instrumentation by noting conditions158 // nested within one or more `!` expressions.159 // (Skipped if branch coverage is not enabled.)160 if let Some(coverage_info) = this.coverage_info.as_mut() {161 coverage_info.visit_unary_not(this.thir, expr_id);162 }163164 let local_scope = this.local_scope();165 let (success_block, failure_block) =166 this.in_if_then_scope(local_scope, expr_span, |this| {167 // Help out coverage instrumentation by injecting a dummy statement with168 // the original condition's span (including `!`). This fixes #115468.169 if this.tcx.sess.instrument_coverage() {170 this.cfg.push_coverage_span_marker(block, this.source_info(expr_span));171 }172 this.then_else_break_inner(173 block,174 arg,175 ThenElseArgs {176 declare_let_bindings: DeclareLetBindings::LetNotPermitted,177 ..args178 },179 )180 });181 this.break_for_else(success_block, args.variable_source_info);182 failure_block.unit()183 }184 ExprKind::Scope { region_scope, hir_id, value } => {185 let region_scope = (region_scope, this.source_info(expr_span));186 this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {187 this.then_else_break_inner(block, value, args)188 })189 }190 ExprKind::Use { source } => this.then_else_break_inner(block, source, args),191 ExprKind::Let { expr, ref pat } => this.lower_let_expr(192 block,193 expr,194 pat,195 Some(args.variable_source_info.scope),196 args.variable_source_info.span,197 args.declare_let_bindings,198 ),199 _ => {200 let mut block = block;201 let temp_scope = args.temp_scope_override.unwrap_or_else(|| this.local_scope());202 let mutability = Mutability::Mut;203204 let place = unpack!(205 block = this.as_temp(206 block,207 TempLifetime {208 temp_lifetime: Some(temp_scope),209 backwards_incompatible: None210 },211 expr_id,212 mutability213 )214 );215216 let operand = Operand::Move(Place::from(place));217218 let then_block = this.cfg.start_new_block();219 let else_block = this.cfg.start_new_block();220 let term = TerminatorKind::if_(operand, then_block, else_block);221222 // Record branch coverage info for this condition.223 // (Does nothing if branch coverage is not enabled.)224 this.visit_coverage_branch_condition(expr_id, then_block, else_block);225226 let source_info = this.source_info(expr_span);227 this.cfg.terminate(block, source_info, term);228 this.break_for_else(else_block, source_info);229230 then_block.unit()231 }232 }233 }234235 /// Generates MIR for a `match` expression.236 ///237 /// The MIR that we generate for a match looks like this.238 ///239 /// ```text240 /// [ 0. Pre-match ]241 /// |242 /// [ 1. Evaluate Scrutinee (expression being matched on) ]243 /// [ (PlaceMention of scrutinee) ]244 /// |245 /// [ 2. Decision tree -- check discriminants ] <--------+246 /// | |247 /// | (once a specific arm is chosen) |248 /// | |249 /// [pre_binding_block] [otherwise_block]250 /// | |251 /// [ 3. Create "guard bindings" for arm ] |252 /// [ (create fake borrows) ] |253 /// | |254 /// [ 4. Execute guard code ] |255 /// [ (read fake borrows) ] --(guard is false)-----------+256 /// |257 /// | (guard results in true)258 /// |259 /// [ 5. Create real bindings and execute arm ]260 /// |261 /// [ Exit match ]262 /// ```263 ///264 /// All of the different arms have been stacked on top of each other to265 /// simplify the diagram. For an arm with no guard the blocks marked 3 and266 /// 4 and the fake borrows are omitted.267 ///268 /// We generate MIR in the following steps:269 ///270 /// 1. Evaluate the scrutinee and add the PlaceMention of it ([Builder::lower_scrutinee]).271 /// 2. Create the decision tree ([Builder::lower_match_tree]).272 /// 3. Determine the fake borrows that are needed from the places that were273 /// matched against and create the required temporaries for them274 /// ([util::collect_fake_borrows]).275 /// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]).276 ///277 /// ## False edges278 ///279 /// We don't want to have the exact structure of the decision tree be visible through borrow280 /// checking. Specifically we want borrowck to think that:281 /// - at any point, any or none of the patterns and guards seen so far may have been tested;282 /// - after the match, any of the patterns may have matched.283 ///284 /// For example, all of these would fail to error if borrowck could see the real CFG (examples285 /// taken from `tests/ui/nll/match-cfg-fake-edges.rs`):286 /// ```ignore (too many errors, this is already in the test suite)287 /// let x = String::new();288 /// let _ = match true {289 /// _ => {},290 /// _ => drop(x),291 /// };292 /// // Borrowck must not know the second arm is never run.293 /// drop(x); //~ ERROR use of moved value294 ///295 /// let x;296 /// # let y = true;297 /// match y {298 /// _ if { x = 2; true } => {},299 /// // Borrowck must not know the guard is always run.300 /// _ => drop(x), //~ ERROR used binding `x` is possibly-uninitialized301 /// };302 ///303 /// let x = String::new();304 /// # let y = true;305 /// match y {306 /// false if { drop(x); true } => {},307 /// // Borrowck must not know the guard is not run in the `true` case.308 /// true => drop(x), //~ ERROR use of moved value: `x`309 /// false => {},310 /// };311 ///312 /// # let mut y = (true, true);313 /// let r = &mut y.1;314 /// match y {315 /// //~^ ERROR cannot use `y.1` because it was mutably borrowed316 /// (false, true) => {}317 /// // Borrowck must not know we don't test `y.1` when `y.0` is `true`.318 /// (true, _) => drop(r),319 /// (false, _) => {}320 /// };321 /// ```322 ///323 /// We add false edges to act as if we were naively matching each arm in order. What we need is324 /// a (fake) path from each candidate to the next, specifically from candidate C's pre-binding325 /// block to next candidate D's pre-binding block. For maximum precision (needed for deref326 /// patterns), we choose the earliest node on D's success path that doesn't also lead to C (to327 /// avoid loops).328 ///329 /// This turns out to be easy to compute: that block is the `start_block` of the first call to330 /// `match_candidates` where D is the first candidate in the list.331 ///332 /// For example:333 /// ```rust334 /// # let (x, y) = (true, true);335 /// match (x, y) {336 /// (true, true) => 1,337 /// (false, true) => 2,338 /// (true, false) => 3,339 /// _ => 4,340 /// }341 /// # ;342 /// ```343 /// In this example, the pre-binding block of arm 1 has a false edge to the block for result344 /// `false` of the first test on `x`. The other arms have false edges to the pre-binding blocks345 /// of the next arm.346 ///347 /// On top of this, we also add a false edge from the otherwise_block of each guard to the348 /// aforementioned start block of the next candidate, to ensure borrock doesn't rely on which349 /// guards may have run.350 #[instrument(level = "debug", skip(self, arms))]351 pub(crate) fn match_expr(352 &mut self,353 destination: Place<'tcx>,354 mut block: BasicBlock,355 scrutinee_id: ExprId,356 arms: &[ArmId],357 span: Span,358 scrutinee_span: Span,359 ) -> BlockAnd<()> {360 let scrutinee_place =361 unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span));362363 let match_start_span = span.shrink_to_lo().to(scrutinee_span);364 let patterns = arms365 .iter()366 .map(|&arm| {367 let arm = &self.thir[arm];368 let has_match_guard =369 if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No };370 (&*arm.pattern, has_match_guard)371 })372 .collect();373 let built_tree = self.lower_match_tree(374 block,375 scrutinee_span,376 &scrutinee_place,377 match_start_span,378 patterns,379 Exhaustive::Yes,380 );381382 self.lower_match_arms(383 destination,384 scrutinee_place,385 scrutinee_span,386 arms,387 built_tree,388 self.source_info(span),389 )390 }391392 /// Evaluate the scrutinee and add the PlaceMention for it.393 pub(crate) fn lower_scrutinee(394 &mut self,395 mut block: BasicBlock,396 scrutinee_id: ExprId,397 scrutinee_span: Span,398 ) -> BlockAnd<PlaceBuilder<'tcx>> {399 let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee_id));400 if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {401 let source_info = self.source_info(scrutinee_span);402 self.cfg.push_place_mention(block, source_info, scrutinee_place);403 }404405 block.and(scrutinee_place_builder)406 }407408 /// Lower the bindings, guards and arm bodies of a `match` expression.409 ///410 /// The decision tree should have already been created411 /// (by [Builder::lower_match_tree]).412 ///413 /// `outer_source_info` is the SourceInfo for the whole match.414 pub(crate) fn lower_match_arms(415 &mut self,416 destination: Place<'tcx>,417 scrutinee_place_builder: PlaceBuilder<'tcx>,418 scrutinee_span: Span,419 arms: &[ArmId],420 built_match_tree: BuiltMatchTree<'tcx>,421 outer_source_info: SourceInfo,422 ) -> BlockAnd<()> {423 let arm_end_blocks: Vec<BasicBlock> = arms424 .iter()425 .map(|&arm| &self.thir[arm])426 .zip(built_match_tree.branches)427 .map(|(arm, branch)| {428 debug!("lowering arm {:?}\ncorresponding branch = {:?}", arm, branch);429430 let arm_source_info = self.source_info(arm.span);431 let arm_scope = (arm.scope, arm_source_info);432 let match_scope = self.local_scope();433 let guard_scope = arm434 .guard435 .map(|_| region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope });436 self.in_scope(arm_scope, LintLevel::Explicit(arm.hir_id), |this| {437 this.opt_in_scope(guard_scope.map(|scope| (scope, arm_source_info)), |this| {438 // `if let` guard temps needing deduplicating will be in the guard scope.439 let old_dedup_scope =440 mem::replace(&mut this.fixed_temps_scope, guard_scope);441442 // `try_to_place` may fail if it is unable to resolve the given443 // `PlaceBuilder` inside a closure. In this case, we don't want to include444 // a scrutinee place. `scrutinee_place_builder` will fail to be resolved445 // if the only match arm is a wildcard (`_`).446 // Example:447 // ```448 // let foo = (0, 1);449 // let c = || {450 // match foo { _ => () };451 // };452 // ```453 let scrutinee_place = scrutinee_place_builder.try_to_place(this);454 let opt_scrutinee_place =455 scrutinee_place.as_ref().map(|place| (Some(place), scrutinee_span));456 let scope = this.declare_bindings(457 None,458 arm.span,459 &arm.pattern,460 arm.guard,461 opt_scrutinee_place,462 );463464 let arm_block = this.bind_pattern(465 outer_source_info,466 branch,467 &built_match_tree.fake_borrow_temps,468 scrutinee_span,469 Some((arm, match_scope)),470 );471472 this.fixed_temps_scope = old_dedup_scope;473474 if let Some(source_scope) = scope {475 this.source_scope = source_scope;476 }477478 this.expr_into_dest(destination, arm_block, arm.body)479 })480 })481 .into_block()482 })483 .collect();484485 // all the arm blocks will rejoin here486 let end_block = self.cfg.start_new_block();487488 let end_brace = self.source_info(489 outer_source_info.span.with_lo(outer_source_info.span.hi() - BytePos::from_usize(1)),490 );491 for arm_block in arm_end_blocks {492 let block = &self.cfg.basic_blocks[arm_block];493 let last_location = block.statements.last().map(|s| s.source_info);494495 self.cfg.goto(arm_block, last_location.unwrap_or(end_brace), end_block);496 }497498 self.source_scope = outer_source_info.scope;499500 end_block.unit()501 }502503 /// For a top-level `match` arm or a `let` binding, binds the variables and504 /// ascribes types, and also checks the match arm guard (if present).505 ///506 /// `arm_scope` should be `Some` if and only if this is called for a507 /// `match` arm.508 ///509 /// In the presence of or-patterns, a match arm might have multiple510 /// sub-branches representing different ways to match, with each sub-branch511 /// requiring its own bindings and its own copy of the guard. This method512 /// handles those sub-branches individually, and then has them jump together513 /// to a common block.514 ///515 /// Returns a single block that the match arm can be lowered into.516 /// (For `let` bindings, this is the code that can use the bindings.)517 fn bind_pattern(518 &mut self,519 outer_source_info: SourceInfo,520 branch: MatchTreeBranch<'tcx>,521 fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],522 scrutinee_span: Span,523 arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,524 ) -> BasicBlock {525 if branch.sub_branches.len() == 1 {526 let [sub_branch] = branch.sub_branches.try_into().unwrap();527 // Avoid generating another `BasicBlock` when we only have one sub branch.528 self.bind_and_guard_matched_candidate(529 sub_branch,530 fake_borrow_temps,531 scrutinee_span,532 arm_match_scope,533 ScheduleDrops::Yes,534 )535 } else {536 // It's helpful to avoid scheduling drops multiple times to save537 // drop elaboration from having to clean up the extra drops.538 //539 // If we are in a `let` then we only schedule drops for the first540 // candidate.541 //542 // If we're in a `match` arm then we could have a case like so:543 //544 // Ok(x) | Err(x) if return => { /* ... */ }545 //546 // In this case we don't want a drop of `x` scheduled when we547 // return: it isn't bound by move until right before enter the arm.548 // To handle this we instead unschedule it's drop after each time549 // we lower the guard.550 // As a result, we end up with the drop order of the last sub-branch we lower. To use551 // the drop order for the first sub-branch, we lower sub-branches in reverse (#142163).552 let target_block = self.cfg.start_new_block();553 for (pos, sub_branch) in branch.sub_branches.into_iter().rev().with_position() {554 debug_assert!(pos != Position::Only);555 let schedule_drops =556 if pos == Position::Last { ScheduleDrops::Yes } else { ScheduleDrops::No };557 let binding_end = self.bind_and_guard_matched_candidate(558 sub_branch,559 fake_borrow_temps,560 scrutinee_span,561 arm_match_scope,562 schedule_drops,563 );564 self.cfg.goto(binding_end, outer_source_info, target_block);565 }566567 target_block568 }569 }570571 pub(super) fn expr_into_pattern(572 &mut self,573 mut block: BasicBlock,574 irrefutable_pat: &Pat<'tcx>,575 initializer_id: ExprId,576 ) -> BlockAnd<()> {577 match irrefutable_pat.kind {578 // Optimize `let x = ...` and `let x: T = ...` to write directly into `x`,579 // and then require that `T == typeof(x)` if present.580 PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => {581 let place = self.storage_live_binding(582 block,583 var,584 irrefutable_pat.span,585 false,586 OutsideGuard,587 ScheduleDrops::Yes,588 );589 block = self.expr_into_dest(place, block, initializer_id).into_block();590591 // Inject a fake read, see comments on `FakeReadCause::ForLet`.592 let source_info = self.source_info(irrefutable_pat.span);593 self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);594595 let ascriptions: &[_] =596 try { irrefutable_pat.extra.as_deref()?.ascriptions.as_slice() }597 .unwrap_or_default();598 for thir::Ascription { annotation, variance: _ } in ascriptions {599 let ty_source_info = self.source_info(annotation.span);600601 let base = self.canonical_user_type_annotations.push(annotation.clone());602 let stmt = Statement::new(603 ty_source_info,604 StatementKind::AscribeUserType(605 Box::new((place, UserTypeProjection { base, projs: Vec::new() })),606 // We always use invariant as the variance here. This is because the607 // variance field from the ascription refers to the variance to use608 // when applying the type to the value being matched, but this609 // ascription applies rather to the type of the binding. e.g., in this610 // example:611 //612 // ```613 // let x: T = <expr>614 // ```615 //616 // We are creating an ascription that defines the type of `x` to be617 // exactly `T` (i.e., with invariance). The variance field, in618 // contrast, is intended to be used to relate `T` to the type of619 // `<expr>`.620 ty::Invariant,621 ),622 );623 self.cfg.push(block, stmt);624 }625626 self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);627 block.unit()628 }629630 _ => {631 let initializer = &self.thir[initializer_id];632 let place_builder =633 unpack!(block = self.lower_scrutinee(block, initializer_id, initializer.span));634 self.place_into_pattern(block, irrefutable_pat, place_builder, true)635 }636 }637 }638639 pub(crate) fn place_into_pattern(640 &mut self,641 block: BasicBlock,642 irrefutable_pat: &Pat<'tcx>,643 initializer: PlaceBuilder<'tcx>,644 set_match_place: bool,645 ) -> BlockAnd<()> {646 let built_tree = self.lower_match_tree(647 block,648 irrefutable_pat.span,649 &initializer,650 irrefutable_pat.span,651 vec![(irrefutable_pat, HasMatchGuard::No)],652 Exhaustive::Yes,653 );654 let [branch] = built_tree.branches.try_into().unwrap();655656 // For matches and function arguments, the place that is being matched657 // can be set when creating the variables. But the place for658 // let PATTERN = ... might not even exist until we do the assignment.659 // so we set it here instead.660 if set_match_place {661 // `try_to_place` may fail if it is unable to resolve the given `PlaceBuilder` inside a662 // closure. In this case, we don't want to include a scrutinee place.663 // `scrutinee_place_builder` will fail for destructured assignments. This is because a664 // closure only captures the precise places that it will read and as a result a closure665 // may not capture the entire tuple/struct and rather have individual places that will666 // be read in the final MIR.667 // Example:668 // ```669 // let foo = (0, 1);670 // let c = || {671 // let (v1, v2) = foo;672 // };673 // ```674 if let Some(place) = initializer.try_to_place(self) {675 // Because or-alternatives bind the same variables, we only explore the first one.676 let first_sub_branch = branch.sub_branches.first().unwrap();677 for binding in &first_sub_branch.bindings {678 let local = self.var_local_id(binding.var_id, OutsideGuard);679 if let LocalInfo::User(BindingForm::Var(VarBindingForm {680 opt_match_place: Some((ref mut match_place, _)),681 ..682 })) = **self.local_decls[local].local_info.as_mut().unwrap_crate_local()683 {684 *match_place = Some(place);685 } else {686 bug!("Let binding to non-user variable.")687 };688 }689 }690 }691692 self.bind_pattern(693 self.source_info(irrefutable_pat.span),694 branch,695 &[],696 irrefutable_pat.span,697 None,698 )699 .unit()700 }701702 /// Declares the bindings of the given patterns and returns the visibility703 /// scope for the bindings in these patterns, if such a scope had to be704 /// created. NOTE: Declaring the bindings should always be done in their705 /// drop scope.706 #[instrument(skip(self), level = "debug")]707 pub(crate) fn declare_bindings(708 &mut self,709 mut visibility_scope: Option<SourceScope>,710 scope_span: Span,711 pattern: &Pat<'tcx>,712 guard: Option<ExprId>,713 opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,714 ) -> Option<SourceScope> {715 self.visit_primary_bindings_special(716 pattern,717 &ProjectedUserTypesNode::None,718 &mut |this, name, mode, var, span, ty, user_tys| {719 let saved_scope = this.source_scope;720 this.set_correct_source_scope_for_arg(var.0, saved_scope, span);721 let vis_scope = *visibility_scope722 .get_or_insert_with(|| this.new_source_scope(scope_span, LintLevel::Inherited));723 let source_info = SourceInfo { span, scope: this.source_scope };724 let user_tys = user_tys.build_user_type_projections();725726 this.declare_binding(727 source_info,728 vis_scope,729 name,730 mode,731 var,732 ty,733 user_tys,734 ArmHasGuard(guard.is_some()),735 opt_match_place.map(|(x, y)| (x.cloned(), y)),736 pattern.span,737 );738 this.source_scope = saved_scope;739 },740 );741 if let Some(guard_expr) = guard {742 self.declare_guard_bindings(guard_expr, scope_span, visibility_scope);743 }744 visibility_scope745 }746747 /// Declare bindings in a guard. This has to be done when declaring bindings748 /// for an arm to ensure that or patterns only have one version of each749 /// variable.750 pub(crate) fn declare_guard_bindings(751 &mut self,752 guard_expr: ExprId,753 scope_span: Span,754 visibility_scope: Option<SourceScope>,755 ) {756 match self.thir.exprs[guard_expr].kind {757 ExprKind::Let { expr: _, pat: ref guard_pat } => {758 // FIXME: pass a proper `opt_match_place`759 self.declare_bindings(visibility_scope, scope_span, guard_pat, None, None);760 }761 ExprKind::Scope { value, .. } => {762 self.declare_guard_bindings(value, scope_span, visibility_scope);763 }764 ExprKind::Use { source } => {765 self.declare_guard_bindings(source, scope_span, visibility_scope);766 }767 ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {768 self.declare_guard_bindings(lhs, scope_span, visibility_scope);769 self.declare_guard_bindings(rhs, scope_span, visibility_scope);770 }771 _ => {}772 }773 }774775 /// Emits a [`StatementKind::StorageLive`] for the given var, and also776 /// schedules a drop if requested (and possible).777 pub(crate) fn storage_live_binding(778 &mut self,779 block: BasicBlock,780 var: LocalVarId,781 span: Span,782 is_shorthand: bool,783 for_guard: ForGuard,784 schedule_drop: ScheduleDrops,785 ) -> Place<'tcx> {786 let local_id = self.var_local_id(var, for_guard);787 let source_info = self.source_info(span);788 self.cfg.push(block, Statement::new(source_info, StatementKind::StorageLive(local_id)));789 // Although there is almost always scope for given variable in corner cases790 // like #92893 we might get variable with no scope.791 if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id)792 && matches!(schedule_drop, ScheduleDrops::Yes)793 {794 self.schedule_drop(span, region_scope, local_id, DropKind::Storage);795 }796 let local_info = self.local_decls[local_id].local_info.as_mut().unwrap_crate_local();797 if let LocalInfo::User(BindingForm::Var(var_info)) = &mut **local_info {798 var_info.introductions.push(VarBindingIntroduction { span, is_shorthand });799 }800 Place::from(local_id)801 }802803 pub(crate) fn schedule_drop_for_binding(804 &mut self,805 var: LocalVarId,806 span: Span,807 for_guard: ForGuard,808 ) {809 let local_id = self.var_local_id(var, for_guard);810 if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) {811 self.schedule_drop(span, region_scope, local_id, DropKind::Value);812 }813 }814815 /// Visits all of the "primary" bindings in a pattern, i.e. the leftmost816 /// occurrence of each variable bound by the pattern.817 /// See [`PatKind::Binding::is_primary`] for more context.818 ///819 /// This variant provides only the limited subset of binding data needed820 /// by its callers, and should be a "pure" visit without side-effects.821 pub(super) fn visit_primary_bindings(822 &mut self,823 pattern: &Pat<'tcx>,824 f: &mut impl FnMut(&mut Self, LocalVarId, Span),825 ) {826 pattern.walk_always(|pat| {827 if let PatKind::Binding { var, is_primary: true, .. } = pat.kind {828 f(self, var, pat.span);829 }830 })831 }832833 /// Visits all of the "primary" bindings in a pattern, while preparing834 /// additional user-type-annotation data needed by `declare_bindings`.835 ///836 /// This also has the side-effect of pushing all user type annotations837 /// onto `canonical_user_type_annotations`, so that they end up in MIR838 /// even if they aren't associated with any bindings.839 #[instrument(level = "debug", skip(self, f))]840 fn visit_primary_bindings_special(841 &mut self,842 pattern: &Pat<'tcx>,843 user_tys: &ProjectedUserTypesNode<'_>,844 f: &mut impl FnMut(845 &mut Self,846 Symbol,847 BindingMode,848 LocalVarId,849 Span,850 Ty<'tcx>,851 &ProjectedUserTypesNode<'_>,852 ),853 ) {854 // Ascriptions correspond to user-written types like `let A::<'a>(_): A<'static> = ...;`.855 //856 // Caution: Pushing user types here is load-bearing even for857 // patterns containing no bindings, to ensure that the type ends858 // up represented in MIR _somewhere_.859 let user_tys = match pattern.extra.as_deref() {860 Some(PatExtra { ascriptions, .. }) if !ascriptions.is_empty() => {861 let base_user_tys = ascriptions862 .iter()863 .map(|thir::Ascription { annotation, variance: _ }| {864 // Note that the variance doesn't apply here, as we are tracking the effect865 // of user types on any bindings contained with subpattern.866 self.canonical_user_type_annotations.push(annotation.clone())867 })868 .collect();869 &user_tys.push_user_types(base_user_tys)870 }871 _ => user_tys,872 };873874 // Avoid having to write the full method name at each recursive call.875 let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| {876 this.visit_primary_bindings_special(subpat, user_tys, f)877 };878879 match pattern.kind {880 PatKind::Binding { name, mode, var, ty, ref subpattern, is_primary, .. } => {881 if is_primary {882 f(self, name, mode, var, pattern.span, ty, user_tys);883 }884 if let Some(subpattern) = subpattern.as_ref() {885 visit_subpat(self, subpattern, user_tys, f);886 }887 }888889 PatKind::Array { ref prefix, ref slice, ref suffix }890 | PatKind::Slice { ref prefix, ref slice, ref suffix } => {891 let from = u64::try_from(prefix.len()).unwrap();892 let to = u64::try_from(suffix.len()).unwrap();893 for subpattern in prefix.iter() {894 visit_subpat(self, subpattern, &user_tys.index(), f);895 }896 if let Some(subpattern) = slice {897 visit_subpat(self, subpattern, &user_tys.subslice(from, to), f);898 }899 for subpattern in suffix.iter() {900 visit_subpat(self, subpattern, &user_tys.index(), f);901 }902 }903904 PatKind::Constant { .. }905 | PatKind::Range { .. }906 | PatKind::Missing907 | PatKind::Wild908 | PatKind::Never909 | PatKind::Error(_) => {}910911 PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => {912 // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`.913 visit_subpat(self, subpattern, &user_tys.leaf(FieldIdx::ZERO).deref(), f);914 }915 PatKind::Deref { pin: Pinnedness::Not, ref subpattern } => {916 visit_subpat(self, subpattern, &user_tys.deref(), f);917 }918919 PatKind::DerefPattern { ref subpattern, .. } => {920 visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f);921 }922923 PatKind::Leaf { ref subpatterns } => {924 for subpattern in subpatterns {925 let subpattern_user_tys = user_tys.leaf(subpattern.field);926 debug!("visit_primary_bindings: subpattern_user_tys={subpattern_user_tys:?}");927 visit_subpat(self, &subpattern.pattern, &subpattern_user_tys, f);928 }929 }930931 PatKind::Variant { adt_def, args: _, variant_index, ref subpatterns } => {932 for subpattern in subpatterns {933 let subpattern_user_tys =934 user_tys.variant(adt_def, variant_index, subpattern.field);935 visit_subpat(self, &subpattern.pattern, &subpattern_user_tys, f);936 }937 }938 PatKind::Or { ref pats } => {939 // In cases where we recover from errors the primary bindings940 // may not all be in the leftmost subpattern. For example in941 // `let (x | y) = ...`, the primary binding of `y` occurs in942 // the right subpattern943 for subpattern in pats.iter() {944 visit_subpat(self, subpattern, user_tys, f);945 }946 }947 PatKind::Guard { ref subpattern, .. } => {948 visit_subpat(self, subpattern, user_tys, f);949 }950 }951 }952}953954/// Data extracted from a pattern that doesn't affect which branch is taken. Collected during955/// pattern simplification and not mutated later.956#[derive(Debug, Clone)]957struct PatternExtraData<'tcx> {958 /// [`Span`] of the original pattern.959 span: Span,960961 /// Bindings that must be established.962 bindings: Vec<SubpatternBindings<'tcx>>,963964 /// Types that must be asserted.965 ascriptions: Vec<Ascription<'tcx>>,966967 /// Whether this corresponds to a never pattern.968 is_never: bool,969}970971impl<'tcx> PatternExtraData<'tcx> {972 fn is_empty(&self) -> bool {973 self.bindings.is_empty() && self.ascriptions.is_empty()974 }975}976977#[derive(Debug, Clone)]978enum SubpatternBindings<'tcx> {979 /// A single binding.980 One(Binding<'tcx>),981 /// Holds the place for an or-pattern's bindings. This ensures their drops are scheduled in the982 /// order the primary bindings appear. See rust-lang/rust#142163 for more information.983 FromOrPattern,984}985986/// A pattern in a form suitable for lowering the match tree, with all irrefutable987/// patterns simplified away.988///989/// Here, "flat" indicates that irrefutable nodes in the pattern tree have been990/// recursively replaced with their refutable subpatterns. They are not991/// necessarily flat in an absolute sense.992///993/// Will typically be incorporated into a [`Candidate`].994#[derive(Debug, Clone)]995struct FlatPat<'tcx> {996 /// To match the pattern, all of these must be satisfied...997 match_pairs: Vec<MatchPairTree<'tcx>>,998999 extra_data: PatternExtraData<'tcx>,1000}10011002/// Candidates are a generalization of (a) top-level match arms, and1003/// (b) sub-branches of or-patterns, allowing the match-lowering process to handle1004/// them both in a mostly-uniform way. For example, the list of candidates passed1005/// to [`Builder::match_candidates`] will often contain a mixture of top-level1006/// candidates and or-pattern subcandidates.1007///1008/// At the start of match lowering, there is one candidate for each match arm.1009/// During match lowering, arms with or-patterns will be expanded into a tree1010/// of candidates, where each "leaf" candidate represents one of the ways for1011/// the arm pattern to successfully match.1012#[derive(Debug)]1013struct Candidate<'tcx> {1014 /// For the candidate to match, all of these must be satisfied...1015 ///1016 /// ---1017 /// Initially contains a list of match pairs created by [`FlatPat`], but is1018 /// subsequently mutated (in a queue-like way) while lowering the match tree.1019 /// When this list becomes empty, the candidate is fully matched and becomes1020 /// a leaf (see [`Builder::select_matched_candidate`]).1021 ///1022 /// Key mutations include:1023 ///1024 /// - When a match pair is fully satisfied by a test, it is removed from the1025 /// list, and its subpairs are added instead (see [`Builder::choose_bucket_for_candidate`]).1026 /// - During or-pattern expansion, any leading or-pattern is removed, and is1027 /// converted into subcandidates (see [`Builder::expand_and_match_or_candidates`]).1028 /// - After a candidate's subcandidates have been lowered, a copy of any remaining1029 /// or-patterns is added to each leaf subcandidate1030 /// (see [`Builder::test_remaining_match_pairs_after_or`]).1031 ///1032 /// Invariants:1033 /// - All or-patterns ([`TestableCase::Or`]) have been sorted to the end.1034 match_pairs: Vec<MatchPairTree<'tcx>>,10351036 /// ...and if this is non-empty, one of these subcandidates also has to match...1037 ///1038 /// ---1039 /// Initially a candidate has no subcandidates; they are added (and then immediately1040 /// lowered) during or-pattern expansion. Their main function is to serve as _output_1041 /// of match tree lowering, allowing later steps to see the leaf candidates that1042 /// represent a match of the entire match arm.1043 ///1044 /// A candidate no subcandidates is either incomplete (if it has match pairs left),1045 /// or is a leaf in the match tree. A candidate with one or more subcandidates is1046 /// an internal node in the match tree.1047 ///1048 /// Invariant: at the end of match tree lowering, this must not contain an1049 /// `is_never` candidate, because that would break binding consistency.1050 /// - See [`Builder::remove_never_subcandidates`].1051 subcandidates: Vec<Candidate<'tcx>>,10521053 /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`.1054 ///1055 /// ---1056 /// For subcandidates, this is copied from the parent candidate, so it indicates1057 /// whether the enclosing match arm has a guard.1058 has_guard: bool,10591060 /// Holds extra pattern data that was prepared by [`FlatPat`], including bindings and1061 /// ascriptions that must be established if this candidate succeeds.1062 extra_data: PatternExtraData<'tcx>,10631064 /// When setting `self.subcandidates`, we store here the span of the or-pattern they came from.1065 ///1066 /// ---1067 /// Invariant: it is `None` iff `subcandidates.is_empty()`.1068 /// - FIXME: We sometimes don't unset this when clearing `subcandidates`.1069 or_span: Option<Span>,10701071 /// The block before the `bindings` have been established.1072 ///1073 /// After the match tree has been lowered, [`Builder::lower_match_arms`]1074 /// will use this as the start point for lowering bindings and guards, and1075 /// then jump to a shared block containing the arm body.1076 pre_binding_block: Option<BasicBlock>,10771078 /// The block to branch to if the guard or a nested candidate fails to match.1079 otherwise_block: Option<BasicBlock>,10801081 /// The earliest block that has only candidates >= this one as descendents. Used for false1082 /// edges, see the doc for [`Builder::match_expr`].1083 false_edge_start_block: Option<BasicBlock>,1084}10851086impl<'tcx> Candidate<'tcx> {1087 fn new(1088 place: PlaceBuilder<'tcx>,1089 pattern: &Pat<'tcx>,1090 has_guard: HasMatchGuard,1091 cx: &mut Builder<'_, 'tcx>,1092 ) -> Self {1093 // Use `FlatPat` to build simplified match pairs, then immediately1094 // incorporate them into a new candidate.1095 Self::from_flat_pat(1096 FlatPat::new(place, pattern, cx),1097 matches!(has_guard, HasMatchGuard::Yes),1098 )1099 }11001101 /// Incorporates an already-simplified [`FlatPat`] into a new candidate.1102 fn from_flat_pat(flat_pat: FlatPat<'tcx>, has_guard: bool) -> Self {1103 let mut this = Candidate {1104 match_pairs: flat_pat.match_pairs,1105 extra_data: flat_pat.extra_data,1106 has_guard,1107 subcandidates: Vec::new(),1108 or_span: None,1109 otherwise_block: None,1110 pre_binding_block: None,1111 false_edge_start_block: None,1112 };1113 this.sort_match_pairs();1114 this1115 }11161117 /// Restores the invariant that or-patterns must be sorted to the end.1118 fn sort_match_pairs(&mut self) {1119 self.match_pairs.sort_by_key(|pair| matches!(pair.testable_case, TestableCase::Or { .. }));1120 }11211122 /// Returns whether the first match pair of this candidate is an or-pattern.1123 fn starts_with_or_pattern(&self) -> bool {1124 matches!(1125 &*self.match_pairs,1126 [MatchPairTree { testable_case: TestableCase::Or { .. }, .. }, ..]1127 )1128 }11291130 /// Visit the leaf candidates (those with no subcandidates) contained in1131 /// this candidate.1132 fn visit_leaves<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) {1133 traverse_candidate(1134 self,1135 &mut (),1136 &mut move |c, _| visit_leaf(c),1137 move |c, _| c.subcandidates.iter_mut(),1138 |_| {},1139 );1140 }11411142 /// Visit the leaf candidates in reverse order.1143 fn visit_leaves_rev<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) {1144 traverse_candidate(1145 self,1146 &mut (),1147 &mut move |c, _| visit_leaf(c),1148 move |c, _| c.subcandidates.iter_mut().rev(),1149 |_| {},1150 );1151 }1152}11531154/// A depth-first traversal of the `Candidate` and all of its recursive1155/// subcandidates.1156///1157/// This signature is very generic, to support traversing candidate trees by1158/// reference or by value, and to allow a mutable "context" to be shared by the1159/// traversal callbacks. Most traversals can use the simpler1160/// [`Candidate::visit_leaves`] wrapper instead.1161fn traverse_candidate<'tcx, C, T, I>(1162 candidate: C,1163 context: &mut T,1164 // Called when visiting a "leaf" candidate (with no subcandidates).1165 visit_leaf: &mut impl FnMut(C, &mut T),1166 // Called when visiting a "node" candidate (with one or more subcandidates).1167 // Returns an iterator over the candidate's children (by value or reference).1168 // Can perform setup before visiting the node's children.1169 get_children: impl Copy + Fn(C, &mut T) -> I,1170 // Called after visiting a "node" candidate's children.1171 complete_children: impl Copy + Fn(&mut T),1172) where1173 C: Borrow<Candidate<'tcx>>, // Typically `Candidate` or `&mut Candidate`1174 I: Iterator<Item = C>,1175{1176 if candidate.borrow().subcandidates.is_empty() {1177 visit_leaf(candidate, context)1178 } else {1179 for child in get_children(candidate, context) {1180 traverse_candidate(child, context, visit_leaf, get_children, complete_children);1181 }1182 complete_children(context)1183 }1184}11851186#[derive(Clone, Copy, Debug)]1187struct Binding<'tcx> {1188 span: Span,1189 source: Place<'tcx>,1190 var_id: LocalVarId,1191 binding_mode: BindingMode,1192 is_shorthand: bool,1193}11941195/// Indicates that the type of `source` must be a subtype of the1196/// user-given type `user_ty`; this is basically a no-op but can1197/// influence region inference.1198#[derive(Clone, Debug)]1199struct Ascription<'tcx> {1200 source: Place<'tcx>,1201 annotation: CanonicalUserTypeAnnotation<'tcx>,1202 variance: ty::Variance,1203}12041205/// Partial summary of a [`thir::Pat`], indicating what sort of test should be1206/// performed to match/reject the pattern, and what the desired test outcome is.1207/// This avoids having to perform a full match on [`thir::PatKind`] in some places,1208/// and helps [`TestKind::Switch`] and [`TestKind::SwitchInt`] know what target1209/// values to use.1210///1211/// Created by [`MatchPairTree`], and then inspected primarily by:1212/// - [`Builder::pick_test_for_match_pair`] (to choose a test)1213/// - [`Builder::choose_bucket_for_candidate`] (to see how the test interacts with a match pair)1214///1215/// Note that or-patterns are not tested directly like the other variants.1216/// Instead they participate in or-pattern expansion, where they are transformed into1217/// subcandidates. See [`Builder::expand_and_match_or_candidates`].1218#[derive(Debug, Clone)]1219enum TestableCase<'tcx> {1220 Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },1221 Constant { value: ty::Value<'tcx>, kind: PatConstKind },1222 Range(Arc<PatRange<'tcx>>),1223 Slice { len: u64, op: SliceLenOp },1224 Deref { temp: Place<'tcx>, mutability: Mutability },1225 Never,1226 Or { pats: Box<[FlatPat<'tcx>]> },1227}12281229impl<'tcx> TestableCase<'tcx> {1230 fn as_range(&self) -> Option<&PatRange<'tcx>> {1231 if let Self::Range(v) = self { Some(v.as_ref()) } else { None }1232 }1233}12341235/// Sub-classification of [`TestableCase::Constant`], which helps to avoid1236/// some redundant ad-hoc checks when preparing and lowering tests.1237#[derive(Debug, Clone)]1238enum PatConstKind {1239 /// The primitive `bool` type, which is like an integer but simpler,1240 /// having only two values.1241 Bool,1242 /// Primitive unsigned/signed integer types, plus `char`.1243 /// These types interact nicely with `SwitchInt`.1244 IntOrChar,1245 /// Floating-point primitives, e.g. `f32`, `f64`.1246 /// These types don't support `SwitchInt` and require an equality test,1247 /// but can also interact with range pattern tests.1248 Float,1249 /// Constant string values, tested via string equality.1250 String,1251 /// Any other constant-pattern is usually tested via some kind of equality1252 /// check. Types that might be encountered here include:1253 /// - raw pointers derived from integer values1254 /// - pattern types, e.g. `pattern_type!(u32 is 1..)`1255 Other,1256}12571258/// Node in a tree of "match pairs", where each pair consists of a place to be1259/// tested, and a test to perform on that place.1260///1261/// Each node also has a list of subpairs (possibly empty) that must also match,1262/// and some additional information from the THIR pattern it represents.1263#[derive(Debug, Clone)]1264pub(crate) struct MatchPairTree<'tcx> {1265 /// This place...1266 ///1267 /// ---1268 /// This can be `None` if it referred to a non-captured place in a closure.1269 ///1270 /// Invariant: Can only be `None` when `testable_case` is `Or`.1271 /// Therefore this must be `Some(_)` after or-pattern expansion.1272 place: Option<Place<'tcx>>,12731274 /// ... must pass this test...1275 testable_case: TestableCase<'tcx>,12761277 /// ... and these subpairs must match.1278 ///1279 /// ---1280 /// Subpairs typically represent tests that can only be performed after their1281 /// parent has succeeded. For example, the pattern `Some(3)` might have an1282 /// outer match pair that tests for the variant `Some`, and then a subpair1283 /// that tests its field for the value `3`.1284 subpairs: Vec<Self>,12851286 /// Span field of the THIR pattern this node was created from.1287 pattern_span: Span,1288}12891290/// A runtime test to perform to determine which candidates match a scrutinee place.1291///1292/// The kind of test to perform is indicated by [`TestKind`].1293#[derive(Debug)]1294pub(crate) struct Test<'tcx> {1295 span: Span,1296 kind: TestKind<'tcx>,1297}12981299/// The kind of runtime test to perform to determine which candidates match a1300/// scrutinee place. This is the main component of [`Test`].1301///1302/// Some of these variants don't contain the constant value(s) being tested1303/// against, because those values are stored in the corresponding bucketed1304/// candidates instead.1305#[derive(Clone, Debug, PartialEq)]1306enum TestKind<'tcx> {1307 /// Test what enum variant a value is.1308 ///1309 /// The subset of expected variants is not stored here; instead they are1310 /// extracted from the [`TestableCase`]s of the candidates participating in the1311 /// test.1312 Switch {1313 /// The enum type being tested.1314 adt_def: ty::AdtDef<'tcx>,1315 },13161317 /// Test what value an integer or `char` has.1318 ///1319 /// The test's target values are not stored here; instead they are extracted1320 /// from the [`TestableCase`]s of the candidates participating in the test.1321 SwitchInt,13221323 /// Test whether a `bool` is `true` or `false`.1324 If,13251326 /// Tests the place against a string constant using string equality.1327 StringEq {1328 /// Constant string value to test against.1329 /// Note that this value has type `str` (not `&str`).1330 value: ty::Value<'tcx>,1331 },13321333 /// Tests the place against a constant using scalar equality.1334 ScalarEq { value: ty::Value<'tcx> },13351336 /// Test whether the value falls within an inclusive or exclusive range.1337 Range(Arc<PatRange<'tcx>>),13381339 /// Test that the length of the slice is `== len` or `>= len`.1340 SliceLen { len: u64, op: SliceLenOp },13411342 /// Call `Deref::deref[_mut]` on the value.1343 Deref {1344 /// Temporary to store the result of `deref()`/`deref_mut()`.1345 temp: Place<'tcx>,1346 mutability: Mutability,1347 },13481349 /// Assert unreachability of never patterns.1350 Never,1351}13521353/// Indicates the kind of slice-length constraint imposed by a slice pattern,1354/// or its corresponding test.1355#[derive(Debug, Clone, Copy, PartialEq)]1356enum SliceLenOp {1357 /// The slice pattern can only match a slice with exactly `len` elements.1358 Equal,1359 /// The slice pattern can match a slice with `len` or more elements1360 /// (i.e. it contains a `..` subpattern in the middle).1361 GreaterOrEqual,1362}13631364/// The branch to be taken after a test.1365#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]1366enum TestBranch<'tcx> {1367 /// Success branch, used for tests with two possible outcomes.1368 Success,1369 /// Branch corresponding to this constant. Must be a scalar.1370 Constant(ty::Value<'tcx>),1371 /// Branch corresponding to this variant.1372 Variant(VariantIdx),1373 /// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests.1374 Failure,1375}13761377impl<'tcx> TestBranch<'tcx> {1378 fn as_constant(&self) -> Option<ty::Value<'tcx>> {1379 if let Self::Constant(v) = self { Some(*v) } else { None }1380 }1381}13821383/// `ArmHasGuard` is a wrapper around a boolean flag. It indicates whether1384/// a match arm has a guard expression attached to it.1385#[derive(Copy, Clone, Debug)]1386pub(crate) struct ArmHasGuard(pub(crate) bool);13871388///////////////////////////////////////////////////////////////////////////1389// Main matching algorithm13901391/// A sub-branch in the output of match lowering. Match lowering has generated MIR code that will1392/// branch to `success_block` when the matched value matches the corresponding pattern. If there is1393/// a guard, its failure must continue to `otherwise_block`, which will resume testing patterns.1394#[derive(Debug, Clone)]1395struct MatchTreeSubBranch<'tcx> {1396 span: Span,1397 /// The block that is branched to if the corresponding subpattern matches.1398 success_block: BasicBlock,1399 /// The block to branch to if this arm had a guard and the guard fails.1400 otherwise_block: BasicBlock,1401 /// The bindings to set up in this sub-branch.1402 bindings: Vec<Binding<'tcx>>,1403 /// The ascriptions to set up in this sub-branch.1404 ascriptions: Vec<Ascription<'tcx>>,1405 /// Whether the sub-branch corresponds to a never pattern.1406 is_never: bool,1407}14081409/// A branch in the output of match lowering.1410#[derive(Debug, Clone)]1411struct MatchTreeBranch<'tcx> {1412 sub_branches: Vec<MatchTreeSubBranch<'tcx>>,1413}14141415/// The result of generating MIR for a pattern-matching expression. Each input branch/arm/pattern1416/// gives rise to an output `MatchTreeBranch`. If one of the patterns matches, we branch to the1417/// corresponding `success_block`. If none of the patterns matches, we branch to `otherwise_block`.1418///1419/// Each branch is made of one of more sub-branches, corresponding to or-patterns. E.g.1420/// ```ignore(illustrative)1421/// match foo {1422/// (x, false) | (false, x) => {}1423/// (true, true) => {}1424/// }1425/// ```1426/// Here the first arm gives the first `MatchTreeBranch`, which has two sub-branches, one for each1427/// alternative of the or-pattern. They are kept separate because each needs to bind `x` to a1428/// different place.1429#[derive(Debug, Clone)]1430pub(crate) struct BuiltMatchTree<'tcx> {1431 branches: Vec<MatchTreeBranch<'tcx>>,1432 otherwise_block: BasicBlock,1433 /// If any of the branches had a guard, we collect here the places and locals to fakely borrow1434 /// to ensure match guards can't modify the values as we match them. For more details, see1435 /// [`util::collect_fake_borrows`].1436 fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,1437}14381439impl<'tcx> MatchTreeSubBranch<'tcx> {1440 fn from_sub_candidate(1441 candidate: Candidate<'tcx>,1442 parent_data: &Vec<PatternExtraData<'tcx>>,1443 ) -> Self {1444 debug_assert!(candidate.match_pairs.is_empty());1445 MatchTreeSubBranch {1446 span: candidate.extra_data.span,1447 success_block: candidate.pre_binding_block.unwrap(),1448 otherwise_block: candidate.otherwise_block.unwrap(),1449 bindings: sub_branch_bindings(parent_data, &candidate.extra_data.bindings),1450 ascriptions: parent_data1451 .iter()1452 .flat_map(|d| &d.ascriptions)1453 .cloned()1454 .chain(candidate.extra_data.ascriptions)1455 .collect(),1456 is_never: candidate.extra_data.is_never,1457 }1458 }1459}14601461impl<'tcx> MatchTreeBranch<'tcx> {1462 fn from_candidate(candidate: Candidate<'tcx>) -> Self {1463 let mut sub_branches = Vec::new();1464 traverse_candidate(1465 candidate,1466 &mut Vec::new(),1467 &mut |candidate: Candidate<'_>, parent_data: &mut Vec<PatternExtraData<'_>>| {1468 sub_branches.push(MatchTreeSubBranch::from_sub_candidate(candidate, parent_data));1469 },1470 |inner_candidate, parent_data| {1471 parent_data.push(inner_candidate.extra_data);1472 inner_candidate.subcandidates.into_iter()1473 },1474 |parent_data| {1475 parent_data.pop();1476 },1477 );1478 MatchTreeBranch { sub_branches }1479 }1480}14811482/// Collects the bindings for a [`MatchTreeSubBranch`], preserving the order they appear in the1483/// pattern, as though the or-alternatives chosen in this sub-branch were inlined.1484fn sub_branch_bindings<'tcx>(1485 parents: &[PatternExtraData<'tcx>],1486 leaf_bindings: &[SubpatternBindings<'tcx>],1487) -> Vec<Binding<'tcx>> {1488 // In the common case, all bindings will be in leaves. Allocate to fit the leaf's bindings.1489 let mut all_bindings = Vec::with_capacity(leaf_bindings.len());1490 let mut remainder = parents1491 .iter()1492 .map(|parent| parent.bindings.as_slice())1493 .chain([leaf_bindings])1494 // Skip over unsimplified or-patterns without bindings.1495 .filter(|bindings| !bindings.is_empty());1496 if let Some(candidate_bindings) = remainder.next() {1497 push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder);1498 }1499 // Make sure we've included all bindings. For ill-formed patterns like `(x, _ | y)`, we may not1500 // have collected all bindings yet, since we only check the first alternative when determining1501 // whether to inline subcandidates' bindings.1502 // FIXME(@dianne): prevent ill-formed patterns from getting here1503 while let Some(candidate_bindings) = remainder.next() {1504 ty::tls::with(|tcx| {1505 tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted")1506 });1507 // To recover, we collect the rest in an arbitrary order.1508 push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder);1509 }1510 all_bindings1511}15121513/// Helper for [`sub_branch_bindings`]. Collects bindings from `candidate_bindings` into1514/// `flattened`. Bindings in or-patterns are collected recursively from `remainder`.1515fn push_sub_branch_bindings<'c, 'tcx: 'c>(1516 flattened: &mut Vec<Binding<'tcx>>,1517 candidate_bindings: &'c [SubpatternBindings<'tcx>],1518 remainder: &mut impl Iterator<Item = &'c [SubpatternBindings<'tcx>]>,1519) {1520 for subpat_bindings in candidate_bindings {1521 match subpat_bindings {1522 SubpatternBindings::One(binding) => flattened.push(*binding),1523 SubpatternBindings::FromOrPattern => {1524 // Inline bindings from an or-pattern. By construction, this always1525 // corresponds to a subcandidate and its closest descendants (i.e. those1526 // from nested or-patterns, but not adjacent or-patterns). To handle1527 // adjacent or-patterns, e.g. `(x | x, y | y)`, we update the `remainder` to1528 // point to the first descendant candidate from outside this or-pattern.1529 if let Some(subcandidate_bindings) = remainder.next() {1530 push_sub_branch_bindings(flattened, subcandidate_bindings, remainder);1531 } else {1532 // For ill-formed patterns like `x | _`, we may not have any subcandidates left1533 // to inline bindings from.1534 // FIXME(@dianne): prevent ill-formed patterns from getting here1535 ty::tls::with(|tcx| {1536 tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted")1537 });1538 };1539 }1540 }1541 }1542}15431544#[derive(Debug, Clone, Copy, PartialEq, Eq)]1545pub(crate) enum HasMatchGuard {1546 Yes,1547 No,1548}15491550#[derive(Debug, Clone, Copy, PartialEq, Eq)]1551pub(crate) enum Exhaustive {1552 /// `let` and `match` are exhaustive.1553 Yes,1554 /// `if let` and `let else` are not exhaustive.1555 No,1556}15571558impl<'a, 'tcx> Builder<'a, 'tcx> {1559 /// The entrypoint of the matching algorithm. Create the decision tree for the match expression,1560 /// starting from `block`.1561 ///1562 /// `patterns` is a list of patterns, one for each arm. The associated boolean indicates whether1563 /// the arm has a guard.1564 ///1565 /// `exhaustive` indicates whether the candidate list is exhaustive (for `if let` and `let else`)1566 /// or not (for `let` and `match`). In the non-exhaustive case we return the block to which we1567 /// branch on failure.1568 pub(crate) fn lower_match_tree(1569 &mut self,1570 block: BasicBlock,1571 scrutinee_span: Span,1572 scrutinee_place_builder: &PlaceBuilder<'tcx>,1573 match_start_span: Span,1574 patterns: Vec<(&Pat<'tcx>, HasMatchGuard)>,1575 exhaustive: Exhaustive,1576 ) -> BuiltMatchTree<'tcx> {1577 // Assemble the initial list of candidates. These top-level candidates are 1:1 with the1578 // input patterns, but other parts of match lowering also introduce subcandidates (for1579 // sub-or-patterns). So inside the algorithm, the candidates list may not correspond to1580 // match arms directly.1581 let mut candidates: Vec<Candidate<'_>> = patterns1582 .into_iter()1583 .map(|(pat, has_guard)| {1584 Candidate::new(scrutinee_place_builder.clone(), pat, has_guard, self)1585 })1586 .collect();15871588 let fake_borrow_temps = util::collect_fake_borrows(1589 self,1590 &candidates,1591 scrutinee_span,1592 scrutinee_place_builder.base(),1593 );15941595 // This will generate code to test scrutinee_place and branch to the appropriate arm block.1596 // If none of the arms match, we branch to `otherwise_block`. When lowering a `match`1597 // expression, exhaustiveness checking ensures that this block is unreachable.1598 let mut candidate_refs = candidates.iter_mut().collect::<Vec<_>>();1599 let otherwise_block =1600 self.match_candidates(match_start_span, scrutinee_span, block, &mut candidate_refs);16011602 // Set up false edges so that the borrow-checker cannot make use of the specific CFG we1603 // generated. We falsely branch from each candidate to the one below it to make it as if we1604 // were testing match branches one by one in order. In the non-exhaustive case we also want a1605 // false edge to the final failure block.1606 let mut next_candidate_start_block = match exhaustive {1607 Exhaustive::Yes => None,1608 Exhaustive::No => Some(otherwise_block),1609 };1610 for candidate in candidates.iter_mut().rev() {1611 let has_guard = candidate.has_guard;1612 candidate.visit_leaves_rev(|leaf_candidate| {1613 if let Some(next_candidate_start_block) = next_candidate_start_block {1614 let source_info = self.source_info(leaf_candidate.extra_data.span);1615 // Falsely branch to `next_candidate_start_block` before reaching pre_binding.1616 let old_pre_binding = leaf_candidate.pre_binding_block.unwrap();1617 let new_pre_binding = self.cfg.start_new_block();1618 self.false_edges(1619 old_pre_binding,1620 new_pre_binding,1621 next_candidate_start_block,1622 source_info,1623 );1624 leaf_candidate.pre_binding_block = Some(new_pre_binding);1625 if has_guard {1626 // Falsely branch to `next_candidate_start_block` also if the guard fails.1627 let new_otherwise = self.cfg.start_new_block();1628 let old_otherwise = leaf_candidate.otherwise_block.unwrap();1629 self.false_edges(1630 new_otherwise,1631 old_otherwise,1632 next_candidate_start_block,1633 source_info,1634 );1635 leaf_candidate.otherwise_block = Some(new_otherwise);1636 }1637 }1638 assert!(leaf_candidate.false_edge_start_block.is_some());1639 next_candidate_start_block = leaf_candidate.false_edge_start_block;1640 });1641 }16421643 if exhaustive == Exhaustive::Yes {1644 // Match checking ensures `otherwise_block` is actually unreachable in exhaustive1645 // cases.1646 let source_info = self.source_info(scrutinee_span);16471648 // Matching on a scrutinee place of an uninhabited type doesn't generate any memory1649 // reads by itself, and so if the place is uninitialized we wouldn't know. In order to1650 // disallow the following:1651 // ```rust1652 // let x: !;1653 // match x {}1654 // ```1655 // we add a dummy read on the place.1656 //1657 // NOTE: If we require never patterns for empty matches, those will check that the place1658 // is initialized, and so this read would no longer be needed.1659 let cause_matched_place = FakeReadCause::ForMatchedPlace(None);16601661 if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {1662 self.cfg.push_fake_read(1663 otherwise_block,1664 source_info,1665 cause_matched_place,1666 scrutinee_place,1667 );1668 }16691670 self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);1671 }16721673 BuiltMatchTree {1674 branches: candidates.into_iter().map(MatchTreeBranch::from_candidate).collect(),1675 otherwise_block,1676 fake_borrow_temps,1677 }1678 }16791680 /// The main match algorithm. It begins with a set of candidates `candidates` and has the job of1681 /// generating code that branches to an appropriate block if the scrutinee matches one of these1682 /// candidates. The1683 /// candidates are ordered such that the first item in the list1684 /// has the highest priority. When a candidate is found to match1685 /// the value, we will set and generate a branch to the appropriate1686 /// pre-binding block.1687 ///1688 /// If none of the candidates apply, we continue to the returned `otherwise_block`.1689 ///1690 /// Note that while `match` expressions in the Rust language are exhaustive,1691 /// candidate lists passed to this method are often _non-exhaustive_.1692 /// For example, the match lowering process will frequently divide up the1693 /// list of candidates, and recursively call this method with a non-exhaustive1694 /// subset of candidates.1695 /// See [`Builder::test_candidates`] for more details on this1696 /// "backtracking automata" approach.1697 ///1698 /// For an example of how we use `otherwise_block`, consider:1699 /// ```1700 /// # fn foo((x, y): (bool, bool)) -> u32 {1701 /// match (x, y) {1702 /// (true, true) => 1,1703 /// (_, false) => 2,1704 /// (false, true) => 3,1705 /// }1706 /// # }1707 /// ```1708 /// For this match, we generate something like:1709 /// ```1710 /// # fn foo((x, y): (bool, bool)) -> u32 {1711 /// if x {1712 /// if y {1713 /// return 11714 /// } else {1715 /// // continue1716 /// }1717 /// } else {1718 /// // continue1719 /// }1720 /// if y {1721 /// if x {1722 /// // This is actually unreachable because the `(true, true)` case was handled above,1723 /// // but we don't know that from within the lowering algorithm.1724 /// // continue1725 /// } else {1726 /// return 31727 /// }1728 /// } else {1729 /// return 21730 /// }1731 /// // this is the final `otherwise_block`, which is unreachable because the match was exhaustive.1732 /// unreachable!()1733 /// # }1734 /// ```1735 ///1736 /// Every `continue` is an instance of branching to some `otherwise_block` somewhere deep within1737 /// the algorithm. For more details on why we lower like this, see [`Builder::test_candidates`].1738 ///1739 /// Note how we test `x` twice. This is the tradeoff of backtracking automata: we prefer smaller1740 /// code size so we accept non-optimal code paths.1741 #[instrument(skip(self), level = "debug")]1742 fn match_candidates(1743 &mut self,1744 span: Span,1745 scrutinee_span: Span,1746 start_block: BasicBlock,1747 candidates: &mut [&mut Candidate<'tcx>],1748 ) -> BasicBlock {1749 ensure_sufficient_stack(|| {1750 self.match_candidates_inner(span, scrutinee_span, start_block, candidates)1751 })1752 }17531754 /// Construct the decision tree for `candidates`. Don't call this, call `match_candidates`1755 /// instead to reserve sufficient stack space.1756 fn match_candidates_inner(1757 &mut self,1758 span: Span,1759 scrutinee_span: Span,1760 mut start_block: BasicBlock,1761 candidates: &mut [&mut Candidate<'tcx>],1762 ) -> BasicBlock {1763 if let [first, ..] = candidates {1764 if first.false_edge_start_block.is_none() {1765 first.false_edge_start_block = Some(start_block);1766 }1767 }17681769 // Process a prefix of the candidates.1770 let rest = match candidates {1771 [] => {1772 // If there are no candidates that still need testing, we're done.1773 return start_block;1774 }1775 [first, remaining @ ..] if first.match_pairs.is_empty() => {1776 // The first candidate has satisfied all its match pairs.1777 // We record the blocks that will be needed by match arm lowering,1778 // and then continue with the remaining candidates.1779 let remainder_start = self.select_matched_candidate(first, start_block);1780 remainder_start.and(remaining)1781 }1782 candidates if candidates.iter().any(|candidate| candidate.starts_with_or_pattern()) => {1783 // If any candidate starts with an or-pattern, we want to expand or-patterns1784 // before we do any more tests.1785 //1786 // The only candidate we strictly _need_ to expand here is the first one.1787 // But by expanding other candidates as early as possible, we unlock more1788 // opportunities to include them in test outcomes, making the match tree1789 // smaller and simpler.1790 self.expand_and_match_or_candidates(span, scrutinee_span, start_block, candidates)1791 }1792 candidates => {1793 // The first candidate has some unsatisfied match pairs; we proceed to do more tests.1794 self.test_candidates(span, scrutinee_span, candidates, start_block)1795 }1796 };17971798 // Process any candidates that remain.1799 let remaining_candidates = unpack!(start_block = rest);1800 self.match_candidates(span, scrutinee_span, start_block, remaining_candidates)1801 }18021803 /// Link up matched candidates.1804 ///1805 /// For example, if we have something like this:1806 ///1807 /// ```ignore (illustrative)1808 /// ...1809 /// Some(x) if cond1 => ...1810 /// Some(x) => ...1811 /// Some(x) if cond2 => ...1812 /// ...1813 /// ```1814 ///1815 /// We generate real edges from:1816 ///1817 /// * `start_block` to the [pre-binding block] of the first pattern,1818 /// * the [otherwise block] of the first pattern to the second pattern,1819 /// * the [otherwise block] of the third pattern to a block with an1820 /// [`Unreachable` terminator](TerminatorKind::Unreachable).1821 ///1822 /// In addition, we later add fake edges from the otherwise blocks to the1823 /// pre-binding block of the next candidate in the original set of1824 /// candidates.1825 ///1826 /// [pre-binding block]: Candidate::pre_binding_block1827 /// [otherwise block]: Candidate::otherwise_block1828 fn select_matched_candidate(1829 &mut self,1830 candidate: &mut Candidate<'tcx>,1831 start_block: BasicBlock,1832 ) -> BasicBlock {1833 assert!(candidate.otherwise_block.is_none());1834 assert!(candidate.pre_binding_block.is_none());1835 assert!(candidate.subcandidates.is_empty());18361837 candidate.pre_binding_block = Some(start_block);1838 let otherwise_block = self.cfg.start_new_block();1839 // Create the otherwise block for this candidate, which is the1840 // pre-binding block for the next candidate.1841 candidate.otherwise_block = Some(otherwise_block);1842 otherwise_block1843 }18441845 /// Takes a list of candidates such that some of the candidates' first match pairs are1846 /// or-patterns. This expands as many or-patterns as possible and processes the resulting1847 /// candidates. Returns the unprocessed candidates if any.1848 fn expand_and_match_or_candidates<'b, 'c>(1849 &mut self,1850 span: Span,1851 scrutinee_span: Span,1852 start_block: BasicBlock,1853 candidates: &'b mut [&'c mut Candidate<'tcx>],1854 ) -> BlockAnd<&'b mut [&'c mut Candidate<'tcx>]> {1855 // We can't expand or-patterns freely. The rule is:1856 // - If a candidate doesn't start with an or-pattern, we include it in1857 // the expansion list as-is (i.e. it "expands" to itself).1858 // - If a candidate has an or-pattern as its only remaining match pair,1859 // we can expand it.1860 // - If it starts with an or-pattern but also has other match pairs,1861 // we can expand it, but we can't process more candidates after it.1862 //1863 // If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the1864 // following, or-pattern simplification (in `merge_trivial_subcandidates`) makes it1865 // so the `1` and `2` cases branch to a same block (which then tests `false`). If we1866 // took `(2, _)` in the same set of candidates, when we reach the block that tests1867 // `false` we don't know whether we came from `1` or `2`, hence we can't know where1868 // to branch on failure.1869 //1870 // ```ignore(illustrative)1871 // match (1, true) {1872 // (1 | 2, false) => {},1873 // (2, _) => {},1874 // _ => {}1875 // }1876 // ```1877 //1878 // We therefore split the `candidates` slice in two, expand or-patterns in the first part,1879 // and process the rest separately.1880 let expand_until = candidates1881 .iter()1882 .position(|candidate| {1883 // If a candidate starts with an or-pattern and has more match pairs,1884 // we can expand it, but we must stop expanding _after_ it.1885 candidate.match_pairs.len() > 1 && candidate.starts_with_or_pattern()1886 })1887 .map(|pos| pos + 1) // Stop _after_ the found candidate1888 .unwrap_or(candidates.len()); // Otherwise, include all candidates1889 let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);18901891 // Expand one level of or-patterns for each candidate in `candidates_to_expand`.1892 // We take care to preserve the relative ordering of candidates, so that1893 // or-patterns are expanded in their parent's relative position.1894 let mut expanded_candidates = Vec::new();1895 for candidate in candidates_to_expand.iter_mut() {1896 if candidate.starts_with_or_pattern() {1897 let or_match_pair = candidate.match_pairs.remove(0);1898 // Expand the or-pattern into subcandidates.1899 self.create_or_subcandidates(candidate, or_match_pair);1900 // Collect the newly created subcandidates.1901 for subcandidate in candidate.subcandidates.iter_mut() {1902 expanded_candidates.push(subcandidate);1903 }1904 // Note that the subcandidates have been added to `expanded_candidates`,1905 // but `candidate` itself has not. If the last candidate has more match pairs,1906 // they are handled separately by `test_remaining_match_pairs_after_or`.1907 } else {1908 // A candidate that doesn't start with an or-pattern has nothing to1909 // expand, so it is included in the post-expansion list as-is.1910 expanded_candidates.push(candidate);1911 }1912 }19131914 // Recursively lower the part of the match tree represented by the1915 // expanded candidates. This is where subcandidates actually get lowered!1916 let remainder_start = self.match_candidates(1917 span,1918 scrutinee_span,1919 start_block,1920 expanded_candidates.as_mut_slice(),1921 );19221923 // Postprocess subcandidates, and process any leftover match pairs.1924 // (Only the last candidate can possibly have more match pairs.)1925 debug_assert!({1926 let mut all_except_last = candidates_to_expand.iter().rev().skip(1);1927 all_except_last.all(|candidate| candidate.match_pairs.is_empty())1928 });1929 for candidate in candidates_to_expand.iter_mut() {1930 if !candidate.subcandidates.is_empty() {1931 self.merge_trivial_subcandidates(candidate);1932 self.remove_never_subcandidates(candidate);1933 }1934 }1935 // It's important to perform the above simplifications _before_ dealing1936 // with remaining match pairs, to avoid exponential blowup if possible1937 // (for trivial or-patterns), and avoid useless work (for never patterns).1938 if let Some(last_candidate) = candidates_to_expand.last_mut() {1939 self.test_remaining_match_pairs_after_or(span, scrutinee_span, last_candidate);1940 }19411942 remainder_start.and(remaining_candidates)1943 }19441945 /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new1946 /// subcandidate. Any candidate that has been expanded this way should also be postprocessed1947 /// at the end of [`Self::expand_and_match_or_candidates`].1948 fn create_or_subcandidates(1949 &mut self,1950 candidate: &mut Candidate<'tcx>,1951 match_pair: MatchPairTree<'tcx>,1952 ) {1953 let TestableCase::Or { pats } = match_pair.testable_case else { bug!() };1954 debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats);1955 candidate.or_span = Some(match_pair.pattern_span);1956 candidate.subcandidates = pats1957 .into_iter()1958 .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))1959 .collect();1960 candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block;1961 }19621963 /// Try to merge all of the subcandidates of the given candidate into one. This avoids1964 /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been1965 /// expanded with `create_or_subcandidates`.1966 ///1967 /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like1968 /// so:1969 ///1970 /// ```text1971 /// [ start ]1972 /// |1973 /// [ match P, Q ]1974 /// |1975 /// +----------------------------------------+------------------------------------+1976 /// | | |1977 /// V V V1978 /// [ P matches ] [ Q matches ] [ otherwise ]1979 /// | | |1980 /// V V |1981 /// [ match R, S ] [ match R, S ] |1982 /// | | |1983 /// +--------------+------------+ +--------------+------------+ |1984 /// | | | | | | |1985 /// V V V V V V |1986 /// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] |1987 /// | | | | | | |1988 /// +--------------+------------|------------+--------------+ | |1989 /// | | | |1990 /// | +----------------------------------------+--------+1991 /// | |1992 /// V V1993 /// [ Success ] [ Failure ]1994 /// ```1995 ///1996 /// In practice there are some complications:1997 ///1998 /// * If there's a guard, then the otherwise branch of the first match on1999 /// `R | S` goes to a test for whether `Q` matches, and the control flow2000 /// doesn't merge into a single success block until after the guard is
Findings
✓ No findings reported for this file.