about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/traits/mod.rs12
-rw-r--r--src/librustc/middle/traits/select.rs104
-rw-r--r--src/librustc/middle/traits/util.rs5
-rw-r--r--src/librustc/middle/ty_fold.rs2
-rw-r--r--src/librustc_trans/trans/meth.rs2
-rw-r--r--src/test/run-pass/associated-types-projection-in-where-clause.rs34
6 files changed, 129 insertions, 30 deletions
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index 3ef6694ce31..35029092517 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -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>),
@@ -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(),
         }
@@ -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)),
         }
@@ -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)),
         }
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index d6302976b9f..2520434f68f 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -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)
     }
@@ -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()
@@ -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));
@@ -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.
     ///
@@ -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 {}",
@@ -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
             }
         }
     }
@@ -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) => {
@@ -1576,7 +1615,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
             ProjectionCandidate => {
                 self.confirm_projection_candidate(obligation);
-                Ok(VtableParam)
+                Ok(VtableParam(Vec::new()))
             }
         }
     }
@@ -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()),
@@ -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()),
@@ -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(()),
diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs
index 272447027af..fe8362223e3 100644
--- a/src/librustc/middle/traits/util.rs
+++ b/src/librustc/middle/traits/util.rs
@@ -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)
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index b81a4ed2f58..cab41d66529 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -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)),
         }
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index 7ac062108f3..493df366d2a 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -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()),
diff --git a/src/test/run-pass/associated-types-projection-in-where-clause.rs b/src/test/run-pass/associated-types-projection-in-where-clause.rs
new file mode 100644
index 00000000000..10a459f3c36
--- /dev/null
+++ b/src/test/run-pass/associated-types-projection-in-where-clause.rs
@@ -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 ()
+{
+}