hir_ty/mir/lower/
pattern_matching.rs

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