Skip to main content

rustc_mir_build/builder/matches/
util.rs

1use rustc_data_structures::fx::FxIndexMap;
2use rustc_middle::mir::*;
3use rustc_middle::ty::Ty;
4use rustc_span::Span;
5use tracing::debug;
6
7use crate::builder::Builder;
8use crate::builder::expr::as_place::PlaceBase;
9use crate::builder::matches::{
10    Binding, Candidate, FlatPat, MatchPairKind, MatchPairTree, TestableCase,
11};
12
13impl<'a, 'tcx> Builder<'a, 'tcx> {
14    /// Creates a false edge to `imaginary_target` and a real edge to
15    /// real_target. If `imaginary_target` is none, or is the same as the real
16    /// target, a Goto is generated instead to simplify the generated MIR.
17    pub(crate) fn false_edges(
18        &mut self,
19        from_block: BasicBlock,
20        real_target: BasicBlock,
21        imaginary_target: BasicBlock,
22        source_info: SourceInfo,
23    ) {
24        if imaginary_target != real_target {
25            self.cfg.terminate(
26                from_block,
27                source_info,
28                TerminatorKind::FalseEdge { real_target, imaginary_target },
29            );
30        } else {
31            self.cfg.goto(from_block, source_info, real_target);
32        }
33    }
34}
35
36/// Determine the set of places that have to be stable across match guards.
37///
38/// Returns a list of places that need a fake borrow along with a local to store it.
39///
40/// Match exhaustiveness checking is not able to handle the case where the place being matched on is
41/// mutated in the guards. We add "fake borrows" to the guards that prevent any mutation of the
42/// place being matched. There are a some subtleties:
43///
44/// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared reference, the borrow
45///    isn't even tracked. As such we have to add fake borrows of any prefixes of a place.
46/// 2. We don't want `match x { (Some(_), _) => (), .. }` to conflict with mutable borrows of `x.1`, so we
47///    only add fake borrows for places which are bound or tested by the match.
48/// 3. We don't want `match x { Some(_) => (), .. }` to conflict with mutable borrows of `(x as
49///    Some).0`, so the borrows are a special shallow borrow that only affects the place and not its
50///    projections.
51///    ```rust
52///    let mut x = (Some(0), true);
53///    match x {
54///        (Some(_), false) => {}
55///        _ if { if let Some(ref mut y) = x.0 { *y += 1 }; true } => {}
56///        _ => {}
57///    }
58///    ```
59/// 4. The fake borrows may be of places in inactive variants, e.g. here we need to fake borrow `x`
60///    and `(x as Some).0`, but when we reach the guard `x` may not be `Some`.
61///    ```rust
62///    let mut x = (Some(Some(0)), true);
63///    match x {
64///        (Some(Some(_)), false) => {}
65///        _ if { if let Some(Some(ref mut y)) = x.0 { *y += 1 }; true } => {}
66///        _ => {}
67///    }
68///    ```
69///    So it would be UB to generate code for the fake borrows. They therefore have to be removed by
70///    a MIR pass run after borrow checking.
71pub(super) fn collect_fake_borrows<'tcx>(
72    cx: &mut Builder<'_, 'tcx>,
73    candidates: &[Candidate<'tcx>],
74    temp_span: Span,
75    scrutinee_base: PlaceBase,
76) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
77    if candidates.iter().all(|candidate| !candidate.has_guard) {
78        // Fake borrows are only used when there is a guard.
79        return Vec::new();
80    }
81    let mut collector =
82        FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() };
83    for candidate in candidates.iter() {
84        collector.visit_candidate(candidate);
85    }
86    let fake_borrows = collector.fake_borrows;
87    {
    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/util.rs:87",
                        "rustc_mir_build::builder::matches::util",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_mir_build/src/builder/matches/util.rs"),
                        ::tracing_core::__macro_support::Option::Some(87u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::matches::util"),
                        ::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!("add_fake_borrows fake_borrows = {0:?}",
                                                    fake_borrows) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
88    let tcx = cx.tcx;
89    fake_borrows
90        .iter()
91        .map(|(matched_place, borrow_kind)| {
92            let fake_borrow_deref_ty = matched_place.ty(&cx.local_decls, tcx).ty;
93            let fake_borrow_ty =
94                Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
95            let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
96            fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
97            let fake_borrow_temp = cx.local_decls.push(fake_borrow_temp);
98            (*matched_place, fake_borrow_temp, *borrow_kind)
99        })
100        .collect()
101}
102
103pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
104    cx: &'a mut Builder<'b, 'tcx>,
105    /// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from
106    /// bindings inside deref patterns.
107    scrutinee_base: PlaceBase,
108    /// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest
109    /// borrow (i.e. Deep > Shallow).
110    /// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are
111    /// dereferences are also borrowed with the same of stronger borrow kind.
112    fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>,
113}
114
115impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
116    // Fake borrow this place and its dereference prefixes.
117    fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
118        if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
119            return;
120        }
121        self.fake_borrows.insert(place, kind);
122        // Also fake borrow the prefixes of any fake borrow.
123        self.fake_borrow_deref_prefixes(place, kind);
124    }
125
126    // Fake borrow the prefixes of this place that are dereferences.
127    fn fake_borrow_deref_prefixes(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
128        for (place_ref, elem) in place.as_ref().iter_projections().rev() {
129            if let ProjectionElem::Deref = elem {
130                // Insert a shallow borrow after a deref. For other projections the borrow of
131                // `place_ref` will conflict with any mutation of `place.base`.
132                let place = place_ref.to_place(self.cx.tcx);
133                if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
134                    return;
135                }
136                self.fake_borrows.insert(place, kind);
137            }
138        }
139    }
140
141    fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) {
142        for binding in &candidate.extra_data.bindings {
143            if let super::SubpatternBindings::One(binding) = binding {
144                self.visit_binding(binding);
145            }
146        }
147        for match_pair in &candidate.match_pairs {
148            self.visit_match_pair(match_pair);
149        }
150    }
151
152    fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'tcx>) {
153        for binding in &flat_pat.extra_data.bindings {
154            if let super::SubpatternBindings::One(binding) = binding {
155                self.visit_binding(binding);
156            }
157        }
158        for match_pair in &flat_pat.match_pairs {
159            self.visit_match_pair(match_pair);
160        }
161    }
162
163    fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'tcx>) {
164        match match_pair.kind {
165            MatchPairKind::Or { ref or_subpats } => {
166                for flat_pat in or_subpats {
167                    self.visit_flat_pat(flat_pat);
168                }
169            }
170            MatchPairKind::Testable { place, ref testable_case, ref subpairs } => {
171                if #[allow(non_exhaustive_omitted_patterns)] match testable_case {
    TestableCase::Deref { .. } => true,
    _ => false,
}matches!(testable_case, TestableCase::Deref { .. }) {
172                    // The subpairs of a deref pattern are all places relative to the deref temporary, so we
173                    // don't fake borrow them. Problem is, if we only shallowly fake-borrowed
174                    // `match_pair.place`, this would allow:
175                    // ```
176                    // let mut b = Box::new(false);
177                    // match b {
178                    //     deref!(true) => {} // not reached because `*b == false`
179                    //     _ if { *b = true; false } => {} // not reached because the guard is `false`
180                    //     deref!(false) => {} // not reached because the guard changed it
181                    //     // UB because we reached the unreachable.
182                    // }
183                    // ```
184                    // Hence we fake borrow using a deep borrow.
185                    self.fake_borrow(place, FakeBorrowKind::Deep);
186                } else {
187                    // Insert a Shallow borrow of any place that is switched on.
188                    self.fake_borrow(place, FakeBorrowKind::Shallow);
189
190                    for subpair in subpairs {
191                        self.visit_match_pair(subpair);
192                    }
193                }
194            }
195        }
196    }
197
198    fn visit_binding(&mut self, Binding { source, .. }: &Binding<'tcx>) {
199        if let PlaceBase::Local(l) = self.scrutinee_base
200            && l != source.local
201        {
202            // The base of this place is a temporary created for deref patterns. We don't emit fake
203            // borrows for these as they are not initialized in all branches.
204            return;
205        }
206
207        // Insert a borrows of prefixes of places that are bound and are
208        // behind a dereference projection.
209        //
210        // These borrows are taken to avoid situations like the following:
211        //
212        // match x[10] {
213        //     _ if { x = &[0]; false } => (),
214        //     y => (), // Out of bounds array access!
215        // }
216        //
217        // match *x {
218        //     // y is bound by reference in the guard and then by copy in the
219        //     // arm, so y is 2 in the arm!
220        //     y if { y == 1 && (x = &2) == () } => y,
221        //     _ => 3,
222        // }
223        //
224        // We don't just fake borrow the whole place because this is allowed:
225        // match u {
226        //     _ if { u = true; false } => (),
227        //     x => (),
228        // }
229        self.fake_borrow_deref_prefixes(*source, FakeBorrowKind::Shallow);
230    }
231}
232
233#[must_use]
234pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind {
235    match ref_mutability {
236        Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
237        Mutability::Not => BorrowKind::Shared,
238    }
239}