rustc_mir_build/builder/matches/
test.rs

1// Testing candidates
2//
3// After candidates have been simplified, the only match pairs that
4// remain are those that require some sort of test. The functions here
5// identify what tests are needed, perform the tests, and then filter
6// the candidates based on the result.
7
8use std::cmp::Ordering;
9use std::sync::Arc;
10
11use rustc_data_structures::fx::FxIndexMap;
12use rustc_hir::{LangItem, RangeEnd};
13use rustc_middle::mir::*;
14use rustc_middle::ty::util::IntTypeExt;
15use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt};
16use rustc_middle::{bug, span_bug};
17use rustc_span::def_id::DefId;
18use rustc_span::source_map::Spanned;
19use rustc_span::{DUMMY_SP, Span, Symbol, sym};
20use tracing::{debug, instrument};
21
22use crate::builder::Builder;
23use crate::builder::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind};
24
25impl<'a, 'tcx> Builder<'a, 'tcx> {
26    /// Identifies what test is needed to decide if `match_pair` is applicable.
27    ///
28    /// It is a bug to call this with a not-fully-simplified pattern.
29    pub(super) fn pick_test_for_match_pair(
30        &mut self,
31        match_pair: &MatchPairTree<'tcx>,
32    ) -> Test<'tcx> {
33        let kind = match match_pair.test_case {
34            TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
35
36            TestCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If,
37            TestCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => TestKind::SwitchInt,
38            TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern_ty },
39
40            TestCase::Range(ref range) => {
41                assert_eq!(range.ty, match_pair.pattern_ty);
42                TestKind::Range(Arc::clone(range))
43            }
44
45            TestCase::Slice { len, variable_length } => {
46                let op = if variable_length { BinOp::Ge } else { BinOp::Eq };
47                TestKind::Len { len: len as u64, op }
48            }
49
50            TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
51
52            TestCase::Never => TestKind::Never,
53
54            // Or-patterns are not tested directly; instead they are expanded into subcandidates,
55            // which are then distinguished by testing whatever non-or patterns they contain.
56            TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
57        };
58
59        Test { span: match_pair.pattern_span, kind }
60    }
61
62    #[instrument(skip(self, target_blocks, place), level = "debug")]
63    pub(super) fn perform_test(
64        &mut self,
65        match_start_span: Span,
66        scrutinee_span: Span,
67        block: BasicBlock,
68        otherwise_block: BasicBlock,
69        place: Place<'tcx>,
70        test: &Test<'tcx>,
71        target_blocks: FxIndexMap<TestBranch<'tcx>, BasicBlock>,
72    ) {
73        let place_ty = place.ty(&self.local_decls, self.tcx);
74        debug!(?place, ?place_ty);
75        let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block);
76
77        let source_info = self.source_info(test.span);
78        match test.kind {
79            TestKind::Switch { adt_def } => {
80                let otherwise_block = target_block(TestBranch::Failure);
81                let switch_targets = SwitchTargets::new(
82                    adt_def.discriminants(self.tcx).filter_map(|(idx, discr)| {
83                        if let Some(&block) = target_blocks.get(&TestBranch::Variant(idx)) {
84                            Some((discr.val, block))
85                        } else {
86                            None
87                        }
88                    }),
89                    otherwise_block,
90                );
91                debug!("num_enum_variants: {}", adt_def.variants().len());
92                let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
93                let discr = self.temp(discr_ty, test.span);
94                self.cfg.push_assign(
95                    block,
96                    self.source_info(scrutinee_span),
97                    discr,
98                    Rvalue::Discriminant(place),
99                );
100                self.cfg.terminate(
101                    block,
102                    self.source_info(match_start_span),
103                    TerminatorKind::SwitchInt {
104                        discr: Operand::Move(discr),
105                        targets: switch_targets,
106                    },
107                );
108            }
109
110            TestKind::SwitchInt => {
111                // The switch may be inexhaustive so we have a catch-all block
112                let otherwise_block = target_block(TestBranch::Failure);
113                let switch_targets = SwitchTargets::new(
114                    target_blocks.iter().filter_map(|(&branch, &block)| {
115                        if let TestBranch::Constant(_, bits) = branch {
116                            Some((bits, block))
117                        } else {
118                            None
119                        }
120                    }),
121                    otherwise_block,
122                );
123                let terminator = TerminatorKind::SwitchInt {
124                    discr: Operand::Copy(place),
125                    targets: switch_targets,
126                };
127                self.cfg.terminate(block, self.source_info(match_start_span), terminator);
128            }
129
130            TestKind::If => {
131                let success_block = target_block(TestBranch::Success);
132                let fail_block = target_block(TestBranch::Failure);
133                let terminator =
134                    TerminatorKind::if_(Operand::Copy(place), success_block, fail_block);
135                self.cfg.terminate(block, self.source_info(match_start_span), terminator);
136            }
137
138            TestKind::Eq { value, mut ty } => {
139                let tcx = self.tcx;
140                let success_block = target_block(TestBranch::Success);
141                let fail_block = target_block(TestBranch::Failure);
142
143                let expect_ty = value.ty();
144                let expect = self.literal_operand(test.span, value);
145
146                let mut place = place;
147                let mut block = block;
148                match ty.kind() {
149                    ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => {
150                        if !tcx.features().string_deref_patterns() {
151                            span_bug!(
152                                test.span,
153                                "matching on `String` went through without enabling string_deref_patterns"
154                            );
155                        }
156                        let re_erased = tcx.lifetimes.re_erased;
157                        let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
158                        let ref_str = self.temp(ref_str_ty, test.span);
159                        let eq_block = self.cfg.start_new_block();
160                        // `let ref_str: &str = <String as Deref>::deref(&place);`
161                        self.call_deref(
162                            block,
163                            eq_block,
164                            place,
165                            Mutability::Not,
166                            ty,
167                            ref_str,
168                            test.span,
169                        );
170                        // Since we generated a `ref_str = <String as Deref>::deref(&place) -> eq_block` terminator,
171                        // we need to add all further statements to `eq_block`.
172                        // Similarly, the normal test code should be generated for the `&str`, instead of the `String`.
173                        block = eq_block;
174                        place = ref_str;
175                        ty = ref_str_ty;
176                    }
177                    _ => {}
178                }
179
180                assert_eq!(expect_ty, ty);
181                if !ty.is_scalar() {
182                    // Use `PartialEq::eq` instead of `BinOp::Eq`
183                    // (the binop can only handle primitives)
184                    // Make sure that we do *not* call any user-defined code here.
185                    // The only type that can end up here is string literals, which have their
186                    // comparison defined in `core`.
187                    // (Interestingly this means that exhaustiveness analysis relies, for soundness,
188                    // on the `PartialEq` impl for `str` to b correct!)
189                    match *ty.kind() {
190                        ty::Ref(_, deref_ty, _) if deref_ty == self.tcx.types.str_ => {}
191                        _ => {
192                            span_bug!(source_info.span, "invalid type for non-scalar compare: {ty}")
193                        }
194                    };
195                    self.string_compare(
196                        block,
197                        success_block,
198                        fail_block,
199                        source_info,
200                        expect,
201                        Operand::Copy(place),
202                    );
203                } else {
204                    self.compare(
205                        block,
206                        success_block,
207                        fail_block,
208                        source_info,
209                        BinOp::Eq,
210                        expect,
211                        Operand::Copy(place),
212                    );
213                }
214            }
215
216            TestKind::Range(ref range) => {
217                let success = target_block(TestBranch::Success);
218                let fail = target_block(TestBranch::Failure);
219                // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
220                let val = Operand::Copy(place);
221
222                let intermediate_block = if !range.lo.is_finite() {
223                    block
224                } else if !range.hi.is_finite() {
225                    success
226                } else {
227                    self.cfg.start_new_block()
228                };
229
230                if let Some(lo) = range.lo.as_finite() {
231                    let lo = self.literal_operand(test.span, lo);
232                    self.compare(
233                        block,
234                        intermediate_block,
235                        fail,
236                        source_info,
237                        BinOp::Le,
238                        lo,
239                        val.clone(),
240                    );
241                };
242
243                if let Some(hi) = range.hi.as_finite() {
244                    let hi = self.literal_operand(test.span, hi);
245                    let op = match range.end {
246                        RangeEnd::Included => BinOp::Le,
247                        RangeEnd::Excluded => BinOp::Lt,
248                    };
249                    self.compare(intermediate_block, success, fail, source_info, op, val, hi);
250                }
251            }
252
253            TestKind::Len { len, op } => {
254                let usize_ty = self.tcx.types.usize;
255                let actual = self.temp(usize_ty, test.span);
256
257                // actual = len(place)
258                self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place));
259
260                // expected = <N>
261                let expected = self.push_usize(block, source_info, len);
262
263                let success_block = target_block(TestBranch::Success);
264                let fail_block = target_block(TestBranch::Failure);
265                // result = actual == expected OR result = actual < expected
266                // branch based on result
267                self.compare(
268                    block,
269                    success_block,
270                    fail_block,
271                    source_info,
272                    op,
273                    Operand::Move(actual),
274                    Operand::Move(expected),
275                );
276            }
277
278            TestKind::Deref { temp, mutability } => {
279                let ty = place_ty.ty;
280                let target = target_block(TestBranch::Success);
281                self.call_deref(block, target, place, mutability, ty, temp, test.span);
282            }
283
284            TestKind::Never => {
285                // Check that the place is initialized.
286                // FIXME(never_patterns): Also assert validity of the data at `place`.
287                self.cfg.push_fake_read(
288                    block,
289                    source_info,
290                    FakeReadCause::ForMatchedPlace(None),
291                    place,
292                );
293                // A never pattern is only allowed on an uninhabited type, so validity of the data
294                // implies unreachability.
295                self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
296            }
297        }
298    }
299
300    /// Perform `let temp = <ty as Deref>::deref(&place)`.
301    /// or `let temp = <ty as DerefMut>::deref_mut(&mut place)`.
302    pub(super) fn call_deref(
303        &mut self,
304        block: BasicBlock,
305        target_block: BasicBlock,
306        place: Place<'tcx>,
307        mutability: Mutability,
308        ty: Ty<'tcx>,
309        temp: Place<'tcx>,
310        span: Span,
311    ) {
312        let (trait_item, method) = match mutability {
313            Mutability::Not => (LangItem::Deref, sym::deref),
314            Mutability::Mut => (LangItem::DerefMut, sym::deref_mut),
315        };
316        let borrow_kind = super::util::ref_pat_borrow_kind(mutability);
317        let source_info = self.source_info(span);
318        let re_erased = self.tcx.lifetimes.re_erased;
319        let trait_item = self.tcx.require_lang_item(trait_item, None);
320        let method = trait_method(self.tcx, trait_item, method, [ty]);
321        let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span);
322        // `let ref_src = &src_place;`
323        // or `let ref_src = &mut src_place;`
324        self.cfg.push_assign(
325            block,
326            source_info,
327            ref_src,
328            Rvalue::Ref(re_erased, borrow_kind, place),
329        );
330        // `let temp = <Ty as Deref>::deref(ref_src);`
331        // or `let temp = <Ty as DerefMut>::deref_mut(ref_src);`
332        self.cfg.terminate(
333            block,
334            source_info,
335            TerminatorKind::Call {
336                func: Operand::Constant(Box::new(ConstOperand {
337                    span,
338                    user_ty: None,
339                    const_: method,
340                })),
341                args: [Spanned { node: Operand::Move(ref_src), span }].into(),
342                destination: temp,
343                target: Some(target_block),
344                unwind: UnwindAction::Continue,
345                call_source: CallSource::Misc,
346                fn_span: source_info.span,
347            },
348        );
349    }
350
351    /// Compare using the provided built-in comparison operator
352    fn compare(
353        &mut self,
354        block: BasicBlock,
355        success_block: BasicBlock,
356        fail_block: BasicBlock,
357        source_info: SourceInfo,
358        op: BinOp,
359        left: Operand<'tcx>,
360        right: Operand<'tcx>,
361    ) {
362        let bool_ty = self.tcx.types.bool;
363        let result = self.temp(bool_ty, source_info.span);
364
365        // result = op(left, right)
366        self.cfg.push_assign(
367            block,
368            source_info,
369            result,
370            Rvalue::BinaryOp(op, Box::new((left, right))),
371        );
372
373        // branch based on result
374        self.cfg.terminate(
375            block,
376            source_info,
377            TerminatorKind::if_(Operand::Move(result), success_block, fail_block),
378        );
379    }
380
381    /// Compare two values of type `&str` using `<str as std::cmp::PartialEq>::eq`.
382    fn string_compare(
383        &mut self,
384        block: BasicBlock,
385        success_block: BasicBlock,
386        fail_block: BasicBlock,
387        source_info: SourceInfo,
388        expect: Operand<'tcx>,
389        val: Operand<'tcx>,
390    ) {
391        let str_ty = self.tcx.types.str_;
392        let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span));
393        let method = trait_method(self.tcx, eq_def_id, sym::eq, [str_ty, str_ty]);
394
395        let bool_ty = self.tcx.types.bool;
396        let eq_result = self.temp(bool_ty, source_info.span);
397        let eq_block = self.cfg.start_new_block();
398        self.cfg.terminate(
399            block,
400            source_info,
401            TerminatorKind::Call {
402                func: Operand::Constant(Box::new(ConstOperand {
403                    span: source_info.span,
404
405                    // FIXME(#54571): This constant comes from user input (a
406                    // constant in a pattern). Are there forms where users can add
407                    // type annotations here?  For example, an associated constant?
408                    // Need to experiment.
409                    user_ty: None,
410
411                    const_: method,
412                })),
413                args: [
414                    Spanned { node: val, span: DUMMY_SP },
415                    Spanned { node: expect, span: DUMMY_SP },
416                ]
417                .into(),
418                destination: eq_result,
419                target: Some(eq_block),
420                unwind: UnwindAction::Continue,
421                call_source: CallSource::MatchCmp,
422                fn_span: source_info.span,
423            },
424        );
425        self.diverge_from(block);
426
427        // check the result
428        self.cfg.terminate(
429            eq_block,
430            source_info,
431            TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block),
432        );
433    }
434
435    /// Given that we are performing `test` against `test_place`, this job
436    /// sorts out what the status of `candidate` will be after the test. See
437    /// `test_candidates` for the usage of this function. The candidate may
438    /// be modified to update its `match_pairs`.
439    ///
440    /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is
441    /// a variant test, then we would modify the candidate to be `(x as
442    /// Option).0 @ P0` and return the index corresponding to the variant
443    /// `Some`.
444    ///
445    /// However, in some cases, the test may just not be relevant to candidate.
446    /// For example, suppose we are testing whether `foo.x == 22`, but in one
447    /// match arm we have `Foo { x: _, ... }`... in that case, the test for
448    /// the value of `x` has no particular relevance to this candidate. In
449    /// such cases, this function just returns None without doing anything.
450    /// This is used by the overall `match_candidates` algorithm to structure
451    /// the match as a whole. See `match_candidates` for more details.
452    ///
453    /// FIXME(#29623). In some cases, we have some tricky choices to make. for
454    /// example, if we are testing that `x == 22`, but the candidate is `x @
455    /// 13..55`, what should we do? In the event that the test is true, we know
456    /// that the candidate applies, but in the event of false, we don't know
457    /// that it *doesn't* apply. For now, we return false, indicate that the
458    /// test does not apply to this candidate, but it might be we can get
459    /// tighter match code if we do something a bit different.
460    pub(super) fn sort_candidate(
461        &mut self,
462        test_place: Place<'tcx>,
463        test: &Test<'tcx>,
464        candidate: &mut Candidate<'tcx>,
465        sorted_candidates: &FxIndexMap<TestBranch<'tcx>, Vec<&mut Candidate<'tcx>>>,
466    ) -> Option<TestBranch<'tcx>> {
467        // Find the match_pair for this place (if any). At present,
468        // afaik, there can be at most one. (In the future, if we
469        // adopted a more general `@` operator, there might be more
470        // than one, but it'd be very unusual to have two sides that
471        // both require tests; you'd expect one side to be simplified
472        // away.)
473        let (match_pair_index, match_pair) = candidate
474            .match_pairs
475            .iter()
476            .enumerate()
477            .find(|&(_, mp)| mp.place == Some(test_place))?;
478
479        // If true, the match pair is completely entailed by its corresponding test
480        // branch, so it can be removed. If false, the match pair is _compatible_
481        // with its test branch, but still needs a more specific test.
482        let fully_matched;
483        let ret = match (&test.kind, &match_pair.test_case) {
484            // If we are performing a variant switch, then this
485            // informs variant patterns, but nothing else.
486            (
487                &TestKind::Switch { adt_def: tested_adt_def },
488                &TestCase::Variant { adt_def, variant_index },
489            ) => {
490                assert_eq!(adt_def, tested_adt_def);
491                fully_matched = true;
492                Some(TestBranch::Variant(variant_index))
493            }
494
495            // If we are performing a switch over integers, then this informs integer
496            // equality, but nothing else.
497            //
498            // FIXME(#29623) we could use PatKind::Range to rule
499            // things out here, in some cases.
500            (TestKind::SwitchInt, &TestCase::Constant { value })
501                if is_switch_ty(match_pair.pattern_ty) =>
502            {
503                // An important invariant of candidate sorting is that a candidate
504                // must not match in multiple branches. For `SwitchInt` tests, adding
505                // a new value might invalidate that property for range patterns that
506                // have already been sorted into the failure arm, so we must take care
507                // not to add such values here.
508                let is_covering_range = |test_case: &TestCase<'tcx>| {
509                    test_case.as_range().is_some_and(|range| {
510                        matches!(
511                            range.contains(value, self.tcx, self.typing_env()),
512                            None | Some(true)
513                        )
514                    })
515                };
516                let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| {
517                    candidate
518                        .match_pairs
519                        .iter()
520                        .any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case))
521                };
522                if sorted_candidates
523                    .get(&TestBranch::Failure)
524                    .is_some_and(|candidates| candidates.iter().any(is_conflicting_candidate))
525                {
526                    fully_matched = false;
527                    None
528                } else {
529                    fully_matched = true;
530                    let bits = value.eval_bits(self.tcx, self.typing_env());
531                    Some(TestBranch::Constant(value, bits))
532                }
533            }
534            (TestKind::SwitchInt, TestCase::Range(range)) => {
535                // When performing a `SwitchInt` test, a range pattern can be
536                // sorted into the failure arm if it doesn't contain _any_ of
537                // the values being tested. (This restricts what values can be
538                // added to the test by subsequent candidates.)
539                fully_matched = false;
540                let not_contained =
541                    sorted_candidates.keys().filter_map(|br| br.as_constant()).copied().all(
542                        |val| {
543                            matches!(range.contains(val, self.tcx, self.typing_env()), Some(false))
544                        },
545                    );
546
547                not_contained.then(|| {
548                    // No switch values are contained in the pattern range,
549                    // so the pattern can be matched only if this test fails.
550                    TestBranch::Failure
551                })
552            }
553
554            (TestKind::If, TestCase::Constant { value }) => {
555                fully_matched = true;
556                let value = value.try_eval_bool(self.tcx, self.typing_env()).unwrap_or_else(|| {
557                    span_bug!(test.span, "expected boolean value but got {value:?}")
558                });
559                Some(if value { TestBranch::Success } else { TestBranch::Failure })
560            }
561
562            (
563                &TestKind::Len { len: test_len, op: BinOp::Eq },
564                &TestCase::Slice { len, variable_length },
565            ) => {
566                match (test_len.cmp(&(len as u64)), variable_length) {
567                    (Ordering::Equal, false) => {
568                        // on true, min_len = len = $actual_length,
569                        // on false, len != $actual_length
570                        fully_matched = true;
571                        Some(TestBranch::Success)
572                    }
573                    (Ordering::Less, _) => {
574                        // test_len < pat_len. If $actual_len = test_len,
575                        // then $actual_len < pat_len and we don't have
576                        // enough elements.
577                        fully_matched = false;
578                        Some(TestBranch::Failure)
579                    }
580                    (Ordering::Equal | Ordering::Greater, true) => {
581                        // This can match both if $actual_len = test_len >= pat_len,
582                        // and if $actual_len > test_len. We can't advance.
583                        fully_matched = false;
584                        None
585                    }
586                    (Ordering::Greater, false) => {
587                        // test_len != pat_len, so if $actual_len = test_len, then
588                        // $actual_len != pat_len.
589                        fully_matched = false;
590                        Some(TestBranch::Failure)
591                    }
592                }
593            }
594            (
595                &TestKind::Len { len: test_len, op: BinOp::Ge },
596                &TestCase::Slice { len, variable_length },
597            ) => {
598                // the test is `$actual_len >= test_len`
599                match (test_len.cmp(&(len as u64)), variable_length) {
600                    (Ordering::Equal, true) => {
601                        // $actual_len >= test_len = pat_len,
602                        // so we can match.
603                        fully_matched = true;
604                        Some(TestBranch::Success)
605                    }
606                    (Ordering::Less, _) | (Ordering::Equal, false) => {
607                        // test_len <= pat_len. If $actual_len < test_len,
608                        // then it is also < pat_len, so the test passing is
609                        // necessary (but insufficient).
610                        fully_matched = false;
611                        Some(TestBranch::Success)
612                    }
613                    (Ordering::Greater, false) => {
614                        // test_len > pat_len. If $actual_len >= test_len > pat_len,
615                        // then we know we won't have a match.
616                        fully_matched = false;
617                        Some(TestBranch::Failure)
618                    }
619                    (Ordering::Greater, true) => {
620                        // test_len < pat_len, and is therefore less
621                        // strict. This can still go both ways.
622                        fully_matched = false;
623                        None
624                    }
625                }
626            }
627
628            (TestKind::Range(test), TestCase::Range(pat)) => {
629                if test == pat {
630                    fully_matched = true;
631                    Some(TestBranch::Success)
632                } else {
633                    fully_matched = false;
634                    // If the testing range does not overlap with pattern range,
635                    // the pattern can be matched only if this test fails.
636                    if !test.overlaps(pat, self.tcx, self.typing_env())? {
637                        Some(TestBranch::Failure)
638                    } else {
639                        None
640                    }
641                }
642            }
643            (TestKind::Range(range), &TestCase::Constant { value }) => {
644                fully_matched = false;
645                if !range.contains(value, self.tcx, self.typing_env())? {
646                    // `value` is not contained in the testing range,
647                    // so `value` can be matched only if this test fails.
648                    Some(TestBranch::Failure)
649                } else {
650                    None
651                }
652            }
653
654            (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => {
655                if test_val == case_val {
656                    fully_matched = true;
657                    Some(TestBranch::Success)
658                } else {
659                    fully_matched = false;
660                    Some(TestBranch::Failure)
661                }
662            }
663
664            (TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. })
665                if test_temp == temp =>
666            {
667                fully_matched = true;
668                Some(TestBranch::Success)
669            }
670
671            (TestKind::Never, _) => {
672                fully_matched = true;
673                Some(TestBranch::Success)
674            }
675
676            (
677                TestKind::Switch { .. }
678                | TestKind::SwitchInt { .. }
679                | TestKind::If
680                | TestKind::Len { .. }
681                | TestKind::Range { .. }
682                | TestKind::Eq { .. }
683                | TestKind::Deref { .. },
684                _,
685            ) => {
686                fully_matched = false;
687                None
688            }
689        };
690
691        if fully_matched {
692            // Replace the match pair by its sub-pairs.
693            let match_pair = candidate.match_pairs.remove(match_pair_index);
694            candidate.match_pairs.extend(match_pair.subpairs);
695            // Move or-patterns to the end.
696            candidate.sort_match_pairs();
697        }
698
699        ret
700    }
701}
702
703fn is_switch_ty(ty: Ty<'_>) -> bool {
704    ty.is_integral() || ty.is_char()
705}
706
707fn trait_method<'tcx>(
708    tcx: TyCtxt<'tcx>,
709    trait_def_id: DefId,
710    method_name: Symbol,
711    args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
712) -> Const<'tcx> {
713    // The unhygienic comparison here is acceptable because this is only
714    // used on known traits.
715    let item = tcx
716        .associated_items(trait_def_id)
717        .filter_by_name_unhygienic(method_name)
718        .find(|item| item.kind == ty::AssocKind::Fn)
719        .expect("trait method not found");
720
721    let method_ty = Ty::new_fn_def(tcx, item.def_id, args);
722
723    Const::zero_sized(method_ty)
724}