use IfEq to defer equality comparison around where clauses`

This commit is contained in:
Niko Matsakis 2018-09-19 16:12:00 -04:00
parent 7f8c42d7f0
commit dc9317ff8c
2 changed files with 59 additions and 49 deletions

View File

@ -401,12 +401,9 @@ where
// Compute the bounds we can derive from the trait definition.
// These are guaranteed to apply, no matter the inference
// results.
let trait_bounds = self.verify_bound
.projection_declared_bounds_from_trait(projection_ty);
debug!(
"projection_must_outlive: trait_bounds={:?}",
trait_bounds
);
let trait_bounds: Vec<_> = self.verify_bound
.projection_declared_bounds_from_trait(projection_ty)
.collect();
// If declared bounds list is empty, the only applicable rule is
// OutlivesProjectionComponent. If there are inference variables,
@ -451,7 +448,7 @@ where
if !trait_bounds.is_empty()
&& trait_bounds[1..]
.iter()
.chain(&approx_env_bounds)
.chain(approx_env_bounds.iter().map(|b| &b.1))
.all(|b| *b == trait_bounds[0])
{
let unique_bound = trait_bounds[0];

View File

@ -14,6 +14,7 @@ use infer::{GenericKind, VerifyBound};
use traits;
use ty::subst::{Subst, Substs};
use ty::{self, Ty, TyCtxt};
use util::captures::Captures;
/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
/// obligation into a series of `'a: 'b` constraints and "verifys", as
@ -65,21 +66,15 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
// Start with anything like `T: 'a` we can scrape from the
// environment
let param_bounds =
self.declared_generic_bounds_from_env(GenericKind::Param(param_ty))
.into_iter();
let param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty))
.into_iter()
.map(|outlives| outlives.1);
// Add in the default bound of fn body that applies to all in
// scope type parameters:
let param_bounds =
param_bounds
.chain(self.implicit_region_bound);
let param_bounds = param_bounds.chain(self.implicit_region_bound);
VerifyBound::AnyBound(
param_bounds
.map(|r| VerifyBound::OutlivedBy(r))
.collect()
)
VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect())
}
/// Given a projection like `T::Item`, searches the environment
@ -98,7 +93,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
pub fn projection_approx_declared_bounds_from_env(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::Region<'tcx>> {
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx);
let erased_projection_ty = self.tcx.erase_regions(&projection_ty);
self.declared_generic_bounds_from_env_with_compare_fn(|ty| {
@ -117,31 +112,42 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
pub fn projection_declared_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::Region<'tcx>> {
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
self.declared_projection_bounds_from_trait(projection_ty)
}
pub fn projection_bound(&self, projection_ty: ty::ProjectionTy<'tcx>) -> VerifyBound<'tcx> {
debug!("projection_bound(projection_ty={:?})", projection_ty);
let projection_ty_as_ty =
self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
// Search the env for where clauses like `P: 'a`.
let env_bounds =
self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty))
.into_iter();
let env_bounds = self.projection_approx_declared_bounds_from_env(projection_ty)
.into_iter()
.map(|ty::OutlivesPredicate(ty, r)| {
let vb = VerifyBound::OutlivedBy(r);
if ty == projection_ty_as_ty {
// Micro-optimize if this is an exact match (this
// occurs often when there are no region variables
// involved).
vb
} else {
VerifyBound::IfEq(ty, Box::new(vb))
}
});
// Extend with bounds that we can find from the trait.
let trait_bounds =
self.projection_declared_bounds_from_trait(projection_ty)
.into_iter();
let trait_bounds = self.projection_declared_bounds_from_trait(projection_ty)
.into_iter()
.map(|r| VerifyBound::OutlivedBy(r));
// see the extensive comment in projection_must_outlive
let ty = self.tcx
.mk_projection(projection_ty.item_def_id, projection_ty.substs);
let recursive_bound = self.recursive_type_bound(ty);
VerifyBound::AnyBound(
env_bounds.chain(trait_bounds).map(|r| VerifyBound::OutlivedBy(r)).collect()
).or(recursive_bound)
VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
}
fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
@ -151,11 +157,12 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
let mut regions = ty.regions();
regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions
bounds.push(
VerifyBound::AllBounds(
regions.into_iter().map(|r| VerifyBound::OutlivedBy(r)).collect()
)
);
bounds.push(VerifyBound::AllBounds(
regions
.into_iter()
.map(|r| VerifyBound::OutlivedBy(r))
.collect(),
));
// remove bounds that must hold, since they are not interesting
bounds.retain(|b| !b.must_hold());
@ -176,7 +183,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
fn declared_generic_bounds_from_env(
&self,
generic: GenericKind<'tcx>,
) -> Vec<ty::Region<'tcx>> {
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let generic_ty = generic.to_ty(self.tcx);
self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty)
}
@ -184,7 +191,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
fn declared_generic_bounds_from_env_with_compare_fn(
&self,
compare_ty: impl Fn(Ty<'tcx>) -> bool,
) -> Vec<ty::Region<'tcx>> {
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let tcx = self.tcx;
// To start, collect bounds from user environment. Note that
@ -212,14 +219,23 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
"declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}",
(r, p)
);
if compare_ty(p.to_ty(tcx)) {
Some(r)
let p_ty = p.to_ty(tcx);
if compare_ty(p_ty) {
Some(ty::OutlivesPredicate(p_ty, r))
} else {
None
}
});
param_bounds.chain(from_region_bound_pairs).collect()
param_bounds
.chain(from_region_bound_pairs)
.inspect(|bound| {
debug!(
"declared_generic_bounds_from_env_with_compare_fn: result predicate = {:?}",
bound
)
})
.collect()
}
/// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
@ -237,13 +253,11 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
fn declared_projection_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::Region<'tcx>> {
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
debug!("projection_bounds(projection_ty={:?})", projection_ty);
let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id);
for r in &mut bounds {
*r = r.subst(self.tcx, projection_ty.substs);
}
bounds
let tcx = self.tcx;
self.region_bounds_declared_on_associated_item(projection_ty.item_def_id)
.map(move |r| r.subst(tcx, projection_ty.substs))
}
/// Given the def-id of an associated item, returns any region
@ -279,7 +293,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
fn region_bounds_declared_on_associated_item(
&self,
assoc_item_def_id: DefId,
) -> Vec<ty::Region<'tcx>> {
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
let tcx = self.tcx;
let assoc_item = tcx.associated_item(assoc_item_def_id);
let trait_def_id = assoc_item.container.assert_trait();
@ -289,7 +303,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
self.collect_outlives_from_predicate_list(
move |ty| ty == identity_proj,
traits::elaborate_predicates(tcx, trait_predicates.predicates),
).collect()
).map(|b| b.1)
}
/// Searches through a predicate list for a predicate `T: 'a`.
@ -302,12 +316,11 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
&self,
compare_ty: impl Fn(Ty<'tcx>) -> bool,
predicates: impl IntoIterator<Item = impl AsRef<ty::Predicate<'tcx>>>,
) -> impl Iterator<Item = ty::Region<'tcx>> {
) -> impl Iterator<Item = ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
predicates
.into_iter()
.filter_map(|p| p.as_ref().to_opt_type_outlives())
.filter_map(|p| p.no_late_bound_regions())
.filter(move |p| compare_ty(p.0))
.map(|p| p.1)
}
}