rustc_borrowck/
path_utils.rs

1use std::ops::ControlFlow;
2
3use rustc_abi::FieldIdx;
4use rustc_data_structures::graph::dominators::Dominators;
5use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem};
6use rustc_middle::ty::TyCtxt;
7use tracing::debug;
8
9use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
10use crate::{AccessDepth, BorrowIndex, places_conflict};
11
12/// Encapsulates the idea of iterating over every borrow that involves a particular path
13pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
14    s: &mut S,
15    tcx: TyCtxt<'tcx>,
16    body: &Body<'tcx>,
17    access_place: (AccessDepth, Place<'tcx>),
18    borrow_set: &BorrowSet<'tcx>,
19    is_candidate: I,
20    mut op: F,
21) where
22    F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> ControlFlow<()>,
23    I: Fn(BorrowIndex) -> bool,
24{
25    let (access, place) = access_place;
26
27    // The number of candidates can be large, but borrows for different locals cannot conflict with
28    // each other, so we restrict the working set a priori.
29    let Some(borrows_for_place_base) = borrow_set.local_map.get(&place.local) else { return };
30
31    // check for loan restricting path P being used. Accounts for
32    // borrows of P, P.a.b, etc.
33    for &i in borrows_for_place_base {
34        if !is_candidate(i) {
35            continue;
36        }
37        let borrowed = &borrow_set[i];
38
39        if places_conflict::borrow_conflicts_with_place(
40            tcx,
41            body,
42            borrowed.borrowed_place,
43            borrowed.kind,
44            place.as_ref(),
45            access,
46            places_conflict::PlaceConflictBias::Overlap,
47        ) {
48            debug!(
49                "each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
50                i, borrowed, place, access
51            );
52            let ctrl = op(s, i, borrowed);
53            if matches!(ctrl, ControlFlow::Break(_)) {
54                return;
55            }
56        }
57    }
58}
59
60pub(super) fn is_active<'tcx>(
61    dominators: &Dominators<BasicBlock>,
62    borrow_data: &BorrowData<'tcx>,
63    location: Location,
64) -> bool {
65    debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
66
67    let activation_location = match borrow_data.activation_location {
68        // If this is not a 2-phase borrow, it is always active.
69        TwoPhaseActivation::NotTwoPhase => return true,
70        // And if the unique 2-phase use is not an activation, then it is *never* active.
71        TwoPhaseActivation::NotActivated => return false,
72        // Otherwise, we derive info from the activation point `loc`:
73        TwoPhaseActivation::ActivatedAt(loc) => loc,
74    };
75
76    // Otherwise, it is active for every location *except* in between
77    // the reservation and the activation:
78    //
79    //       X
80    //      /
81    //     R      <--+ Except for this
82    //    / \        | diamond
83    //    \ /        |
84    //     A  <------+
85    //     |
86    //     Z
87    //
88    // Note that we assume that:
89    // - the reservation R dominates the activation A
90    // - the activation A post-dominates the reservation R (ignoring unwinding edges).
91    //
92    // This means that there can't be an edge that leaves A and
93    // comes back into that diamond unless it passes through R.
94    //
95    // Suboptimal: In some cases, this code walks the dominator
96    // tree twice when it only has to be walked once. I am
97    // lazy. -nmatsakis
98
99    // If dominated by the activation A, then it is active. The
100    // activation occurs upon entering the point A, so this is
101    // also true if location == activation_location.
102    if activation_location.dominates(location, dominators) {
103        return true;
104    }
105
106    // The reservation starts *on exiting* the reservation block,
107    // so check if the location is dominated by R.successor. If so,
108    // this point falls in between the reservation and location.
109    let reserve_location = borrow_data.reserve_location.successor_within_block();
110    if reserve_location.dominates(location, dominators) {
111        false
112    } else {
113        // Otherwise, this point is outside the diamond, so
114        // consider the borrow active. This could happen for
115        // example if the borrow remains active around a loop (in
116        // which case it would be active also for the point R,
117        // which would generate an error).
118        true
119    }
120}
121
122/// Determines if a given borrow is borrowing local data
123/// This is called for all Yield expressions on movable coroutines
124pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
125    // Reborrow of already borrowed data is ignored
126    // Any errors will be caught on the initial borrow
127    !place.is_indirect()
128}
129
130/// If `place` is a field projection, and the field is being projected from a closure type,
131/// then returns the index of the field being projected. Note that this closure will always
132/// be `self` in the current MIR, because that is the only time we directly access the fields
133/// of a closure type.
134pub(crate) fn is_upvar_field_projection<'tcx>(
135    tcx: TyCtxt<'tcx>,
136    upvars: &[&rustc_middle::ty::CapturedPlace<'tcx>],
137    place_ref: PlaceRef<'tcx>,
138    body: &Body<'tcx>,
139) -> Option<FieldIdx> {
140    let mut place_ref = place_ref;
141    let mut by_ref = false;
142
143    if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() {
144        place_ref = place_base;
145        by_ref = true;
146    }
147
148    match place_ref.last_projection() {
149        Some((place_base, ProjectionElem::Field(field, _ty))) => {
150            let base_ty = place_base.ty(body, tcx).ty;
151            if (base_ty.is_closure() || base_ty.is_coroutine() || base_ty.is_coroutine_closure())
152                && (!by_ref || upvars[field.index()].is_by_ref())
153            {
154                Some(field)
155            } else {
156                None
157            }
158        }
159        _ => None,
160    }
161}