rustc_borrowck/
root_cx.rs

1use std::mem;
2use std::rc::Rc;
3
4use rustc_abi::FieldIdx;
5use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
6use rustc_hir::def_id::LocalDefId;
7use rustc_middle::mir::ConstraintCategory;
8use rustc_middle::ty::{self, TyCtxt};
9use rustc_span::ErrorGuaranteed;
10use smallvec::SmallVec;
11
12use crate::consumers::BorrowckConsumer;
13use crate::nll::compute_closure_requirements_modulo_opaques;
14use crate::region_infer::opaque_types::{
15    apply_computed_concrete_opaque_types, clone_and_resolve_opaque_types,
16    compute_concrete_opaque_types, detect_opaque_types_added_while_handling_opaque_types,
17};
18use crate::type_check::{Locations, constraint_conversion};
19use crate::{
20    ClosureRegionRequirements, CollectRegionConstraintsResult, ConcreteOpaqueTypes,
21    PropagatedBorrowCheckResults, borrowck_check_region_constraints,
22    borrowck_collect_region_constraints,
23};
24
25/// The shared context used by both the root as well as all its nested
26/// items.
27pub(super) struct BorrowCheckRootCtxt<'tcx> {
28    pub tcx: TyCtxt<'tcx>,
29    root_def_id: LocalDefId,
30    concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
31    /// The region constraints computed by [borrowck_collect_region_constraints]. This uses
32    /// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before
33    /// their parents.
34    collect_region_constraints_results:
35        FxIndexMap<LocalDefId, CollectRegionConstraintsResult<'tcx>>,
36    propagated_borrowck_results: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
37    tainted_by_errors: Option<ErrorGuaranteed>,
38    /// This should be `None` during normal compilation. See [`crate::consumers`] for more
39    /// information on how this is used.
40    pub consumer: Option<BorrowckConsumer<'tcx>>,
41}
42
43impl<'tcx> BorrowCheckRootCtxt<'tcx> {
44    pub(super) fn new(
45        tcx: TyCtxt<'tcx>,
46        root_def_id: LocalDefId,
47        consumer: Option<BorrowckConsumer<'tcx>>,
48    ) -> BorrowCheckRootCtxt<'tcx> {
49        BorrowCheckRootCtxt {
50            tcx,
51            root_def_id,
52            concrete_opaque_types: Default::default(),
53            collect_region_constraints_results: Default::default(),
54            propagated_borrowck_results: Default::default(),
55            tainted_by_errors: None,
56            consumer,
57        }
58    }
59
60    pub(super) fn root_def_id(&self) -> LocalDefId {
61        self.root_def_id
62    }
63
64    pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) {
65        self.tainted_by_errors = Some(guar);
66    }
67
68    pub(super) fn used_mut_upvars(
69        &mut self,
70        nested_body_def_id: LocalDefId,
71    ) -> &SmallVec<[FieldIdx; 8]> {
72        &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars
73    }
74
75    pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> {
76        if let Some(guar) = self.tainted_by_errors {
77            Err(guar)
78        } else {
79            Ok(self.tcx.arena.alloc(self.concrete_opaque_types))
80        }
81    }
82
83    fn handle_opaque_type_uses(&mut self) {
84        let mut per_body_info = Vec::new();
85        for input in self.collect_region_constraints_results.values_mut() {
86            let (num_entries, opaque_types) = clone_and_resolve_opaque_types(
87                &input.infcx,
88                &input.universal_region_relations,
89                &mut input.constraints,
90            );
91            input.deferred_opaque_type_errors = compute_concrete_opaque_types(
92                &input.infcx,
93                &input.universal_region_relations,
94                &input.constraints,
95                Rc::clone(&input.location_map),
96                &mut self.concrete_opaque_types,
97                &opaque_types,
98            );
99            per_body_info.push((num_entries, opaque_types));
100        }
101
102        for (input, (opaque_types_storage_num_entries, opaque_types)) in
103            self.collect_region_constraints_results.values_mut().zip(per_body_info)
104        {
105            if input.deferred_opaque_type_errors.is_empty() {
106                input.deferred_opaque_type_errors = apply_computed_concrete_opaque_types(
107                    &input.infcx,
108                    &input.body_owned,
109                    &input.universal_region_relations.universal_regions,
110                    &input.region_bound_pairs,
111                    &input.known_type_outlives_obligations,
112                    &mut input.constraints,
113                    &mut self.concrete_opaque_types,
114                    &opaque_types,
115                );
116            }
117
118            detect_opaque_types_added_while_handling_opaque_types(
119                &input.infcx,
120                opaque_types_storage_num_entries,
121            )
122        }
123    }
124
125    /// Computing defining uses of opaques may depend on the propagated region
126    /// requirements of nested bodies, while applying defining uses may introduce
127    /// additional region requirements we need to propagate.
128    ///
129    /// This results in cyclic dependency. To compute the defining uses in parent
130    /// bodies, we need the closure requirements of its nested bodies, but to check
131    /// non-defining uses in nested bodies, we may rely on the defining uses in the
132    /// parent.
133    ///
134    /// We handle this issue by applying closure requirements twice. Once using the
135    /// region constraints from before we've handled opaque types in the nested body
136    /// - which is used by the parent to handle its defining uses - and once after.
137    ///
138    /// As a performance optimization, we also eagerly finish borrowck for bodies
139    /// which don't depend on opaque types. In this case they get removed from
140    /// `collect_region_constraints_results` and the final result gets put into
141    /// `propagated_borrowck_results`.
142    fn apply_closure_requirements_modulo_opaques(&mut self) {
143        let mut closure_requirements_modulo_opaques = FxHashMap::default();
144        // We need to `mem::take` both `self.collect_region_constraints_results` and
145        // `input.deferred_closure_requirements` as we otherwise can't iterate over
146        // them while mutably using the containing struct.
147        let collect_region_constraints_results =
148            mem::take(&mut self.collect_region_constraints_results);
149        // We iterate over all bodies here, visiting nested bodies before their parent.
150        for (def_id, mut input) in collect_region_constraints_results {
151            // A body depends on opaque types if it either has any opaque type uses itself,
152            // or it has a nested body which does.
153            //
154            // If the current body does not depend on any opaque types, we eagerly compute
155            // its final result and write it into `self.propagated_borrowck_results`. This
156            // avoids having to compute its closure requirements modulo regions, as they
157            // are just the same as its final closure requirements.
158            let mut depends_on_opaques = input.infcx.has_opaque_types_in_storage();
159
160            // Iterate over all nested bodies of `input`. If that nested body depends on
161            // opaque types, we apply its closure requirements modulo opaques. Otherwise
162            // we use the closure requirements from its final borrowck result.
163            //
164            // In case we've only applied the closure requirements modulo opaques, we have
165            // to later apply its closure requirements considering opaques, so we put that
166            // nested body back into `deferred_closure_requirements`.
167            for (def_id, args, locations) in mem::take(&mut input.deferred_closure_requirements) {
168                let closure_requirements = match self.propagated_borrowck_results.get(&def_id) {
169                    None => {
170                        depends_on_opaques = true;
171                        input.deferred_closure_requirements.push((def_id, args, locations));
172                        &closure_requirements_modulo_opaques[&def_id]
173                    }
174                    Some(result) => &result.closure_requirements,
175                };
176
177                Self::apply_closure_requirements(
178                    &mut input,
179                    closure_requirements,
180                    def_id,
181                    args,
182                    locations,
183                );
184            }
185
186            // In case the current body does depend on opaques and is a nested body,
187            // we need to compute its closure requirements modulo opaques so that
188            // we're able to use it when visiting its parent later in this function.
189            //
190            // If the current body does not depend on opaque types, we finish borrowck
191            // and write its result into `propagated_borrowck_results`.
192            if depends_on_opaques {
193                if def_id != self.root_def_id {
194                    let req = Self::compute_closure_requirements_modulo_opaques(&input);
195                    closure_requirements_modulo_opaques.insert(def_id, req);
196                }
197                self.collect_region_constraints_results.insert(def_id, input);
198            } else {
199                assert!(input.deferred_closure_requirements.is_empty());
200                let result = borrowck_check_region_constraints(self, input);
201                self.propagated_borrowck_results.insert(def_id, result);
202            }
203        }
204    }
205
206    fn compute_closure_requirements_modulo_opaques(
207        input: &CollectRegionConstraintsResult<'tcx>,
208    ) -> Option<ClosureRegionRequirements<'tcx>> {
209        compute_closure_requirements_modulo_opaques(
210            &input.infcx,
211            &input.body_owned,
212            Rc::clone(&input.location_map),
213            &input.universal_region_relations,
214            &input.constraints,
215        )
216    }
217
218    fn apply_closure_requirements(
219        input: &mut CollectRegionConstraintsResult<'tcx>,
220        closure_requirements: &Option<ClosureRegionRequirements<'tcx>>,
221        closure_def_id: LocalDefId,
222        args: ty::GenericArgsRef<'tcx>,
223        locations: Locations,
224    ) {
225        if let Some(closure_requirements) = closure_requirements {
226            constraint_conversion::ConstraintConversion::new(
227                &input.infcx,
228                &input.universal_region_relations.universal_regions,
229                &input.region_bound_pairs,
230                &input.known_type_outlives_obligations,
231                locations,
232                input.body_owned.span,      // irrelevant; will be overridden.
233                ConstraintCategory::Boring, // same as above.
234                &mut input.constraints,
235            )
236            .apply_closure_requirements(closure_requirements, closure_def_id, args);
237        }
238    }
239
240    pub(super) fn do_mir_borrowck(&mut self) {
241        // The list of all bodies we need to borrowck. This first looks at
242        // nested bodies, and then their parents. This means accessing e.g.
243        // `used_mut_upvars` for a closure can assume that we've already
244        // checked that closure.
245        let all_bodies = self
246            .tcx
247            .nested_bodies_within(self.root_def_id)
248            .iter()
249            .chain(std::iter::once(self.root_def_id));
250        for def_id in all_bodies {
251            let result = borrowck_collect_region_constraints(self, def_id);
252            self.collect_region_constraints_results.insert(def_id, result);
253        }
254
255        // We now apply the closure requirements of nested bodies modulo
256        // regions. In case a body does not depend on opaque types, we
257        // eagerly check its region constraints and use the final closure
258        // requirements.
259        //
260        // We eagerly finish borrowck for bodies which don't depend on
261        // opaques.
262        self.apply_closure_requirements_modulo_opaques();
263
264        // We handle opaque type uses for all bodies together.
265        self.handle_opaque_type_uses();
266
267        // Now walk over all bodies which depend on opaque types and finish borrowck.
268        //
269        // We first apply the final closure requirements from nested bodies which also
270        // depend on opaque types and then finish borrow checking the parent. Bodies
271        // which don't depend on opaques have already been fully borrowchecked in
272        // `apply_closure_requirements_modulo_opaques` as an optimization.
273        for (def_id, mut input) in mem::take(&mut self.collect_region_constraints_results) {
274            for (def_id, args, locations) in mem::take(&mut input.deferred_closure_requirements) {
275                // We visit nested bodies before their parent, so we're already
276                // done with nested bodies at this point.
277                let closure_requirements =
278                    &self.propagated_borrowck_results[&def_id].closure_requirements;
279                Self::apply_closure_requirements(
280                    &mut input,
281                    closure_requirements,
282                    def_id,
283                    args,
284                    locations,
285                );
286            }
287
288            let result = borrowck_check_region_constraints(self, input);
289            self.propagated_borrowck_results.insert(def_id, result);
290        }
291    }
292}