about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2014-12-26 07:07:55 -0500
committerNiko Matsakis <niko@alum.mit.edu>2014-12-30 09:36:22 -0500
commitde806bc057caa599fce959ce56259e6e919f1041 (patch)
tree8531798d6dc6a503691210b7b9ab5fb4e642e069
parent5b53b11ad9aed822ce4c573714de6cb28289f321 (diff)
downloadrust-de806bc057caa599fce959ce56259e6e919f1041.tar.gz
rust-de806bc057caa599fce959ce56259e6e919f1041.zip
Teach `project` to project associated types out of object types.
-rw-r--r--src/librustc/middle/traits/project.rs55
-rw-r--r--src/librustc/middle/ty.rs25
-rw-r--r--src/librustc_typeck/check/vtable.rs53
3 files changed, 116 insertions, 17 deletions
diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs
index 083227d02bd..0594c0ac3b8 100644
--- a/src/librustc/middle/traits/project.rs
+++ b/src/librustc/middle/traits/project.rs
@@ -19,7 +19,7 @@ use super::VtableImplData;
 
 use middle::infer;
 use middle::subst::Subst;
-use middle::ty::{mod, ToPolyTraitRef, Ty};
+use middle::ty::{mod, AsPredicate, ToPolyTraitRef, Ty};
 use std::fmt;
 use util::ppaux::Repr;
 
@@ -46,7 +46,7 @@ pub enum ProjectionError<'tcx> {
 
 #[deriving(Clone)]
 pub struct MismatchedProjectionTypes<'tcx> {
-    pub err: ty::type_err<'tcx> // TODO expected/actual/etc
+    pub err: ty::type_err<'tcx>
 }
 
 pub type ProjectionResult<'tcx, T> = Result<T, ProjectionError<'tcx>>;
@@ -123,9 +123,20 @@ pub fn project_type<'cx,'tcx>(
                                                 obligation,
                                                 &mut candidates);
 
-    let () = try!(assemble_candidates_from_impls(selcx,
-                                                 obligation,
-                                                 &mut candidates));
+    let () = assemble_candidates_from_object_type(selcx,
+                                                  obligation,
+                                                  &mut candidates);
+
+    if candidates.vec.is_empty() {
+        // TODO This `if` is not necessarily wrong, but it needs an
+        // explanation, and it should probably be accompanied by a
+        // similar rule in `select.rs`. Currently it's *needed*
+        // because the impl-trait-for-trait branch has not landed.
+
+        let () = try!(assemble_candidates_from_impls(selcx,
+                                                     obligation,
+                                                     &mut candidates));
+    }
 
     debug!("{} candidates, ambiguous={}",
            candidates.vec.len(),
@@ -155,9 +166,21 @@ fn assemble_candidates_from_param_env<'cx,'tcx>(
     obligation:  &ProjectionTyObligation<'tcx>,
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
 {
-    let infcx = selcx.infcx();
     let env_predicates = selcx.param_env().caller_bounds.predicates.clone();
     let env_predicates = env_predicates.iter().cloned().collect();
+    assemble_candidates_from_predicates(selcx, obligation, candidate_set, env_predicates);
+}
+
+fn assemble_candidates_from_predicates<'cx,'tcx>(
+    selcx: &mut SelectionContext<'cx,'tcx>,
+    obligation:  &ProjectionTyObligation<'tcx>,
+    candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
+    env_predicates: Vec<ty::Predicate<'tcx>>)
+{
+    debug!("assemble_candidates_from_predicates(obligation={}, env_predicates={})",
+           obligation.repr(selcx.tcx()),
+           env_predicates.repr(selcx.tcx()));
+    let infcx = selcx.infcx();
     for predicate in elaborate_predicates(selcx.tcx(), env_predicates) {
         match predicate {
             ty::Predicate::Projection(ref data) => {
@@ -183,6 +206,26 @@ fn assemble_candidates_from_param_env<'cx,'tcx>(
     }
 }
 
+fn assemble_candidates_from_object_type<'cx,'tcx>(
+    selcx: &mut SelectionContext<'cx,'tcx>,
+    obligation:  &ProjectionTyObligation<'tcx>,
+    candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
+{
+    let infcx = selcx.infcx();
+    let trait_ref = infcx.resolve_type_vars_if_possible(&obligation.predicate.trait_ref);
+    debug!("assemble_candidates_from_object_type(trait_ref={})",
+           trait_ref.repr(infcx.tcx));
+    let self_ty = trait_ref.self_ty();
+    let data = match self_ty.sty {
+        ty::ty_trait(ref data) => data,
+        _ => { return; }
+    };
+    let env_predicates = data.projection_bounds_with_self_ty(self_ty).iter()
+                                                                     .map(|p| p.as_predicate())
+                                                                     .collect();
+    assemble_candidates_from_predicates(selcx, obligation, candidate_set, env_predicates)
+}
+
 fn assemble_candidates_from_impls<'cx,'tcx>(
     selcx: &mut SelectionContext<'cx,'tcx>,
     obligation:  &ProjectionTyObligation<'tcx>,
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index e989f5a2cc2..98539b4d152 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -1401,6 +1401,31 @@ impl<'tcx> TyTrait<'tcx> {
             substs: tcx.mk_substs(self.principal.0.substs.with_self_ty(self_ty)),
         }))
     }
+
+    pub fn projection_bounds_with_self_ty(&self, self_ty: Ty<'tcx>)
+                                          -> Vec<ty::PolyProjectionPredicate<'tcx>>
+    {
+        // otherwise the escaping regions would be captured by the binders
+        assert!(!self_ty.has_escaping_regions());
+
+        self.bounds.projection_bounds.iter()
+            .map(|in_poly_projection_predicate| {
+                let in_projection_ty = &in_poly_projection_predicate.0.projection_ty;
+                let trait_ref =
+                    Rc::new(ty::TraitRef::new(
+                        in_projection_ty.trait_ref.def_id,
+                        in_projection_ty.trait_ref.substs.with_self_ty(self_ty)));
+                let projection_ty = ty::ProjectionTy {
+                    trait_ref: trait_ref,
+                    item_name: in_projection_ty.item_name
+                };
+                ty::Binder(ty::ProjectionPredicate {
+                    projection_ty: projection_ty,
+                    ty: in_poly_projection_predicate.0.ty
+                })
+            })
+            .collect()
+    }
 }
 
 /// A complete reference to a trait. These take numerous guises in syntax,
diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs
index e8bd5c088fc..f5cbfcdcc52 100644
--- a/src/librustc_typeck/check/vtable.rs
+++ b/src/librustc_typeck/check/vtable.rs
@@ -13,12 +13,12 @@ use middle::subst::{FnSpace, SelfSpace};
 use middle::traits;
 use middle::traits::{Obligation, ObligationCause};
 use middle::traits::report_fulfillment_errors;
-use middle::ty::{mod, Ty, AsPredicate, ToPolyTraitRef};
+use middle::ty::{mod, Ty, AsPredicate};
 use middle::infer;
-use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap::Span;
-use util::ppaux::{Repr, ty_to_string};
+use util::nodemap::FnvHashSet;
+use util::ppaux::{Repr, UserString};
 
 pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                    cast_expr: &ast::Expr,
@@ -133,10 +133,33 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
                                  object_trait: &ty::TyTrait<'tcx>,
                                  span: Span)
 {
+    // Also check that the type `object_trait` specifies all
+    // associated types for all supertraits.
+    let mut associated_types: FnvHashSet<(ast::DefId, ast::Name)> = FnvHashSet::new();
+
     let object_trait_ref =
         object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
-    for tr in traits::supertraits(tcx, object_trait_ref) {
+    for tr in traits::supertraits(tcx, object_trait_ref.clone()) {
         check_object_safety_inner(tcx, &tr, span);
+
+        let trait_def = ty::lookup_trait_def(tcx, object_trait_ref.def_id());
+        for &associated_type_name in trait_def.associated_type_names.iter() {
+            associated_types.insert((object_trait_ref.def_id(), associated_type_name));
+        }
+    }
+
+    for projection_bound in object_trait.bounds.projection_bounds.iter() {
+        let pair = (projection_bound.0.projection_ty.trait_ref.def_id,
+                    projection_bound.0.projection_ty.item_name);
+        associated_types.remove(&pair);
+    }
+
+    for (trait_def_id, name) in associated_types.into_iter() {
+        tcx.sess.span_err(
+            span,
+            format!("the value of the associated type `{}` (from the trait `{}`) must be specified",
+                    name.user_string(tcx),
+                    ty::item_path_str(tcx, trait_def_id)).as_slice());
     }
 }
 
@@ -201,7 +224,7 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
                 Some(format!(
                     "cannot call a method (`{}`) whose type contains \
                      a self-type (`{}`) through a trait object",
-                    method_name, ty_to_string(tcx, ty)))
+                    method_name, ty.user_string(tcx)))
             } else {
                 None
             }
@@ -343,15 +366,15 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
            referent_ty.repr(fcx.tcx()),
            object_trait_ty.repr(fcx.tcx()));
 
+    let cause = ObligationCause::new(span,
+                                     fcx.body_id,
+                                     traits::ObjectCastObligation(object_trait_ty));
+
     // Create the obligation for casting from T to Trait.
     let object_trait_ref =
         object_trait.principal_trait_ref_with_self_ty(fcx.tcx(), referent_ty);
     let object_obligation =
-        Obligation::new(
-            ObligationCause::new(span,
-                                 fcx.body_id,
-                                 traits::ObjectCastObligation(object_trait_ty)),
-            object_trait_ref.as_predicate());
+        Obligation::new(cause.clone(), object_trait_ref.as_predicate());
     fcx.register_predicate(object_obligation);
 
     // Create additional obligations for all the various builtin
@@ -362,7 +385,15 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         fcx.register_builtin_bound(
             referent_ty,
             builtin_bound,
-            ObligationCause::new(span, fcx.body_id, traits::ObjectCastObligation(object_trait_ty)));
+            cause.clone());
+    }
+
+    // Finally, create obligations for the projection predicates.
+    let projection_bounds = object_trait.projection_bounds_with_self_ty(referent_ty);
+    for projection_bound in projection_bounds.iter() {
+        let projection_obligation =
+            Obligation::new(cause.clone(), projection_bound.as_predicate());
+        fcx.register_predicate(projection_obligation);
     }
 
     object_trait_ref