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