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