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