use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::transitive_relation::TransitiveRelation;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::free_regions::FreeRegionRelations;
use rustc_infer::infer::outlives;
use rustc_infer::infer::region_constraints::GenericKind;
use rustc_infer::infer::InferCtxt;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
use std::rc::Rc;
use crate::borrow_check::{
nll::ToRegionVid,
type_check::constraint_conversion,
type_check::{Locations, MirTypeckRegionConstraints},
universal_regions::UniversalRegions,
};
#[derive(Debug)]
crate struct UniversalRegionRelations<'tcx> {
universal_regions: Rc<UniversalRegions<'tcx>>,
outlives: TransitiveRelation<RegionVid>,
inverse_outlives: TransitiveRelation<RegionVid>,
}
type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>;
type NormalizedInputsAndOutput<'tcx> = Vec<Ty<'tcx>>;
crate struct CreateResult<'tcx> {
pub(in crate::borrow_check) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
crate region_bound_pairs: RegionBoundPairs<'tcx>,
crate normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>,
}
crate fn create(
infcx: &InferCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
universal_regions: &Rc<UniversalRegions<'tcx>>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
) -> CreateResult<'tcx> {
UniversalRegionRelationsBuilder {
infcx,
param_env,
implicit_region_bound,
constraints,
universal_regions: universal_regions.clone(),
region_bound_pairs: Vec::new(),
relations: UniversalRegionRelations {
universal_regions: universal_regions.clone(),
outlives: Default::default(),
inverse_outlives: Default::default(),
},
}
.create()
}
impl UniversalRegionRelations<'tcx> {
fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b);
self.outlives.add(fr_a, fr_b);
self.inverse_outlives.add(fr_b, fr_a);
}
crate fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid {
assert!(self.universal_regions.is_universal_region(fr1));
assert!(self.universal_regions.is_universal_region(fr2));
*self
.inverse_outlives
.postdom_upper_bound(&fr1, &fr2)
.unwrap_or(&self.universal_regions.fr_static)
}
crate fn non_local_upper_bounds(&'a self, fr: &'a RegionVid) -> Vec<&'a RegionVid> {
debug!("non_local_upper_bound(fr={:?})", fr);
let res = self.non_local_bounds(&self.inverse_outlives, fr);
assert!(!res.is_empty(), "can't find an upper bound!?");
res
}
crate fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid {
let upper_bounds = self.non_local_upper_bounds(&fr);
let post_dom = self.inverse_outlives.mutual_immediate_postdominator(upper_bounds);
debug!("non_local_bound: post_dom={:?}", post_dom);
post_dom
.and_then(|&post_dom| {
if !self.universal_regions.is_local_free_region(post_dom) {
Some(post_dom)
} else {
None
}
})
.unwrap_or(self.universal_regions.fr_static)
}
crate fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
debug!("non_local_lower_bound(fr={:?})", fr);
let lower_bounds = self.non_local_bounds(&self.outlives, &fr);
let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds);
debug!("non_local_bound: post_dom={:?}", post_dom);
post_dom.and_then(|&post_dom| {
if !self.universal_regions.is_local_free_region(post_dom) {
Some(post_dom)
} else {
None
}
})
}
fn non_local_bounds<'a>(
&self,
relation: &'a TransitiveRelation<RegionVid>,
fr0: &'a RegionVid,
) -> Vec<&'a RegionVid> {
assert!(self.universal_regions.is_universal_region(*fr0));
let mut external_parents = vec![];
let mut queue = vec![fr0];
while let Some(fr) = queue.pop() {
if !self.universal_regions.is_local_free_region(*fr) {
external_parents.push(fr);
continue;
}
queue.extend(relation.parents(fr));
}
debug!("non_local_bound: external_parents={:?}", external_parents);
external_parents
}
crate fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
self.outlives.contains(&fr1, &fr2)
}
crate fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<&RegionVid> {
self.outlives.reachable_from(&fr1)
}
crate fn known_outlives(&self) -> impl Iterator<Item = (&RegionVid, &RegionVid)> {
self.outlives.base_edges()
}
}
struct UniversalRegionRelationsBuilder<'this, 'tcx> {
infcx: &'this InferCtxt<'this, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
universal_regions: Rc<UniversalRegions<'tcx>>,
implicit_region_bound: Option<ty::Region<'tcx>>,
constraints: &'this mut MirTypeckRegionConstraints<'tcx>,
relations: UniversalRegionRelations<'tcx>,
region_bound_pairs: RegionBoundPairs<'tcx>,
}
impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
crate fn create(mut self) -> CreateResult<'tcx> {
let unnormalized_input_output_tys = self
.universal_regions
.unnormalized_input_tys
.iter()
.cloned()
.chain(Some(self.universal_regions.unnormalized_output_ty));
let mut normalized_inputs_and_output =
Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1);
let constraint_sets: Vec<_> = unnormalized_input_output_tys
.flat_map(|ty| {
debug!("build: input_or_output={:?}", ty);
let (ty, constraints1) = self
.param_env
.and(type_op::normalize::Normalize::new(ty))
.fully_perform(self.infcx)
.unwrap_or_else(|_| {
self.infcx
.tcx
.sess
.delay_span_bug(DUMMY_SP, &format!("failed to normalize {:?}", ty));
(self.infcx.tcx.ty_error(), None)
});
let constraints2 = self.add_implied_bounds(ty);
normalized_inputs_and_output.push(ty);
constraints1.into_iter().chain(constraints2)
})
.collect();
let param_env = self.param_env;
self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env));
let fr_static = self.universal_regions.fr_static;
let fr_fn_body = self.universal_regions.fr_fn_body;
for fr in self.universal_regions.universal_regions() {
debug!("build: relating free region {:?} to itself and to 'static", fr);
self.relations.relate_universal_regions(fr, fr);
self.relations.relate_universal_regions(fr_static, fr);
self.relations.relate_universal_regions(fr, fr_fn_body);
}
for data in &constraint_sets {
constraint_conversion::ConstraintConversion::new(
self.infcx,
&self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
Locations::All(DUMMY_SP),
ConstraintCategory::Internal,
&mut self.constraints,
)
.convert_all(data);
}
CreateResult {
universal_region_relations: Frozen::freeze(self.relations),
region_bound_pairs: self.region_bound_pairs,
normalized_inputs_and_output,
}
}
fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<Rc<QueryRegionConstraints<'tcx>>> {
debug!("add_implied_bounds(ty={:?})", ty);
let (bounds, constraints) = self
.param_env
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
.fully_perform(self.infcx)
.unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty));
self.add_outlives_bounds(bounds);
constraints
}
fn add_outlives_bounds<I>(&mut self, outlives_bounds: I)
where
I: IntoIterator<Item = OutlivesBound<'tcx>>,
{
for outlives_bound in outlives_bounds {
debug!("add_outlives_bounds(bound={:?})", outlives_bound);
match outlives_bound {
OutlivesBound::RegionSubRegion(r1, r2) => {
if let ty::ReEmpty(_) = r1 {
return;
}
let r1 = self.universal_regions.to_region_vid(r1);
let r2 = self.universal_regions.to_region_vid(r2);
self.relations.relate_universal_regions(r2, r1);
}
OutlivesBound::RegionSubParam(r_a, param_b) => {
self.region_bound_pairs.push((r_a, GenericKind::Param(param_b)));
}
OutlivesBound::RegionSubProjection(r_a, projection_b) => {
self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b)));
}
}
}
}
}
impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegionRelations<'tcx> {
fn sub_free_regions(
&self,
_tcx: TyCtxt<'tcx>,
shorter: ty::Region<'tcx>,
longer: ty::Region<'tcx>,
) -> bool {
let shorter = shorter.to_region_vid();
assert!(self.universal_regions.is_universal_region(shorter));
let longer = longer.to_region_vid();
assert!(self.universal_regions.is_universal_region(longer));
self.outlives(longer, shorter)
}
}