mirror of
https://github.com/rust-lang/rust.git
synced 2025-10-30 12:36:38 +00:00
293 lines
13 KiB
Rust
293 lines
13 KiB
Rust
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<LocalDefId, CollectRegionConstraintsResult<'tcx>>,
|
|
propagated_borrowck_results: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
|
|
tainted_by_errors: Option<ErrorGuaranteed>,
|
|
/// This should be `None` during normal compilation. See [`crate::consumers`] for more
|
|
/// information on how this is used.
|
|
pub consumer: Option<BorrowckConsumer<'tcx>>,
|
|
}
|
|
|
|
impl<'tcx> BorrowCheckRootCtxt<'tcx> {
|
|
pub(super) fn new(
|
|
tcx: TyCtxt<'tcx>,
|
|
root_def_id: LocalDefId,
|
|
consumer: Option<BorrowckConsumer<'tcx>>,
|
|
) -> 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<ClosureRegionRequirements<'tcx>> {
|
|
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<ClosureRegionRequirements<'tcx>>,
|
|
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);
|
|
}
|
|
}
|
|
}
|