use std::mem; use std::rc::Rc; use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::ErrorGuaranteed; use smallvec::SmallVec; use crate::consumers::BorrowckConsumer; use crate::nll::compute_closure_requirements_modulo_opaques; use crate::region_infer::opaque_types::{ apply_computed_concrete_opaque_types, clone_and_resolve_opaque_types, compute_concrete_opaque_types, detect_opaque_types_added_while_handling_opaque_types, }; use crate::type_check::{Locations, constraint_conversion}; use crate::{ ClosureRegionRequirements, CollectRegionConstraintsResult, ConcreteOpaqueTypes, PropagatedBorrowCheckResults, borrowck_check_region_constraints, borrowck_collect_region_constraints, }; /// The shared context used by both the root as well as all its nested /// items. pub(super) struct BorrowCheckRootCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, root_def_id: LocalDefId, concrete_opaque_types: ConcreteOpaqueTypes<'tcx>, /// The region constraints computed by [borrowck_collect_region_constraints]. This uses /// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before /// their parents. collect_region_constraints_results: FxIndexMap>, propagated_borrowck_results: FxHashMap>, tainted_by_errors: Option, /// This should be `None` during normal compilation. See [`crate::consumers`] for more /// information on how this is used. pub consumer: Option>, } impl<'tcx> BorrowCheckRootCtxt<'tcx> { pub(super) fn new( tcx: TyCtxt<'tcx>, root_def_id: LocalDefId, consumer: Option>, ) -> BorrowCheckRootCtxt<'tcx> { BorrowCheckRootCtxt { tcx, root_def_id, concrete_opaque_types: Default::default(), collect_region_constraints_results: Default::default(), propagated_borrowck_results: Default::default(), tainted_by_errors: None, consumer, } } pub(super) fn root_def_id(&self) -> LocalDefId { self.root_def_id } pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) { self.tainted_by_errors = Some(guar); } pub(super) fn used_mut_upvars( &mut self, nested_body_def_id: LocalDefId, ) -> &SmallVec<[FieldIdx; 8]> { &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars } pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> { if let Some(guar) = self.tainted_by_errors { Err(guar) } else { Ok(self.tcx.arena.alloc(self.concrete_opaque_types)) } } fn handle_opaque_type_uses(&mut self) { let mut per_body_info = Vec::new(); for input in self.collect_region_constraints_results.values_mut() { let (num_entries, opaque_types) = clone_and_resolve_opaque_types( &input.infcx, &input.universal_region_relations, &mut input.constraints, ); input.deferred_opaque_type_errors = compute_concrete_opaque_types( &input.infcx, &input.universal_region_relations, &input.constraints, Rc::clone(&input.location_map), &mut self.concrete_opaque_types, &opaque_types, ); per_body_info.push((num_entries, opaque_types)); } for (input, (opaque_types_storage_num_entries, opaque_types)) in self.collect_region_constraints_results.values_mut().zip(per_body_info) { if input.deferred_opaque_type_errors.is_empty() { input.deferred_opaque_type_errors = apply_computed_concrete_opaque_types( &input.infcx, &input.body_owned, &input.universal_region_relations.universal_regions, &input.region_bound_pairs, &input.known_type_outlives_obligations, &mut input.constraints, &mut self.concrete_opaque_types, &opaque_types, ); } detect_opaque_types_added_while_handling_opaque_types( &input.infcx, opaque_types_storage_num_entries, ) } } /// Computing defining uses of opaques may depend on the propagated region /// requirements of nested bodies, while applying defining uses may introduce /// additional region requirements we need to propagate. /// /// This results in cyclic dependency. To compute the defining uses in parent /// bodies, we need the closure requirements of its nested bodies, but to check /// non-defining uses in nested bodies, we may rely on the defining uses in the /// parent. /// /// We handle this issue by applying closure requirements twice. Once using the /// region constraints from before we've handled opaque types in the nested body /// - which is used by the parent to handle its defining uses - and once after. /// /// As a performance optimization, we also eagerly finish borrowck for bodies /// which don't depend on opaque types. In this case they get removed from /// `collect_region_constraints_results` and the final result gets put into /// `propagated_borrowck_results`. fn apply_closure_requirements_modulo_opaques(&mut self) { let mut closure_requirements_modulo_opaques = FxHashMap::default(); // We need to `mem::take` both `self.collect_region_constraints_results` and // `input.deferred_closure_requirements` as we otherwise can't iterate over // them while mutably using the containing struct. let collect_region_constraints_results = mem::take(&mut self.collect_region_constraints_results); // We iterate over all bodies here, visiting nested bodies before their parent. for (def_id, mut input) in collect_region_constraints_results { // A body depends on opaque types if it either has any opaque type uses itself, // or it has a nested body which does. // // If the current body does not depend on any opaque types, we eagerly compute // its final result and write it into `self.propagated_borrowck_results`. This // avoids having to compute its closure requirements modulo regions, as they // are just the same as its final closure requirements. let mut depends_on_opaques = input.infcx.has_opaque_types_in_storage(); // Iterate over all nested bodies of `input`. If that nested body depends on // opaque types, we apply its closure requirements modulo opaques. Otherwise // we use the closure requirements from its final borrowck result. // // In case we've only applied the closure requirements modulo opaques, we have // to later apply its closure requirements considering opaques, so we put that // nested body back into `deferred_closure_requirements`. for (def_id, args, locations) in mem::take(&mut input.deferred_closure_requirements) { let closure_requirements = match self.propagated_borrowck_results.get(&def_id) { None => { depends_on_opaques = true; input.deferred_closure_requirements.push((def_id, args, locations)); &closure_requirements_modulo_opaques[&def_id] } Some(result) => &result.closure_requirements, }; Self::apply_closure_requirements( &mut input, closure_requirements, def_id, args, locations, ); } // In case the current body does depend on opaques and is a nested body, // we need to compute its closure requirements modulo opaques so that // we're able to use it when visiting its parent later in this function. // // If the current body does not depend on opaque types, we finish borrowck // and write its result into `propagated_borrowck_results`. if depends_on_opaques { if def_id != self.root_def_id { let req = Self::compute_closure_requirements_modulo_opaques(&input); closure_requirements_modulo_opaques.insert(def_id, req); } self.collect_region_constraints_results.insert(def_id, input); } else { assert!(input.deferred_closure_requirements.is_empty()); let result = borrowck_check_region_constraints(self, input); self.propagated_borrowck_results.insert(def_id, result); } } } fn compute_closure_requirements_modulo_opaques( input: &CollectRegionConstraintsResult<'tcx>, ) -> Option> { compute_closure_requirements_modulo_opaques( &input.infcx, &input.body_owned, Rc::clone(&input.location_map), &input.universal_region_relations, &input.constraints, ) } fn apply_closure_requirements( input: &mut CollectRegionConstraintsResult<'tcx>, closure_requirements: &Option>, closure_def_id: LocalDefId, args: ty::GenericArgsRef<'tcx>, locations: Locations, ) { if let Some(closure_requirements) = closure_requirements { constraint_conversion::ConstraintConversion::new( &input.infcx, &input.universal_region_relations.universal_regions, &input.region_bound_pairs, &input.known_type_outlives_obligations, locations, input.body_owned.span, // irrelevant; will be overridden. ConstraintCategory::Boring, // same as above. &mut input.constraints, ) .apply_closure_requirements(closure_requirements, closure_def_id, args); } } pub(super) fn do_mir_borrowck(&mut self) { // The list of all bodies we need to borrowck. This first looks at // nested bodies, and then their parents. This means accessing e.g. // `used_mut_upvars` for a closure can assume that we've already // checked that closure. let all_bodies = self .tcx .nested_bodies_within(self.root_def_id) .iter() .chain(std::iter::once(self.root_def_id)); for def_id in all_bodies { let result = borrowck_collect_region_constraints(self, def_id); self.collect_region_constraints_results.insert(def_id, result); } // We now apply the closure requirements of nested bodies modulo // regions. In case a body does not depend on opaque types, we // eagerly check its region constraints and use the final closure // requirements. // // We eagerly finish borrowck for bodies which don't depend on // opaques. self.apply_closure_requirements_modulo_opaques(); // We handle opaque type uses for all bodies together. self.handle_opaque_type_uses(); // Now walk over all bodies which depend on opaque types and finish borrowck. // // We first apply the final closure requirements from nested bodies which also // depend on opaque types and then finish borrow checking the parent. Bodies // which don't depend on opaques have already been fully borrowchecked in // `apply_closure_requirements_modulo_opaques` as an optimization. for (def_id, mut input) in mem::take(&mut self.collect_region_constraints_results) { for (def_id, args, locations) in mem::take(&mut input.deferred_closure_requirements) { // We visit nested bodies before their parent, so we're already // done with nested bodies at this point. let closure_requirements = &self.propagated_borrowck_results[&def_id].closure_requirements; Self::apply_closure_requirements( &mut input, closure_requirements, def_id, args, locations, ); } let result = borrowck_check_region_constraints(self, input); self.propagated_borrowck_results.insert(def_id, result); } } }