Skip to main content

hir_ty/mir/lower/
pattern_matching.rs

1//! MIR lowering for patterns
2
3use hir_def::{
4    hir::{ExprId, RecordFieldPat},
5    signatures::VariantFields,
6};
7use rustc_type_ir::inherent::{IntoKind, Ty as _};
8
9use crate::{
10    BindingMode, ByRef,
11    mir::{
12        FieldIndex, LocalId, MutBorrowKind, Operand, OperandKind, PlaceRef, Projection,
13        lower::{
14            BasicBlockId, BinOp, BindingId, BorrowKind, Expr, Idx, MemoryMap, MirLowerCtx,
15            MirLowerError, MirSpan, Pat, PatId, PlaceElem, ProjectionElem, ResolveValueResult,
16            Result, Rvalue, SwitchTargets, TerminatorKind, Ty, TyKind, ValueNs, VariantId,
17        },
18    },
19};
20use crate::{method_resolution::CandidateId, next_solver::GenericArgs};
21
22macro_rules! not_supported {
23    ($x: expr) => {
24        return Err(MirLowerError::NotSupported(format!($x)))
25    };
26}
27
28pub(super) enum AdtPatternShape<'a> {
29    Tuple { args: &'a [PatId], ellipsis: Option<u32> },
30    Record { args: &'a [RecordFieldPat] },
31    Unit,
32}
33
34/// We need to do pattern matching in two phases: One to check if the pattern matches, and one to fill the bindings
35/// of patterns. This is necessary to prevent double moves and similar problems. For example:
36/// ```ignore
37/// struct X;
38/// match (X, 3) {
39///     (b, 2) | (b, 3) => {},
40///     _ => {}
41/// }
42/// ```
43/// If we do everything in one pass, we will move `X` to the first `b`, then we see that the second field of tuple
44/// doesn't match and we should move the `X` to the second `b` (which here is the same thing, but doesn't need to be) and
45/// it might even doesn't match the second pattern and we may want to not move `X` at all.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47enum MatchingMode {
48    /// Check that if this pattern matches
49    Check,
50    /// Assume that this pattern matches, fill bindings
51    Bind,
52    /// Assume that this pattern matches, assign to existing variables.
53    Assign,
54}
55
56impl<'db> MirLowerCtx<'_, 'db> {
57    /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
58    /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
59    /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
60    /// mismatched path block is `None`.
61    ///
62    /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
63    /// `current_else` argument to save an unnecessary jump. If `current_else` isn't `None`, the result mismatched path
64    /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
65    /// so it should be an empty block.
66    pub(super) fn pattern_match(
67        &mut self,
68        current: BasicBlockId,
69        current_else: Option<BasicBlockId>,
70        cond_place: PlaceRef<'db>,
71        pattern: PatId,
72    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
73        let (current, current_else) = self.pattern_match_inner(
74            current,
75            current_else,
76            cond_place,
77            pattern,
78            MatchingMode::Check,
79        )?;
80        let (current, current_else) = self.pattern_match_inner(
81            current,
82            current_else,
83            cond_place,
84            pattern,
85            MatchingMode::Bind,
86        )?;
87        Ok((current, current_else))
88    }
89
90    pub(super) fn pattern_match_assignment(
91        &mut self,
92        current: BasicBlockId,
93        value: PlaceRef<'db>,
94        pattern: PatId,
95    ) -> Result<'db, BasicBlockId> {
96        let (current, _) =
97            self.pattern_match_inner(current, None, value, pattern, MatchingMode::Assign)?;
98        Ok(current)
99    }
100
101    pub(super) fn match_self_param(
102        &mut self,
103        id: BindingId,
104        current: BasicBlockId,
105        local: LocalId,
106    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
107        self.pattern_match_binding(
108            id,
109            BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not),
110            local.into(),
111            MirSpan::SelfParam,
112            current,
113            None,
114        )
115    }
116
117    fn pattern_match_inner(
118        &mut self,
119        mut current: BasicBlockId,
120        mut current_else: Option<BasicBlockId>,
121        mut cond_place: PlaceRef<'db>,
122        pattern: PatId,
123        mode: MatchingMode,
124    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
125        let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default();
126        cond_place.projection = Projection::new_from_iter(
127            cond_place
128                .projection
129                .as_slice()
130                .iter()
131                .cloned()
132                .chain((0..cnt).map(|_| ProjectionElem::Deref)),
133        );
134        Ok(match &self.store[pattern] {
135            Pat::Missing | Pat::Rest | Pat::NotNull => {
136                return Err(MirLowerError::IncompletePattern);
137            }
138            Pat::Wild => (current, current_else),
139            Pat::Tuple { args, ellipsis } => {
140                let place_ty = cond_place.ty(&self.result, &self.infcx, self.env).ty;
141                let subst = match place_ty.kind() {
142                    TyKind::Tuple(s) => s,
143                    _ => {
144                        return Err(MirLowerError::TypeError(
145                            "non tuple type matched with tuple pattern",
146                        ));
147                    }
148                };
149                self.pattern_match_tuple_like(
150                    current,
151                    current_else,
152                    args,
153                    *ellipsis,
154                    (0..subst.len()).map(|i| PlaceElem::Field(FieldIndex(i as u32))),
155                    &cond_place,
156                    mode,
157                )?
158            }
159            Pat::Or(pats) => {
160                let then_target = self.new_basic_block();
161                let mut finished = false;
162                for pat in &**pats {
163                    let (mut next, next_else) = self.pattern_match_inner(
164                        current,
165                        None,
166                        cond_place,
167                        *pat,
168                        MatchingMode::Check,
169                    )?;
170                    if mode != MatchingMode::Check {
171                        (next, _) = self.pattern_match_inner(next, None, cond_place, *pat, mode)?;
172                    }
173                    self.set_goto(next, then_target, pattern.into());
174                    match next_else {
175                        Some(t) => {
176                            current = t;
177                        }
178                        None => {
179                            finished = true;
180                            break;
181                        }
182                    }
183                }
184                if !finished {
185                    if mode == MatchingMode::Check {
186                        let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
187                        self.set_goto(current, ce, pattern.into());
188                    } else {
189                        self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
190                    }
191                }
192                (then_target, current_else)
193            }
194            Pat::Record { args, .. } => {
195                let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
196                    not_supported!("unresolved variant for record");
197                };
198                self.pattern_matching_variant(
199                    cond_place,
200                    variant,
201                    current,
202                    pattern.into(),
203                    current_else,
204                    AdtPatternShape::Record { args },
205                    mode,
206                )?
207            }
208            Pat::Range { start, end, range_type: _ } => {
209                let mut add_check = |l: &ExprId, binop| -> Result<'db, ()> {
210                    let lv =
211                        self.lower_literal_or_const_to_operand(self.infer.pat_ty(pattern), l)?;
212                    let else_target = *current_else.get_or_insert_with(|| self.new_basic_block());
213                    let next = self.new_basic_block();
214                    let discr =
215                        self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into();
216                    self.push_assignment(
217                        current,
218                        discr,
219                        Rvalue::CheckedBinaryOp(
220                            binop,
221                            lv,
222                            Operand { kind: OperandKind::Copy(cond_place.store()), span: None },
223                        ),
224                        pattern.into(),
225                    );
226                    let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None };
227                    self.set_terminator(
228                        current,
229                        TerminatorKind::SwitchInt {
230                            discr,
231                            targets: SwitchTargets::static_if(1, next, else_target),
232                        },
233                        pattern.into(),
234                    );
235                    current = next;
236                    Ok(())
237                };
238                if mode == MatchingMode::Check {
239                    if let Some(start) = start {
240                        add_check(start, BinOp::Le)?;
241                    }
242                    if let Some(end) = end {
243                        add_check(end, BinOp::Ge)?;
244                    }
245                }
246                (current, current_else)
247            }
248            Pat::Slice { prefix, slice, suffix } => {
249                let pat_ty = self.infer.pat_ty(pattern);
250                // FIXME: MIR lowering should be skipped for bodies with inference errors. Once
251                // that happens, this recovery for invalid slice patterns can be removed.
252                if !matches!(pat_ty.kind(), TyKind::Array(..) | TyKind::Slice(_)) {
253                    return Err(MirLowerError::TypeError(
254                        "non array or slice type matched with slice pattern",
255                    ));
256                }
257
258                if mode == MatchingMode::Check {
259                    // emit runtime length check for slice
260                    if let TyKind::Slice(_) = pat_ty.kind() {
261                        let pattern_len = prefix.len() + suffix.len();
262                        let place_len = self
263                            .temp(Ty::new_usize(self.interner()), current, pattern.into())?
264                            .into();
265                        self.push_assignment(
266                            current,
267                            place_len,
268                            Rvalue::Len(cond_place.store()),
269                            pattern.into(),
270                        );
271                        let else_target =
272                            *current_else.get_or_insert_with(|| self.new_basic_block());
273                        let next = self.new_basic_block();
274                        if slice.is_none() {
275                            self.set_terminator(
276                                current,
277                                TerminatorKind::SwitchInt {
278                                    discr: Operand {
279                                        kind: OperandKind::Copy(place_len.store()),
280                                        span: None,
281                                    },
282                                    targets: SwitchTargets::static_if(
283                                        pattern_len as u128,
284                                        next,
285                                        else_target,
286                                    ),
287                                },
288                                pattern.into(),
289                            );
290                        } else {
291                            let c = Operand::from_concrete_const(
292                                pattern_len.to_le_bytes().into(),
293                                MemoryMap::default(),
294                                Ty::new_usize(self.interner()),
295                            );
296                            let discr = self
297                                .temp(Ty::new_bool(self.interner()), current, pattern.into())?
298                                .into();
299                            self.push_assignment(
300                                current,
301                                discr,
302                                Rvalue::CheckedBinaryOp(
303                                    BinOp::Le,
304                                    c,
305                                    Operand {
306                                        kind: OperandKind::Copy(place_len.store()),
307                                        span: None,
308                                    },
309                                ),
310                                pattern.into(),
311                            );
312                            let discr =
313                                Operand { kind: OperandKind::Copy(discr.store()), span: None };
314                            self.set_terminator(
315                                current,
316                                TerminatorKind::SwitchInt {
317                                    discr,
318                                    targets: SwitchTargets::static_if(1, next, else_target),
319                                },
320                                pattern.into(),
321                            );
322                        }
323                        current = next;
324                    }
325                }
326                for (i, &pat) in prefix.iter().enumerate() {
327                    let next_place = cond_place.project(ProjectionElem::ConstantIndex {
328                        offset: i as u64,
329                        from_end: false,
330                    });
331                    (current, current_else) =
332                        self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
333                }
334                if let &Some(slice) = slice
335                    && mode != MatchingMode::Check
336                    && let Pat::Bind { id, subpat: _ } = self.store[slice]
337                {
338                    let next_place = cond_place.project(ProjectionElem::Subslice {
339                        from: prefix.len() as u64,
340                        to: suffix.len() as u64,
341                    });
342                    let mode = self.infer.binding_modes[slice];
343                    (current, current_else) = self.pattern_match_binding(
344                        id,
345                        mode,
346                        next_place,
347                        (slice).into(),
348                        current,
349                        current_else,
350                    )?;
351                }
352                for (i, &pat) in suffix.iter().enumerate() {
353                    let next_place = cond_place.project(ProjectionElem::ConstantIndex {
354                        offset: i as u64,
355                        from_end: true,
356                    });
357                    (current, current_else) =
358                        self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
359                }
360                (current, current_else)
361            }
362            Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) {
363                Some(variant) => self.pattern_matching_variant(
364                    cond_place,
365                    variant,
366                    current,
367                    pattern.into(),
368                    current_else,
369                    AdtPatternShape::Unit,
370                    mode,
371                )?,
372                None => {
373                    let unresolved_name = || {
374                        MirLowerError::unresolved_path(
375                            self.db,
376                            p,
377                            self.display_target(),
378                            self.owner.expression_store_owner(self.db),
379                            self.store,
380                        )
381                    };
382                    let hygiene = self.store.pat_path_hygiene(pattern);
383                    let pr = self
384                        .resolver
385                        .resolve_path_in_value_ns(self.db, p, hygiene)
386                        .ok_or_else(unresolved_name)?;
387
388                    if let (
389                        MatchingMode::Assign,
390                        ResolveValueResult::ValueNs(ValueNs::LocalBinding(binding)),
391                    ) = (mode, &pr)
392                    {
393                        let local = self.binding_local(*binding)?;
394                        self.push_match_assignment(
395                            current,
396                            local,
397                            BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not),
398                            cond_place,
399                            pattern.into(),
400                        );
401                        return Ok((current, current_else));
402                    }
403
404                    // The path is not a variant or a local, so it is a const
405                    if mode != MatchingMode::Check {
406                        // A const don't bind anything. Only needs check.
407                        return Ok((current, current_else));
408                    }
409                    let (c, subst) = 'b: {
410                        if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern)
411                            && let CandidateId::ConstId(c) = x.0
412                        {
413                            break 'b (c, x.1);
414                        }
415                        if let ResolveValueResult::ValueNs(ValueNs::ConstId(c)) = pr {
416                            break 'b (c, GenericArgs::empty(self.interner()));
417                        }
418                        not_supported!("path in pattern position that is not const or variant")
419                    };
420                    let tmp =
421                        self.temp(self.infer.pat_ty(pattern), current, pattern.into())?.into();
422                    let span = pattern.into();
423                    self.lower_const(c.into(), current, tmp, subst, span)?;
424                    let tmp2 =
425                        self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into();
426                    self.push_assignment(
427                        current,
428                        tmp2,
429                        Rvalue::CheckedBinaryOp(
430                            BinOp::Eq,
431                            Operand { kind: OperandKind::Copy(tmp.store()), span: None },
432                            Operand { kind: OperandKind::Copy(cond_place.store()), span: None },
433                        ),
434                        span,
435                    );
436                    let next = self.new_basic_block();
437                    let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
438                    self.set_terminator(
439                        current,
440                        TerminatorKind::SwitchInt {
441                            discr: Operand { kind: OperandKind::Copy(tmp2.store()), span: None },
442                            targets: SwitchTargets::static_if(1, next, else_target),
443                        },
444                        span,
445                    );
446                    (next, Some(else_target))
447                }
448            },
449            Pat::Lit(l) => match &self.store[*l] {
450                Expr::Literal(l) => {
451                    if mode == MatchingMode::Check {
452                        let c = self.lower_literal_to_operand(self.infer.pat_ty(pattern), l)?;
453                        self.pattern_match_const(current_else, current, c, cond_place, pattern)?
454                    } else {
455                        (current, current_else)
456                    }
457                }
458                _ => not_supported!("expression path literal"),
459            },
460            Pat::Bind { id, subpat } => {
461                if let Some(subpat) = subpat {
462                    (current, current_else) =
463                        self.pattern_match_inner(current, current_else, cond_place, *subpat, mode)?
464                }
465                if mode != MatchingMode::Check {
466                    let mode = self.infer.binding_modes[pattern];
467                    self.pattern_match_binding(
468                        *id,
469                        mode,
470                        cond_place,
471                        pattern.into(),
472                        current,
473                        current_else,
474                    )?
475                } else {
476                    (current, current_else)
477                }
478            }
479            Pat::TupleStruct { path: _, args, ellipsis } => {
480                let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
481                    not_supported!("unresolved variant");
482                };
483                self.pattern_matching_variant(
484                    cond_place,
485                    variant,
486                    current,
487                    pattern.into(),
488                    current_else,
489                    AdtPatternShape::Tuple { args, ellipsis: *ellipsis },
490                    mode,
491                )?
492            }
493            Pat::Ref { pat, mutability: _ } => {
494                let cond_place = cond_place.project(ProjectionElem::Deref);
495                self.pattern_match_inner(current, current_else, cond_place, *pat, mode)?
496            }
497            &Pat::Expr(expr) => {
498                stdx::always!(
499                    mode == MatchingMode::Assign,
500                    "Pat::Expr can only come in destructuring assignments"
501                );
502                let Some((lhs_place, current)) = self.lower_expr_as_place(current, expr, false)?
503                else {
504                    return Ok((current, current_else));
505                };
506                self.push_assignment(
507                    current,
508                    lhs_place,
509                    Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into(),
510                    expr.into(),
511                );
512                (current, current_else)
513            }
514            Pat::Box { .. } => not_supported!("box pattern"),
515            Pat::Deref { .. } => not_supported!("deref pattern"),
516            Pat::ConstBlock(_) => not_supported!("const block pattern"),
517        })
518    }
519
520    fn pattern_match_binding(
521        &mut self,
522        id: BindingId,
523        mode: BindingMode,
524        cond_place: PlaceRef<'db>,
525        span: MirSpan,
526        current: BasicBlockId,
527        current_else: Option<BasicBlockId>,
528    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
529        let target_place = self.binding_local(id)?;
530        self.push_storage_live(id, current)?;
531        self.push_match_assignment(current, target_place, mode, cond_place, span);
532        Ok((current, current_else))
533    }
534
535    fn push_match_assignment(
536        &mut self,
537        current: BasicBlockId,
538        target_place: LocalId,
539        mode: BindingMode,
540        cond_place: PlaceRef<'db>,
541        span: MirSpan,
542    ) {
543        self.push_assignment(
544            current,
545            target_place.into(),
546            match mode {
547                BindingMode(ByRef::No, _) => {
548                    Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into()
549                }
550                BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => {
551                    Rvalue::Ref(BorrowKind::Shared, cond_place.store())
552                }
553                BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => Rvalue::Ref(
554                    BorrowKind::Mut { kind: MutBorrowKind::Default },
555                    cond_place.store(),
556                ),
557            },
558            span,
559        );
560    }
561
562    fn pattern_match_const(
563        &mut self,
564        current_else: Option<BasicBlockId>,
565        current: BasicBlockId,
566        c: Operand,
567        cond_place: PlaceRef<'db>,
568        pattern: Idx<Pat>,
569    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
570        let then_target = self.new_basic_block();
571        let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
572        let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into();
573        self.push_assignment(
574            current,
575            discr,
576            Rvalue::CheckedBinaryOp(
577                BinOp::Eq,
578                c,
579                Operand { kind: OperandKind::Copy(cond_place.store()), span: None },
580            ),
581            pattern.into(),
582        );
583        let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None };
584        self.set_terminator(
585            current,
586            TerminatorKind::SwitchInt {
587                discr,
588                targets: SwitchTargets::static_if(1, then_target, else_target),
589            },
590            pattern.into(),
591        );
592        Ok((then_target, Some(else_target)))
593    }
594
595    fn pattern_matching_variant(
596        &mut self,
597        cond_place: PlaceRef<'db>,
598        variant: VariantId,
599        mut current: BasicBlockId,
600        span: MirSpan,
601        mut current_else: Option<BasicBlockId>,
602        shape: AdtPatternShape<'_>,
603        mode: MatchingMode,
604    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
605        let place_ty = cond_place.ty(&self.result, &self.infcx, self.env).ty;
606        let Some((place_adt, _)) = place_ty.as_adt() else {
607            return Err(MirLowerError::TypeError("non ADT type matched with ADT pattern"));
608        };
609        if place_adt != variant.adt_id(self.db) {
610            return Err(MirLowerError::TypeError("ADT pattern does not match place type"));
611        }
612
613        Ok(match variant {
614            VariantId::EnumVariantId(v) => {
615                if mode == MatchingMode::Check {
616                    let e = self.const_eval_discriminant(v)? as u128;
617                    let tmp = self.discr_temp_place(current);
618                    self.push_assignment(
619                        current,
620                        tmp,
621                        Rvalue::Discriminant(cond_place.store()),
622                        span,
623                    );
624                    let next = self.new_basic_block();
625                    let else_target = current_else.get_or_insert_with(|| self.new_basic_block());
626                    self.set_terminator(
627                        current,
628                        TerminatorKind::SwitchInt {
629                            discr: Operand { kind: OperandKind::Copy(tmp.store()), span: None },
630                            targets: SwitchTargets::static_if(e, next, *else_target),
631                        },
632                        span,
633                    );
634                    current = next;
635                }
636                self.pattern_matching_variant_fields(
637                    shape,
638                    v.fields(self.db),
639                    variant,
640                    current,
641                    current_else,
642                    &cond_place,
643                    mode,
644                )?
645            }
646            VariantId::StructId(s) => self.pattern_matching_variant_fields(
647                shape,
648                s.fields(self.db),
649                variant,
650                current,
651                current_else,
652                &cond_place,
653                mode,
654            )?,
655            VariantId::UnionId(_) => {
656                return Err(MirLowerError::TypeError("pattern matching on union"));
657            }
658        })
659    }
660
661    fn pattern_matching_variant_fields(
662        &mut self,
663        shape: AdtPatternShape<'_>,
664        variant_data: &VariantFields,
665        v: VariantId,
666        current: BasicBlockId,
667        current_else: Option<BasicBlockId>,
668        cond_place: &PlaceRef<'db>,
669        mode: MatchingMode,
670    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
671        let downcast_place = if matches!(v, VariantId::EnumVariantId(_)) {
672            cond_place.project(ProjectionElem::Downcast(v))
673        } else {
674            *cond_place
675        };
676        Ok(match shape {
677            AdtPatternShape::Record { args } => {
678                let it = args
679                    .iter()
680                    .map(|x| {
681                        let field_id =
682                            variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?;
683                        Ok((PlaceElem::Field(field_id.into()), x.pat))
684                    })
685                    .collect::<Result<'db, Vec<_>>>()?;
686                self.pattern_match_adt(
687                    current,
688                    current_else,
689                    it.into_iter(),
690                    &downcast_place,
691                    mode,
692                )?
693            }
694            AdtPatternShape::Tuple { args, ellipsis } => {
695                let fields = variant_data.fields().iter().map(|(x, _)| PlaceElem::Field(x.into()));
696                self.pattern_match_tuple_like(
697                    current,
698                    current_else,
699                    args,
700                    ellipsis,
701                    fields,
702                    &downcast_place,
703                    mode,
704                )?
705            }
706            AdtPatternShape::Unit => (current, current_else),
707        })
708    }
709
710    fn pattern_match_adt(
711        &mut self,
712        mut current: BasicBlockId,
713        mut current_else: Option<BasicBlockId>,
714        args: impl Iterator<Item = (PlaceElem, PatId)>,
715        cond_place: &PlaceRef<'db>,
716        mode: MatchingMode,
717    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
718        for (proj, arg) in args {
719            let cond_place = cond_place.project(proj);
720            (current, current_else) =
721                self.pattern_match_inner(current, current_else, cond_place, arg, mode)?;
722        }
723        Ok((current, current_else))
724    }
725
726    fn pattern_match_tuple_like(
727        &mut self,
728        current: BasicBlockId,
729        current_else: Option<BasicBlockId>,
730        args: &[PatId],
731        ellipsis: Option<u32>,
732        fields: impl DoubleEndedIterator<Item = PlaceElem> + Clone,
733        cond_place: &PlaceRef<'db>,
734        mode: MatchingMode,
735    ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
736        let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
737        let it = al
738            .iter()
739            .zip(fields.clone())
740            .chain(ar.iter().rev().zip(fields.rev()))
741            .map(|(x, y)| (y, *x));
742        self.pattern_match_adt(current, current_else, it, cond_place, mode)
743    }
744}