extract the code to create OutlivesBounds into its own module

Now it can be reused by the NLL code.
This commit is contained in:
Niko Matsakis 2017-11-12 05:25:13 -05:00
parent 1f33145ae9
commit c45307fae1
2 changed files with 110 additions and 98 deletions

View File

@ -8,9 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use infer::{InferCtxt, GenericKind};
use infer::{GenericKind, InferCtxt};
use infer::outlives::free_region_map::FreeRegionMap;
use infer::outlives::implied_bounds::ImpliedBound;
use infer::outlives::implied_bounds::{self, OutlivesBound};
use ty::{self, Ty};
use syntax::ast;
@ -50,7 +50,7 @@ pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
region_bound_pairs: vec![],
};
env.init_free_regions_from_predicates();
env.add_outlives_bounds(None, implied_bounds::explicit_outlives_bounds(param_env));
env
}
@ -144,65 +144,54 @@ pub fn add_implied_bounds(
for &ty in fn_sig_tys {
let ty = infcx.resolve_type_vars_if_possible(&ty);
debug!("add_implied_bounds: ty = {}", ty);
let implied_bounds = infcx.implied_bounds(self.param_env, body_id, ty, span);
// But also record other relationships, such as `T:'x`,
// that don't go into the free-region-map but which we use
// here.
for implication in implied_bounds {
debug!("add_implied_bounds: implication={:?}", implication);
match implication {
ImpliedBound::RegionSubRegion(
r_a @ &ty::ReEarlyBound(_),
&ty::ReVar(vid_b),
) |
ImpliedBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => {
infcx.add_given(r_a, vid_b);
}
ImpliedBound::RegionSubParam(r_a, param_b) => {
self.region_bound_pairs
.push((r_a, GenericKind::Param(param_b)));
}
ImpliedBound::RegionSubProjection(r_a, projection_b) => {
self.region_bound_pairs
.push((r_a, GenericKind::Projection(projection_b)));
}
ImpliedBound::RegionSubRegion(r_a, r_b) => {
// In principle, we could record (and take
// advantage of) every relationship here, but
// we are also free not to -- it simply means
// strictly less that we can successfully type
// check. Right now we only look for things
// relationships between free regions. (It may
// also be that we should revise our inference
// system to be more general and to make use
// of *every* relationship that arises here,
// but presently we do not.)
self.free_region_map.relate_regions(r_a, r_b);
}
}
}
let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
self.add_outlives_bounds(Some(infcx), implied_bounds)
}
}
fn init_free_regions_from_predicates(&mut self) {
debug!("init_free_regions_from_predicates()");
for predicate in self.param_env.caller_bounds {
debug!("init_free_regions_from_predicates: predicate={:?}", predicate);
match *predicate {
ty::Predicate::Projection(..) |
ty::Predicate::Trait(..) |
ty::Predicate::Equate(..) |
ty::Predicate::Subtype(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) |
ty::Predicate::ConstEvaluatable(..) => {
// No region bounds here
/// Processes outlives bounds that are known to hold, whether from implied or other sources.
///
/// The `infcx` parameter is optional; if the implied bounds may
/// contain inference variables, it should be supplied, in which
/// case we will register "givens" on the inference context. (See
/// `RegionConstraintData`.)
fn add_outlives_bounds<I>(
&mut self,
infcx: Option<&InferCtxt<'a, 'gcx, 'tcx>>,
outlives_bounds: I,
) where
I: IntoIterator<Item = OutlivesBound<'tcx>>,
{
// But also record other relationships, such as `T:'x`,
// that don't go into the free-region-map but which we use
// here.
for outlives_bound in outlives_bounds {
debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
match outlives_bound {
OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) |
OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => {
infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b);
}
ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => {
self.free_region_map.relate_regions(r_b, r_a);
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)));
}
OutlivesBound::RegionSubRegion(r_a, r_b) => {
// In principle, we could record (and take
// advantage of) every relationship here, but
// we are also free not to -- it simply means
// strictly less that we can successfully type
// check. Right now we only look for things
// relationships between free regions. (It may
// also be that we should revise our inference
// system to be more general and to make use
// of *every* relationship that arises here,
// but presently we do not.)
self.free_region_map.relate_regions(r_a, r_b);
}
}
}

View File

@ -16,28 +16,32 @@
use ty::outlives::Component;
use ty::wf;
/// Implied bounds are region relationships that we deduce
/// automatically. The idea is that (e.g.) a caller must check that a
/// function's argument types are well-formed immediately before
/// calling that fn, and hence the *callee* can assume that its
/// argument types are well-formed. This may imply certain relationships
/// between generic parameters. For example:
///
/// fn foo<'a,T>(x: &'a T)
///
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
/// Outlives bounds are relationships between generic parameters,
/// whether they both be regions (`'a: 'b`) or whether types are
/// involved (`T: 'a`). These relationships can be extracted from the
/// full set of predicates we understand or also from types (in which
/// case they are called implied bounds). They are fed to the
/// `OutlivesEnv` which in turn is supplied to the region checker and
/// other parts of the inference system.
#[derive(Debug)]
pub enum ImpliedBound<'tcx> {
pub enum OutlivesBound<'tcx> {
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
}
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// Compute the implied bounds that a callee/impl can assume based on
/// the fact that caller/projector has ensured that `ty` is WF. See
/// the `ImpliedBound` type for more details.
/// Implied bounds are region relationships that we deduce
/// automatically. The idea is that (e.g.) a caller must check that a
/// function's argument types are well-formed immediately before
/// calling that fn, and hence the *callee* can assume that its
/// argument types are well-formed. This may imply certain relationships
/// between generic parameters. For example:
///
/// fn foo<'a,T>(x: &'a T)
///
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
///
/// # Parameters
///
@ -48,13 +52,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// - `ty`, the type that we are supposed to assume is WF.
/// - `span`, a span to use when normalizing, hopefully not important,
/// might be useful if a `bug!` occurs.
pub fn implied_bounds(
pub fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
body_id: ast::NodeId,
ty: Ty<'tcx>,
span: Span,
) -> Vec<ImpliedBound<'tcx>> {
) -> Vec<OutlivesBound<'tcx>> {
let tcx = self.tcx;
// Sometimes when we ask what it takes for T: WF, we get back that
@ -76,8 +80,7 @@ pub fn implied_bounds(
// than the ultimate set. (Note: normally there won't be
// unresolved inference variables here anyway, but there might be
// during typeck under some circumstances.)
let obligations =
wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]);
let obligations = wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]);
// NB: All of these predicates *ought* to be easily proven
// true. In fact, their correctness is (mostly) implied by
@ -105,7 +108,8 @@ pub fn implied_bounds(
obligations
.iter()
.filter(|o| o.predicate.has_infer_types())
.cloned());
.cloned(),
);
// From the full set of obligations, just filter down to the
// region relationships.
@ -125,25 +129,21 @@ pub fn implied_bounds(
vec![]
}
ty::Predicate::RegionOutlives(ref data) => {
match data.no_late_bound_regions() {
None => vec![],
Some(ty::OutlivesPredicate(r_a, r_b)) => {
vec![ImpliedBound::RegionSubRegion(r_b, r_a)]
}
ty::Predicate::RegionOutlives(ref data) => match data.no_late_bound_regions() {
None => vec![],
Some(ty::OutlivesPredicate(r_a, r_b)) => {
vec![OutlivesBound::RegionSubRegion(r_b, r_a)]
}
}
},
ty::Predicate::TypeOutlives(ref data) => {
match data.no_late_bound_regions() {
None => vec![],
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
let ty_a = self.resolve_type_vars_if_possible(&ty_a);
let components = tcx.outlives_components(ty_a);
Self::implied_bounds_from_components(r_b, components)
}
ty::Predicate::TypeOutlives(ref data) => match data.no_late_bound_regions() {
None => vec![],
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
let ty_a = self.resolve_type_vars_if_possible(&ty_a);
let components = tcx.outlives_components(ty_a);
Self::implied_bounds_from_components(r_b, components)
}
}
},
}
}));
}
@ -165,17 +165,17 @@ pub fn implied_bounds(
fn implied_bounds_from_components(
sub_region: ty::Region<'tcx>,
sup_components: Vec<Component<'tcx>>,
) -> Vec<ImpliedBound<'tcx>> {
) -> Vec<OutlivesBound<'tcx>> {
sup_components
.into_iter()
.flat_map(|component| {
match component {
Component::Region(r) =>
vec![ImpliedBound::RegionSubRegion(sub_region, r)],
vec![OutlivesBound::RegionSubRegion(sub_region, r)],
Component::Param(p) =>
vec![ImpliedBound::RegionSubParam(sub_region, p)],
vec![OutlivesBound::RegionSubParam(sub_region, p)],
Component::Projection(p) =>
vec![ImpliedBound::RegionSubProjection(sub_region, p)],
vec![OutlivesBound::RegionSubProjection(sub_region, p)],
Component::EscapingProjection(_) =>
// If the projection has escaping regions, don't
// try to infer any implied bounds even for its
@ -193,3 +193,26 @@ fn implied_bounds_from_components(
.collect()
}
}
pub fn explicit_outlives_bounds<'tcx>(
param_env: ty::ParamEnv<'tcx>,
) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
debug!("explicit_outlives_bounds()");
param_env
.caller_bounds
.into_iter()
.filter_map(move |predicate| match predicate {
ty::Predicate::Projection(..) |
ty::Predicate::Trait(..) |
ty::Predicate::Equate(..) |
ty::Predicate::Subtype(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) |
ty::Predicate::ConstEvaluatable(..) => None,
ty::Predicate::RegionOutlives(ref data) => data.no_late_bound_regions().map(
|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a),
),
})
}