Skip to main content

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