Skip to content

Commit

Permalink
Normalize bounds that we extract from where clauses. Fixes rust-lang#…
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis committed Jan 12, 2015
1 parent b21a6da commit 47424cd
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 30 deletions.
12 changes: 7 additions & 5 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,10 @@ pub enum Vtable<'tcx, N> {
VtableImpl(VtableImplData<'tcx, N>),

/// Successful resolution to an obligation provided by the caller
/// for some type parameter.
VtableParam,
/// for some type parameter. The `Vec<N>` represents the
/// obligations incurred from normalizing the where-clause (if
/// any).
VtableParam(Vec<N>),

/// Virtual calls through an object
VtableObject(VtableObjectData<'tcx>),
Expand Down Expand Up @@ -443,7 +445,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableImpl(ref i) => i.iter_nested(),
VtableFnPointer(..) => (&[]).iter(),
VtableUnboxedClosure(..) => (&[]).iter(),
VtableParam => (&[]).iter(),
VtableParam(ref n) => n.iter(),
VtableObject(_) => (&[]).iter(),
VtableBuiltin(ref i) => i.iter_nested(),
}
Expand All @@ -454,7 +456,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableImpl(ref i) => VtableImpl(i.map_nested(op)),
VtableFnPointer(ref sig) => VtableFnPointer((*sig).clone()),
VtableUnboxedClosure(d, ref s) => VtableUnboxedClosure(d, s.clone()),
VtableParam => VtableParam,
VtableParam(ref n) => VtableParam(n.iter().map(op).collect()),
VtableObject(ref p) => VtableObject(p.clone()),
VtableBuiltin(ref b) => VtableBuiltin(b.map_nested(op)),
}
Expand All @@ -467,7 +469,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableImpl(i) => VtableImpl(i.map_move_nested(op)),
VtableFnPointer(sig) => VtableFnPointer(sig),
VtableUnboxedClosure(d, s) => VtableUnboxedClosure(d, s),
VtableParam => VtableParam,
VtableParam(n) => VtableParam(n.into_iter().map(op).collect()),
VtableObject(p) => VtableObject(p),
VtableBuiltin(no) => VtableBuiltin(no.map_move_nested(op)),
}
Expand Down
104 changes: 83 additions & 21 deletions src/librustc/middle/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
try!(self.assemble_candidates_from_caller_bounds(obligation, &mut candidates));
try!(self.assemble_candidates_from_caller_bounds(stack, &mut candidates));
debug!("candidate list size: {}", candidates.vec.len());
Ok(candidates)
}
Expand Down Expand Up @@ -884,13 +884,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// supplied to find out whether it is listed among them.
///
/// Never affects inference environment.
fn assemble_candidates_from_caller_bounds(&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
fn assemble_candidates_from_caller_bounds<'o>(&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
{
debug!("assemble_candidates_from_caller_bounds({})",
obligation.repr(self.tcx()));
stack.obligation.repr(self.tcx()));

let caller_trait_refs: Vec<_> =
self.param_env().caller_bounds.predicates.iter()
Expand All @@ -903,8 +903,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

let matching_bounds =
all_bounds.filter(
|bound| self.infcx.probe(
|_| self.match_poly_trait_ref(obligation, bound.clone())).is_ok());
|bound| self.evaluate_where_clause(stack, bound.clone()).may_apply());

let param_candidates =
matching_bounds.map(|bound| ParamCandidate(bound));
Expand All @@ -914,6 +913,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(())
}

fn evaluate_where_clause<'o>(&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
-> EvaluationResult<'tcx>
{
self.infcx().probe(move |_| {
match self.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
Ok(obligations) => {
self.evaluate_predicates_recursively(Some(stack), obligations.iter())
}
Err(()) => {
EvaluatedToErr(Unimplemented)
}
}
})
}

/// Check for the artificial impl that the compiler will create for an obligation like `X :
/// FnMut<..>` where `X` is an unboxed closure type.
///
Expand Down Expand Up @@ -1140,6 +1156,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidate_j: &SelectionCandidate<'tcx>)
-> bool
{
if candidate_i == candidate_j {
return true;
}

match (candidate_i, candidate_j) {
(&ImplCandidate(impl_def_id), &ParamCandidate(ref bound)) => {
debug!("Considering whether to drop param {} in favor of impl {}",
Expand Down Expand Up @@ -1179,8 +1199,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// the where clauses are in scope.
true
}
(&ParamCandidate(ref bound1), &ParamCandidate(ref bound2)) => {
self.infcx.probe(|_| {
let bound1 =
project::normalize_with_depth(self,
stack.obligation.cause.clone(),
stack.obligation.recursion_depth+1,
bound1);
let bound2 =
project::normalize_with_depth(self,
stack.obligation.cause.clone(),
stack.obligation.recursion_depth+1,
bound2);
let origin =
infer::RelateOutputImplTypes(stack.obligation.cause.span);
self.infcx
.sub_poly_trait_refs(false, origin, bound1.value, bound2.value)
.is_ok()
})
}
_ => {
*candidate_i == *candidate_j
false
}
}
}
Expand Down Expand Up @@ -1548,8 +1587,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

ParamCandidate(param) => {
self.confirm_param_candidate(obligation, param);
Ok(VtableParam)
let obligations = self.confirm_param_candidate(obligation, param);
Ok(VtableParam(obligations))
}

ImplCandidate(impl_def_id) => {
Expand All @@ -1576,7 +1615,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

ProjectionCandidate => {
self.confirm_projection_candidate(obligation);
Ok(VtableParam)
Ok(VtableParam(Vec::new()))
}
}
}
Expand All @@ -1597,6 +1636,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_param_candidate(&mut self,
obligation: &TraitObligation<'tcx>,
param: ty::PolyTraitRef<'tcx>)
-> Vec<PredicateObligation<'tcx>>
{
debug!("confirm_param_candidate({},{})",
obligation.repr(self.tcx()),
Expand All @@ -1606,11 +1646,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// where-clause trait-ref could be unified with the obligation
// trait-ref. Repeat that unification now without any
// transactional boundary; it should not fail.
match self.confirm_poly_trait_refs(obligation.cause.clone(),
obligation.predicate.to_poly_trait_ref(),
param.clone()) {
Ok(()) => { }
Err(_) => {
match self.match_where_clause_trait_ref(obligation, param.clone()) {
Ok(obligations) => obligations,
Err(()) => {
self.tcx().sess.bug(
format!("Where clause `{}` was applicable to `{}` but now is not",
param.repr(self.tcx()),
Expand Down Expand Up @@ -2037,19 +2075,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
})
}

/// Normalize `where_clause_trait_ref` and try to match it against
/// `obligation`. If successful, return any predicates that
/// result from the normalization. Normalization is necessary
/// because where-clauses are stored in the parameter environment
/// unnormalized.
fn match_where_clause_trait_ref(&mut self,
obligation: &TraitObligation<'tcx>,
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
-> Result<Vec<PredicateObligation<'tcx>>,()>
{
let where_clause_trait_ref =
project::normalize_with_depth(self,
obligation.cause.clone(),
obligation.recursion_depth+1,
&where_clause_trait_ref);

let () =
try!(self.match_poly_trait_ref(obligation, where_clause_trait_ref.value.clone()));

Ok(where_clause_trait_ref.obligations)
}

/// Returns `Ok` if `poly_trait_ref` being true implies that the
/// obligation is satisfied.
fn match_poly_trait_ref(&mut self,
obligation: &TraitObligation<'tcx>,
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
poly_trait_ref: ty::PolyTraitRef<'tcx>)
-> Result<(),()>
{
debug!("match_poly_trait_ref: obligation={} where_clause_trait_ref={}",
debug!("match_poly_trait_ref: obligation={} poly_trait_ref={}",
obligation.repr(self.tcx()),
where_clause_trait_ref.repr(self.tcx()));
poly_trait_ref.repr(self.tcx()));

let origin = infer::RelateOutputImplTypes(obligation.cause.span);
match self.infcx.sub_poly_trait_refs(false,
origin,
where_clause_trait_ref,
poly_trait_ref,
obligation.predicate.to_poly_trait_ref()) {
Ok(()) => Ok(()),
Err(_) => Err(()),
Expand Down
5 changes: 3 additions & 2 deletions src/librustc/middle/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,9 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::Vtable<'tcx, N> {
format!("VtableObject({})",
d.repr(tcx)),

super::VtableParam =>
format!("VtableParam"),
super::VtableParam(ref n) =>
format!("VtableParam({})",
n.repr(tcx)),

super::VtableBuiltin(ref d) =>
d.repr(tcx)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/ty_fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
traits::VtableFnPointer(ref d) => {
traits::VtableFnPointer(d.fold_with(folder))
}
traits::VtableParam => traits::VtableParam,
traits::VtableParam(ref n) => traits::VtableParam(n.fold_with(folder)),
traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)),
traits::VtableObject(ref d) => traits::VtableObject(d.fold_with(folder)),
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/meth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
format!("cannot get vtable for an object type: {}",
data.repr(bcx.tcx())).as_slice());
}
traits::VtableParam => {
traits::VtableParam(..) => {
bcx.sess().bug(
&format!("resolved vtable for {} to bad vtable {} in trans",
trait_ref.repr(bcx.tcx()),
Expand Down
34 changes: 34 additions & 0 deletions src/test/run-pass/associated-types-projection-in-where-clause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test a where clause that uses a non-normalized projection type.

trait Int
{
type T;
}

trait NonZero
{
fn non_zero(self) -> bool;
}

fn foo<I:Int<T=J>,J>(t: I) -> bool
where <I as Int>::T : NonZero
// ^~~~~~~~~~~~~ canonical form is just J
{
bar::<J>()
}

fn bar<NZ:NonZero>() -> bool { true }

fn main ()
{
}

1 comment on commit 47424cd

@nikomatsakis
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=nrc

Please sign in to comment.