Skip to main content

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::sync::Arc;
9
10use rustc_data_structures::fx::FxIndexMap;
11use rustc_hir::RangeEnd;
12use rustc_hir::attrs::lang_items::LangItem;
13use rustc_middle::mir::*;
14use rustc_middle::ty::util::IntTypeExt;
15use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt};
16use rustc_span::def_id::DefId;
17use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, bug, sym};
18use tracing::{debug, instrument};
19
20use crate::builder::Builder;
21use crate::builder::matches::{
22    MatchPairKind, MatchPairTree, PatConstKind, SliceLenOp, Test, TestBranch, TestKind,
23    TestableCase,
24};
25
26impl<'a, 'tcx> Builder<'a, 'tcx> {
27    /// Identifies what test is needed to decide if `match_pair` is applicable.
28    ///
29    /// It is a bug to call this with a not-fully-simplified pattern.
30    pub(super) fn pick_test_for_match_pair(
31        &mut self,
32        match_pair: &MatchPairTree<'tcx>,
33    ) -> Test<'tcx> {
34        // Or-patterns are not tested directly; instead they are expanded into subcandidates,
35        // which are then distinguished by testing whatever non-or patterns they contain.
36        let MatchPairKind::Testable { ref testable_case, .. } = match_pair.kind else {
37            bug_impl(None, format_args!("or-patterns should have already been handled"),
    Location::caller())bug!("or-patterns should have already been handled")
38        };
39        let kind = match *testable_case {
40            TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
41
42            TestableCase::Constant { value: _, kind: PatConstKind::Bool } => TestKind::If,
43            TestableCase::Constant { value: _, kind: PatConstKind::IntOrChar } => {
44                TestKind::SwitchInt
45            }
46            TestableCase::Constant { value, kind: PatConstKind::String } => {
47                TestKind::StringEq { value }
48            }
49            TestableCase::Constant { value, kind: PatConstKind::Float | PatConstKind::Other } => {
50                TestKind::ScalarEq { value }
51            }
52
53            TestableCase::Range(ref range) => TestKind::Range(Arc::clone(range)),
54
55            TestableCase::Slice { len, op } => TestKind::SliceLen { len, op },
56
57            TestableCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
58
59            TestableCase::Never => TestKind::Never,
60        };
61
62        Test { span: match_pair.pattern_span, kind }
63    }
64
65    {}
#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("perform_test",
                                    "rustc_mir_build::builder::matches::test",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs"),
                                    ::tracing_core::__macro_support::Option::Some(65u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::matches::test"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("match_start_span")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("match_start_span");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("scrutinee_span")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("scrutinee_span");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("block")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("block");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("otherwise_block")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("otherwise_block");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("test")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("test");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&match_start_span)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&scrutinee_span)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&block)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&otherwise_block)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&test)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let place_ty = place.ty(&self.local_decls, self.tcx);
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("event /rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs:77",
                                    "rustc_mir_build::builder::matches::test",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs"),
                                    ::tracing_core::__macro_support::Option::Some(77u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::matches::test"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("place")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("place");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("place_ty")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("place_ty");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::EVENT)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let enabled =
                    ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        {
                            let interest = __CALLSITE.interest();
                            !interest.is_never() &&
                                ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                    interest)
                        };
                if enabled {
                    (|value_set: ::tracing::field::ValueSet|
                                {
                                    let meta = __CALLSITE.metadata();
                                    ::tracing::Event::dispatch(meta, &value_set);
                                    ;
                                })({
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&place)
                                                        as &dyn ::tracing::field::Value)),
                                            (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&place_ty)
                                                        as &dyn ::tracing::field::Value))])
                        });
                } else { ; }
            };
            let target_block =
                |branch|
                    target_blocks.get(&branch).copied().unwrap_or(otherwise_block);
            let source_info = self.source_info(test.span);
            match test.kind {
                TestKind::Switch { adt_def } => {
                    let otherwise_block = target_block(TestBranch::Failure);
                    let switch_targets =
                        SwitchTargets::new(adt_def.discriminants(self.tcx).filter_map(|(idx,
                                        discr)|
                                    {
                                        if let Some(&block) =
                                                target_blocks.get(&TestBranch::Variant(idx)) {
                                            Some((discr.val, block))
                                        } else { None }
                                    }), otherwise_block);
                    {
                        use ::tracing::__macro_support::Callsite as _;
                        static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                            {
                                static META: ::tracing::Metadata<'static> =
                                    {
                                        ::tracing_core::metadata::Metadata::new("event /rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs:94",
                                            "rustc_mir_build::builder::matches::test",
                                            ::tracing::Level::DEBUG,
                                            ::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/test.rs"),
                                            ::tracing_core::__macro_support::Option::Some(94u32),
                                            ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::matches::test"),
                                            ::tracing_core::field::FieldSet::new(&["message"],
                                                ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                            ::tracing::metadata::Kind::EVENT)
                                    };
                                ::tracing::callsite::DefaultCallsite::new(&META)
                            };
                        let enabled =
                            ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                    ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::LevelFilter::current() &&
                                {
                                    let interest = __CALLSITE.interest();
                                    !interest.is_never() &&
                                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                            interest)
                                };
                        if enabled {
                            (|value_set: ::tracing::field::ValueSet|
                                        {
                                            let meta = __CALLSITE.metadata();
                                            ::tracing::Event::dispatch(meta, &value_set);
                                            ;
                                        })({
                                    #[allow(unused_imports)]
                                    use ::tracing::field::{debug, display, Value};
                                    __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("num_enum_variants: {0}",
                                                                        adt_def.variants().len()) as
                                                                &dyn ::tracing::field::Value))])
                                });
                        } else { ; }
                    };
                    let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
                    let discr = self.temp(discr_ty, test.span);
                    self.cfg.push_assign(block,
                        self.source_info(scrutinee_span), discr,
                        Rvalue::Discriminant(place));
                    self.cfg.terminate(block,
                        self.source_info(match_start_span),
                        TerminatorKind::SwitchInt {
                            discr: Operand::Move(discr),
                            targets: switch_targets,
                        });
                }
                TestKind::SwitchInt => {
                    let otherwise_block = target_block(TestBranch::Failure);
                    let switch_targets =
                        SwitchTargets::new(target_blocks.iter().filter_map(|(&branch,
                                        &block)|
                                    {
                                        if let TestBranch::Constant(value) = branch {
                                            let bits = value.to_leaf().to_bits_unchecked();
                                            Some((bits, block))
                                        } else { None }
                                    }), otherwise_block);
                    let terminator =
                        TerminatorKind::SwitchInt {
                            discr: Operand::Copy(place),
                            targets: switch_targets,
                        };
                    self.cfg.terminate(block,
                        self.source_info(match_start_span), terminator);
                }
                TestKind::If => {
                    let success_block = target_block(TestBranch::Success);
                    let fail_block = target_block(TestBranch::Failure);
                    let terminator =
                        TerminatorKind::if_(Operand::Copy(place), success_block,
                            fail_block);
                    self.cfg.terminate(block,
                        self.source_info(match_start_span), terminator);
                }
                TestKind::StringEq { value } => {
                    let tcx = self.tcx;
                    let success_block = target_block(TestBranch::Success);
                    let fail_block = target_block(TestBranch::Failure);
                    let ref_str_ty =
                        Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased,
                            tcx.types.str_);
                    if !ref_str_ty.is_imm_ref_str() {
                        {
                            ::core::panicking::panic_fmt(format_args!("{0:?}",
                                    ref_str_ty));
                        }
                    };
                    if !value.ty.is_str() {
                        {
                            ::core::panicking::panic_fmt(format_args!("unexpected value type for StringEq test: {0:?}",
                                    value));
                        }
                    };
                    let expected_value =
                        ty::Value { ty: ref_str_ty, valtree: value.valtree };
                    let expected_value_operand =
                        self.literal_operand(test.span,
                            Const::from_ty_value(tcx, expected_value));
                    let actual_value_ref_place =
                        self.temp(ref_str_ty, test.span);
                    self.cfg.push_assign(block, self.source_info(test.span),
                        actual_value_ref_place,
                        Rvalue::Ref(tcx.lifetimes.re_erased, BorrowKind::Shared,
                            place));
                    self.string_compare(block, success_block, fail_block,
                        source_info, expected_value_operand,
                        Operand::Copy(actual_value_ref_place));
                }
                TestKind::ScalarEq { value } => {
                    let tcx = self.tcx;
                    let success_block = target_block(TestBranch::Success);
                    let fail_block = target_block(TestBranch::Failure);
                    let mut expected_value_ty = value.ty;
                    let mut expected_value_operand =
                        self.literal_operand(test.span,
                            Const::from_ty_value(tcx, value));
                    let mut actual_value_place = place;
                    match value.ty.kind() {
                        &ty::Pat(base, _) => {
                            if !base.is_trivially_pure_clone_copy() {
                                ::core::panicking::panic("assertion failed: base.is_trivially_pure_clone_copy()")
                            };
                            let transmuted_place = self.temp(base, test.span);
                            self.cfg.push_assign(block,
                                self.source_info(scrutinee_span), transmuted_place,
                                Rvalue::Cast(CastKind::Transmute,
                                    Operand::Copy(actual_value_place), base));
                            let transmuted_expect = self.temp(base, test.span);
                            self.cfg.push_assign(block, self.source_info(test.span),
                                transmuted_expect,
                                Rvalue::Cast(CastKind::Transmute, expected_value_operand,
                                    base));
                            actual_value_place = transmuted_place;
                            expected_value_operand = Operand::Copy(transmuted_expect);
                            expected_value_ty = base;
                        }
                        _ => {}
                    }
                    if !expected_value_ty.is_scalar() {
                        ::core::panicking::panic("assertion failed: expected_value_ty.is_scalar()")
                    };
                    self.compare(block, success_block, fail_block, source_info,
                        BinOp::Eq, expected_value_operand,
                        Operand::Copy(actual_value_place));
                }
                TestKind::Range(ref range) => {
                    let success = target_block(TestBranch::Success);
                    let fail = target_block(TestBranch::Failure);
                    let val = Operand::Copy(place);
                    let intermediate_block =
                        if !range.lo.is_finite() {
                            block
                        } else if !range.hi.is_finite() {
                            success
                        } else { self.cfg.start_new_block() };
                    if let Some(lo) = range.lo.as_finite() {
                        let lo = ty::Value { ty: range.ty, valtree: lo };
                        let lo =
                            self.literal_operand(test.span,
                                Const::from_ty_value(self.tcx, lo));
                        self.compare(block, intermediate_block, fail, source_info,
                            BinOp::Le, lo, val.clone());
                    };
                    if let Some(hi) = range.hi.as_finite() {
                        let hi = ty::Value { ty: range.ty, valtree: hi };
                        let hi =
                            self.literal_operand(test.span,
                                Const::from_ty_value(self.tcx, hi));
                        let op =
                            match range.end {
                                RangeEnd::Included => BinOp::Le,
                                RangeEnd::Excluded => BinOp::Lt,
                            };
                        self.compare(intermediate_block, success, fail, source_info,
                            op, val, hi);
                    }
                }
                TestKind::SliceLen { len, op } => {
                    let usize_ty = self.tcx.types.usize;
                    let actual = self.temp(usize_ty, test.span);
                    let length_op =
                        self.len_of_slice_or_array(block, place, test.span,
                            source_info);
                    self.cfg.push_assign(block, source_info, actual,
                        Rvalue::Use(length_op, WithRetag::Yes));
                    let expected = self.push_usize(block, source_info, len);
                    let success_block = target_block(TestBranch::Success);
                    let fail_block = target_block(TestBranch::Failure);
                    self.compare(block, success_block, fail_block, source_info,
                        match op {
                            SliceLenOp::Equal => BinOp::Eq,
                            SliceLenOp::GreaterOrEqual => BinOp::Ge,
                        }, Operand::Move(actual), Operand::Move(expected));
                }
                TestKind::Deref { temp, mutability } => {
                    let ty = place_ty.ty;
                    let target = target_block(TestBranch::Success);
                    self.call_deref(block, target, place, mutability, ty, temp,
                        test.span);
                }
                TestKind::Never => {
                    self.cfg.push_fake_read(block, source_info,
                        FakeReadCause::ForMatchedPlace(None), place);
                    self.cfg.terminate(block, source_info,
                        TerminatorKind::Unreachable);
                }
            }
        }
    }
}#[instrument(skip(self, target_blocks, place), level = "debug")]
66    pub(super) fn perform_test(
67        &mut self,
68        match_start_span: Span,
69        scrutinee_span: Span,
70        block: BasicBlock,
71        otherwise_block: BasicBlock,
72        place: Place<'tcx>,
73        test: &Test<'tcx>,
74        target_blocks: FxIndexMap<TestBranch<'tcx>, BasicBlock>,
75    ) {
76        let place_ty = place.ty(&self.local_decls, self.tcx);
77        debug!(?place, ?place_ty);
78        let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block);
79
80        let source_info = self.source_info(test.span);
81        match test.kind {
82            TestKind::Switch { adt_def } => {
83                let otherwise_block = target_block(TestBranch::Failure);
84                let switch_targets = SwitchTargets::new(
85                    adt_def.discriminants(self.tcx).filter_map(|(idx, discr)| {
86                        if let Some(&block) = target_blocks.get(&TestBranch::Variant(idx)) {
87                            Some((discr.val, block))
88                        } else {
89                            None
90                        }
91                    }),
92                    otherwise_block,
93                );
94                debug!("num_enum_variants: {}", adt_def.variants().len());
95                let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
96                let discr = self.temp(discr_ty, test.span);
97                self.cfg.push_assign(
98                    block,
99                    self.source_info(scrutinee_span),
100                    discr,
101                    Rvalue::Discriminant(place),
102                );
103                self.cfg.terminate(
104                    block,
105                    self.source_info(match_start_span),
106                    TerminatorKind::SwitchInt {
107                        discr: Operand::Move(discr),
108                        targets: switch_targets,
109                    },
110                );
111            }
112
113            TestKind::SwitchInt => {
114                // The switch may be inexhaustive so we have a catch-all block
115                let otherwise_block = target_block(TestBranch::Failure);
116                let switch_targets = SwitchTargets::new(
117                    target_blocks.iter().filter_map(|(&branch, &block)| {
118                        if let TestBranch::Constant(value) = branch {
119                            let bits = value.to_leaf().to_bits_unchecked();
120                            Some((bits, block))
121                        } else {
122                            None
123                        }
124                    }),
125                    otherwise_block,
126                );
127                let terminator = TerminatorKind::SwitchInt {
128                    discr: Operand::Copy(place),
129                    targets: switch_targets,
130                };
131                self.cfg.terminate(block, self.source_info(match_start_span), terminator);
132            }
133
134            TestKind::If => {
135                let success_block = target_block(TestBranch::Success);
136                let fail_block = target_block(TestBranch::Failure);
137                let terminator =
138                    TerminatorKind::if_(Operand::Copy(place), success_block, fail_block);
139                self.cfg.terminate(block, self.source_info(match_start_span), terminator);
140            }
141
142            TestKind::StringEq { value } => {
143                let tcx = self.tcx;
144                let success_block = target_block(TestBranch::Success);
145                let fail_block = target_block(TestBranch::Failure);
146
147                let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, tcx.types.str_);
148                assert!(ref_str_ty.is_imm_ref_str(), "{ref_str_ty:?}");
149
150                // The string constant we're testing against has type `str`, but
151                // calling `<str as PartialEq>::eq` requires `&str` operands.
152                //
153                // Because `str` and `&str` have the same valtree representation,
154                // we can "cast" to the desired type by just replacing the type.
155                assert!(value.ty.is_str(), "unexpected value type for StringEq test: {value:?}");
156                let expected_value = ty::Value { ty: ref_str_ty, valtree: value.valtree };
157                let expected_value_operand =
158                    self.literal_operand(test.span, Const::from_ty_value(tcx, expected_value));
159
160                // Similarly, the scrutinized place has type `str`, but we need `&str`.
161                // Get a reference by doing `let actual_value_ref_place: &str = &place`.
162                let actual_value_ref_place = self.temp(ref_str_ty, test.span);
163                self.cfg.push_assign(
164                    block,
165                    self.source_info(test.span),
166                    actual_value_ref_place,
167                    Rvalue::Ref(tcx.lifetimes.re_erased, BorrowKind::Shared, place),
168                );
169
170                // Compare two strings using `<str as std::cmp::PartialEq>::eq`.
171                // (Interestingly this means that exhaustiveness analysis relies, for soundness,
172                // on the `PartialEq` impl for `str` to be correct!)
173                self.string_compare(
174                    block,
175                    success_block,
176                    fail_block,
177                    source_info,
178                    expected_value_operand,
179                    Operand::Copy(actual_value_ref_place),
180                );
181            }
182
183            TestKind::ScalarEq { value } => {
184                let tcx = self.tcx;
185                let success_block = target_block(TestBranch::Success);
186                let fail_block = target_block(TestBranch::Failure);
187
188                let mut expected_value_ty = value.ty;
189                let mut expected_value_operand =
190                    self.literal_operand(test.span, Const::from_ty_value(tcx, value));
191
192                let mut actual_value_place = place;
193
194                match value.ty.kind() {
195                    &ty::Pat(base, _) => {
196                        assert!(base.is_trivially_pure_clone_copy());
197
198                        let transmuted_place = self.temp(base, test.span);
199                        self.cfg.push_assign(
200                            block,
201                            self.source_info(scrutinee_span),
202                            transmuted_place,
203                            Rvalue::Cast(
204                                CastKind::Transmute,
205                                Operand::Copy(actual_value_place),
206                                base,
207                            ),
208                        );
209
210                        let transmuted_expect = self.temp(base, test.span);
211                        self.cfg.push_assign(
212                            block,
213                            self.source_info(test.span),
214                            transmuted_expect,
215                            Rvalue::Cast(CastKind::Transmute, expected_value_operand, base),
216                        );
217
218                        actual_value_place = transmuted_place;
219                        expected_value_operand = Operand::Copy(transmuted_expect);
220                        expected_value_ty = base;
221                    }
222                    _ => {}
223                }
224
225                assert!(expected_value_ty.is_scalar());
226
227                self.compare(
228                    block,
229                    success_block,
230                    fail_block,
231                    source_info,
232                    BinOp::Eq,
233                    expected_value_operand,
234                    Operand::Copy(actual_value_place),
235                );
236            }
237
238            TestKind::Range(ref range) => {
239                let success = target_block(TestBranch::Success);
240                let fail = target_block(TestBranch::Failure);
241                // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
242                let val = Operand::Copy(place);
243
244                let intermediate_block = if !range.lo.is_finite() {
245                    block
246                } else if !range.hi.is_finite() {
247                    success
248                } else {
249                    self.cfg.start_new_block()
250                };
251
252                if let Some(lo) = range.lo.as_finite() {
253                    let lo = ty::Value { ty: range.ty, valtree: lo };
254                    let lo = self.literal_operand(test.span, Const::from_ty_value(self.tcx, lo));
255                    self.compare(
256                        block,
257                        intermediate_block,
258                        fail,
259                        source_info,
260                        BinOp::Le,
261                        lo,
262                        val.clone(),
263                    );
264                };
265
266                if let Some(hi) = range.hi.as_finite() {
267                    let hi = ty::Value { ty: range.ty, valtree: hi };
268                    let hi = self.literal_operand(test.span, Const::from_ty_value(self.tcx, hi));
269                    let op = match range.end {
270                        RangeEnd::Included => BinOp::Le,
271                        RangeEnd::Excluded => BinOp::Lt,
272                    };
273                    self.compare(intermediate_block, success, fail, source_info, op, val, hi);
274                }
275            }
276
277            TestKind::SliceLen { len, op } => {
278                let usize_ty = self.tcx.types.usize;
279                let actual = self.temp(usize_ty, test.span);
280
281                // actual = len(place)
282                let length_op = self.len_of_slice_or_array(block, place, test.span, source_info);
283                self.cfg.push_assign(
284                    block,
285                    source_info,
286                    actual,
287                    Rvalue::Use(length_op, WithRetag::Yes),
288                );
289
290                // expected = <N>
291                let expected = self.push_usize(block, source_info, len);
292
293                let success_block = target_block(TestBranch::Success);
294                let fail_block = target_block(TestBranch::Failure);
295                // result = actual == expected OR result = actual < expected
296                // branch based on result
297                self.compare(
298                    block,
299                    success_block,
300                    fail_block,
301                    source_info,
302                    match op {
303                        SliceLenOp::Equal => BinOp::Eq,
304                        SliceLenOp::GreaterOrEqual => BinOp::Ge,
305                    },
306                    Operand::Move(actual),
307                    Operand::Move(expected),
308                );
309            }
310
311            TestKind::Deref { temp, mutability } => {
312                let ty = place_ty.ty;
313                let target = target_block(TestBranch::Success);
314                self.call_deref(block, target, place, mutability, ty, temp, test.span);
315            }
316
317            TestKind::Never => {
318                // Check that the place is initialized.
319                // FIXME(never_patterns): Also assert validity of the data at `place`.
320                self.cfg.push_fake_read(
321                    block,
322                    source_info,
323                    FakeReadCause::ForMatchedPlace(None),
324                    place,
325                );
326                // A never pattern is only allowed on an uninhabited type, so validity of the data
327                // implies unreachability.
328                self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
329            }
330        }
331    }
332
333    /// Perform `let temp = <ty as Deref>::deref(&place)`.
334    /// or `let temp = <ty as DerefMut>::deref_mut(&mut place)`.
335    pub(super) fn call_deref(
336        &mut self,
337        block: BasicBlock,
338        target_block: BasicBlock,
339        place: Place<'tcx>,
340        mutability: Mutability,
341        ty: Ty<'tcx>,
342        temp: Place<'tcx>,
343        span: Span,
344    ) {
345        let (trait_item, method) = match mutability {
346            Mutability::Not => (LangItem::Deref, sym::deref),
347            Mutability::Mut => (LangItem::DerefMut, sym::deref_mut),
348        };
349        let borrow_kind = super::util::ref_pat_borrow_kind(mutability);
350        let source_info = self.source_info(span);
351        let re_erased = self.tcx.lifetimes.re_erased;
352        let trait_item = self.tcx.require_lang_item(trait_item, span);
353        let method = trait_method(self.tcx, trait_item, method, &[ty.into()]);
354        let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span);
355        // `let ref_src = &src_place;`
356        // or `let ref_src = &mut src_place;`
357        self.cfg.push_assign(
358            block,
359            source_info,
360            ref_src,
361            Rvalue::Ref(re_erased, borrow_kind, place),
362        );
363        // `let temp = <Ty as Deref>::deref(ref_src);`
364        // or `let temp = <Ty as DerefMut>::deref_mut(ref_src);`
365        self.cfg.terminate(
366            block,
367            source_info,
368            TerminatorKind::Call {
369                func: Operand::Constant(Box::new(ConstOperand {
370                    span,
371                    user_ty: None,
372                    const_: method,
373                })),
374                args: [Spanned { node: Operand::Move(ref_src), span }].into(),
375                destination: temp,
376                target: Some(target_block),
377                unwind: UnwindAction::Continue,
378                call_source: CallSource::Misc,
379                fn_span: source_info.span,
380            },
381        );
382    }
383
384    /// Compare using the provided built-in comparison operator
385    fn compare(
386        &mut self,
387        block: BasicBlock,
388        success_block: BasicBlock,
389        fail_block: BasicBlock,
390        source_info: SourceInfo,
391        op: BinOp,
392        left: Operand<'tcx>,
393        right: Operand<'tcx>,
394    ) {
395        let bool_ty = self.tcx.types.bool;
396        let result = self.temp(bool_ty, source_info.span);
397
398        // result = op(left, right)
399        self.cfg.push_assign(
400            block,
401            source_info,
402            result,
403            Rvalue::BinaryOp(op, Box::new((left, right))),
404        );
405
406        // branch based on result
407        self.cfg.terminate(
408            block,
409            source_info,
410            TerminatorKind::if_(Operand::Move(result), success_block, fail_block),
411        );
412    }
413
414    /// Compare two values of type `&str` using `<str as std::cmp::PartialEq>::eq`.
415    fn string_compare(
416        &mut self,
417        block: BasicBlock,
418        success_block: BasicBlock,
419        fail_block: BasicBlock,
420        source_info: SourceInfo,
421        expect: Operand<'tcx>,
422        val: Operand<'tcx>,
423    ) {
424        let str_ty = self.tcx.types.str_;
425        let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, source_info.span);
426        let method = trait_method(self.tcx, eq_def_id, sym::eq, &[str_ty.into(), str_ty.into()]);
427
428        let bool_ty = self.tcx.types.bool;
429        let eq_result = self.temp(bool_ty, source_info.span);
430        let eq_block = self.cfg.start_new_block();
431        self.cfg.terminate(
432            block,
433            source_info,
434            TerminatorKind::Call {
435                func: Operand::Constant(Box::new(ConstOperand {
436                    span: source_info.span,
437
438                    // FIXME(#54571): This constant comes from user input (a
439                    // constant in a pattern). Are there forms where users can add
440                    // type annotations here?  For example, an associated constant?
441                    // Need to experiment.
442                    user_ty: None,
443
444                    const_: method,
445                })),
446                args: [
447                    Spanned { node: val, span: DUMMY_SP },
448                    Spanned { node: expect, span: DUMMY_SP },
449                ]
450                .into(),
451                destination: eq_result,
452                target: Some(eq_block),
453                unwind: UnwindAction::Continue,
454                call_source: CallSource::MatchCmp,
455                fn_span: source_info.span,
456            },
457        );
458        self.diverge_from(block);
459
460        // check the result
461        self.cfg.terminate(
462            eq_block,
463            source_info,
464            TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block),
465        );
466    }
467}
468
469fn trait_method<'tcx>(
470    tcx: TyCtxt<'tcx>,
471    trait_def_id: DefId,
472    method_name: Symbol,
473    args: &[GenericArg<'tcx>],
474) -> Const<'tcx> {
475    // The unhygienic comparison here is acceptable because this is only
476    // used on known traits.
477    let item = tcx
478        .associated_items(trait_def_id)
479        .filter_by_name_unhygienic(method_name)
480        .find(|item| item.is_fn())
481        .expect("trait method not found");
482
483    Const::zero_sized(tcx.type_of(item.def_id).instantiate(tcx, args).skip_norm_wip())
484}