Skip to content

Commit

Permalink
Make the WF check a bit more friendly to binders
Browse files Browse the repository at this point in the history
Skip the binder in less places during WF check. I thought this might help fix rust-lang#50781, but I couldn't actually observe any effect for this changes. Maybe it's still a worthwhile refactoring since skipping binders is generally a bad thing. There is a (manageble) conflict with rust-lang#50183.
  • Loading branch information
leoyvens committed May 17, 2018
1 parent 448cc57 commit 30da22b
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 23 deletions.
28 changes: 28 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,34 @@ impl<T> Binder<T> {
let (u, v) = f(self.0);
(Binder(u), Binder(v))
}

pub fn unfold<U,F>(self, f: F) -> impl Iterator<Item = Binder<U>>
where F: FnOnce(T) -> Vec<U>
{
f(self.0).into_iter().map(|u| Binder(u))
}
}

impl<'tcx> Binder<ty::Predicate<'tcx>> {
pub fn flatten<'a, 'gcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> ty::Predicate<'tcx> {
// We will rebind inside the predicate.
match self.0 {
ty::Predicate::Trait(b) =>
ty::Predicate::Trait(tcx.flatten_late_bound_regions(&Binder::bind(b))),
ty::Predicate::Subtype(b) =>
ty::Predicate::Subtype(tcx.flatten_late_bound_regions(&Binder::bind(b))),
ty::Predicate::RegionOutlives(b) =>
ty::Predicate::RegionOutlives(tcx.flatten_late_bound_regions(&Binder::bind(b))),
ty::Predicate::TypeOutlives(b) =>
ty::Predicate::TypeOutlives(tcx.flatten_late_bound_regions(&Binder::bind(b))),
ty::Predicate::Projection(b) =>
ty::Predicate::Projection(tcx.flatten_late_bound_regions(&Binder::bind(b))),
ty::Predicate::WellFormed(_) |
ty::Predicate::ObjectSafe(_) |
ty::Predicate::ClosureKind(_, _, _) |
ty::Predicate::ConstEvaluatable(_, _) => self.no_late_bound_regions().unwrap()
}
}
}

/// Represents the projection of an associated type. In explicit UFCS
Expand Down
64 changes: 41 additions & 23 deletions src/librustc/ty/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use middle::const_val::ConstVal;
use infer::InferCtxt;
use ty::subst::Substs;
use traits;
use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
use std::iter::once;
use syntax::ast;
use syntax_pos::Span;
Expand Down Expand Up @@ -59,7 +59,7 @@ pub fn trait_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-> Vec<traits::PredicateObligation<'tcx>>
{
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] };
wf.compute_trait_ref(trait_ref, Elaborate::All);
wf.compute_trait_ref(&trait_ref.to_poly_trait_ref(), Elaborate::All);
wf.normalize()
}

Expand All @@ -73,19 +73,18 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] };

// (*) ok to skip binders, because wf code is prepared for it
match *predicate {
ty::Predicate::Trait(ref t) => {
wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*)
match predicate {
ty::Predicate::Trait(t) => {
wf.compute_trait_ref(&t.to_poly_trait_ref(), Elaborate::None);
}
ty::Predicate::RegionOutlives(..) => {
}
ty::Predicate::TypeOutlives(ref t) => {
ty::Predicate::TypeOutlives(t) => {
wf.compute(t.skip_binder().0);
}
ty::Predicate::Projection(ref t) => {
let t = t.skip_binder(); // (*)
wf.compute_projection(t.projection_ty);
wf.compute(t.ty);
ty::Predicate::Projection(t) => {
wf.compute_projection(t.map_bound(|t| t.projection_ty));
wf.compute(t.skip_binder().ty); // (*)
}
ty::Predicate::WellFormed(t) => {
wf.compute(t);
Expand All @@ -94,12 +93,12 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
}
ty::Predicate::ClosureKind(..) => {
}
ty::Predicate::Subtype(ref data) => {
ty::Predicate::Subtype(data) => {
wf.compute(data.skip_binder().a); // (*)
wf.compute(data.skip_binder().b); // (*)
}
ty::Predicate::ConstEvaluatable(def_id, substs) => {
let obligations = wf.nominal_obligations(def_id, substs);
let obligations = wf.nominal_obligations(*def_id, substs);
wf.out.extend(obligations);

for ty in substs.types() {
Expand Down Expand Up @@ -169,8 +168,8 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {

/// Pushes the obligations required for `trait_ref` to be WF into
/// `self.out`.
fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
fn compute_trait_ref(&mut self, poly_trait_ref: &ty::PolyTraitRef<'tcx>, elaborate: Elaborate) {
let obligations = self.nominal_trait_ref_obligations(poly_trait_ref);

let cause = self.cause(traits::MiscObligation);
let param_env = self.param_env;
Expand All @@ -189,7 +188,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
self.out.extend(obligations);

self.out.extend(
trait_ref.substs.types()
poly_trait_ref.skip_binder().substs.types()
.filter(|ty| !ty.has_escaping_regions())
.map(|ty| traits::Obligation::new(cause.clone(),
param_env,
Expand All @@ -198,16 +197,18 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {

/// Pushes the obligations required for `trait_ref::Item` to be WF
/// into `self.out`.
fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) {
fn compute_projection(&mut self, poly_proj: ty::Binder<ty::ProjectionTy<'tcx>>) {
// A projection is well-formed if (a) the trait ref itself is
// WF and (b) the trait-ref holds. (It may also be
// normalizable and be WF that way.)
let trait_ref = data.trait_ref(self.infcx.tcx);
self.compute_trait_ref(&trait_ref, Elaborate::None);

if !data.has_escaping_regions() {
let predicate = trait_ref.to_predicate();
let cause = self.cause(traits::ProjectionWf(data));
let poly_trait_ref = poly_proj.map_bound(|proj| proj.trait_ref(self.infcx.tcx));
self.compute_trait_ref(&poly_trait_ref, Elaborate::None);

// FIXME(leodasvacas): Should be straightforward to remove
// this condition and use the `poly_proj` directly.
if let Some(proj) = poly_proj.no_late_bound_regions() {
let predicate = poly_trait_ref.to_predicate();
let cause = self.cause(traits::ProjectionWf(proj));
self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
}
}
Expand Down Expand Up @@ -289,7 +290,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {

ty::TyProjection(data) => {
subtys.skip_current_subtree(); // subtree handled by compute_projection
self.compute_projection(data);
self.compute_projection(ty::Binder::bind(data));
}

ty::TyAdt(def, substs) => {
Expand Down Expand Up @@ -431,6 +432,23 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
return true;
}

fn nominal_trait_ref_obligations(&mut self,
poly_trait_ref: &ty::PolyTraitRef<'tcx>)
-> Vec<traits::PredicateObligation<'tcx>> {
let cause = self.cause(traits::ItemObligation(poly_trait_ref.def_id()));
poly_trait_ref
.unfold(|trait_ref| {
self.infcx
.tcx
.predicates_of(trait_ref.def_id)
.instantiate(self.infcx.tcx, trait_ref.substs)
.predicates
})
.map(|poly_pred| poly_pred.flatten(self.infcx.tcx))
.map(|pred| traits::Obligation::new(cause.clone(), self.param_env, pred))
.collect()
}

fn nominal_obligations(&mut self,
def_id: DefId,
substs: &Substs<'tcx>)
Expand Down

0 comments on commit 30da22b

Please sign in to comment.