about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2015-01-05 21:50:01 -0500
committerNiko Matsakis <niko@alum.mit.edu>2015-01-06 17:28:37 -0500
commit279de38cc8d1c1a1c68d714b9384da32c240e420 (patch)
tree9719257ed895e45d7ceb832134e8d149e99ba611
parentfcc23238fd40c568cf3f7e1a2947ebb93277091e (diff)
downloadrust-279de38cc8d1c1a1c68d714b9384da32c240e420.tar.gz
rust-279de38cc8d1c1a1c68d714b9384da32c240e420.zip
Support methods invoked on projection types based on the bounds found in the trait.
-rw-r--r--src/librustc/middle/infer/combine.rs10
-rw-r--r--src/librustc/middle/infer/mod.rs33
-rw-r--r--src/librustc/middle/subst.rs2
-rw-r--r--src/librustc/middle/traits/project.rs2
-rw-r--r--src/librustc/middle/traits/select.rs2
-rw-r--r--src/librustc_typeck/check/method/probe.rs74
-rw-r--r--src/test/run-pass/method-projection.rs78
7 files changed, 185 insertions, 16 deletions
diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs
index dd711fcbf02..772d7b2532a 100644
--- a/src/librustc/middle/infer/combine.rs
+++ b/src/librustc/middle/infer/combine.rs
@@ -427,6 +427,16 @@ impl<'tcx> Combineable<'tcx> for ty::TraitRef<'tcx> {
     }
 }
 
+impl<'tcx> Combineable<'tcx> for Ty<'tcx> {
+    fn combine<C:Combine<'tcx>>(combiner: &C,
+                                a: &Ty<'tcx>,
+                                b: &Ty<'tcx>)
+                                -> cres<'tcx, Ty<'tcx>>
+    {
+        combiner.tys(*a, *b)
+    }
+}
+
 impl<'tcx> Combineable<'tcx> for ty::ProjectionPredicate<'tcx> {
     fn combine<C:Combine<'tcx>>(combiner: &C,
                                 a: &ty::ProjectionPredicate<'tcx>,
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index c2db81d3114..05b8d9da06f 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -39,7 +39,7 @@ use util::ppaux::{ty_to_string};
 use util::ppaux::{Repr, UserString};
 
 use self::coercion::Coerce;
-use self::combine::{Combine, CombineFields};
+use self::combine::{Combine, Combineable, CombineFields};
 use self::region_inference::{RegionVarBindings, RegionSnapshot};
 use self::equate::Equate;
 use self::sub::Sub;
@@ -360,17 +360,9 @@ pub fn can_mk_subty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
     })
 }
 
-pub fn can_mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
-                             a: Ty<'tcx>, b: Ty<'tcx>)
-                             -> ures<'tcx> {
-    debug!("can_mk_subty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
-    cx.probe(|_| {
-        let trace = TypeTrace {
-            origin: Misc(codemap::DUMMY_SP),
-            values: Types(expected_found(true, a, b))
-        };
-        cx.equate(true, trace).tys(a, b)
-    }).to_ures()
+pub fn can_mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> ures<'tcx>
+{
+    cx.can_equate(&a, &b)
 }
 
 pub fn mk_subr<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
@@ -1072,6 +1064,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
         self.region_vars.verify_generic_bound(origin, kind, a, bs);
     }
+
+    pub fn can_equate<T>(&self, a: &T, b: &T) -> ures<'tcx>
+        where T : Combineable<'tcx> + Repr<'tcx>
+    {
+        debug!("can_equate({}, {})", a.repr(self.tcx), b.repr(self.tcx));
+        self.probe(|_| {
+            // Gin up a dummy trace, since this won't be committed
+            // anyhow. We should make this typetrace stuff more
+            // generic so we don't have to do anything quite this
+            // terrible.
+            let e = self.tcx.types.err;
+            let trace = TypeTrace { origin: Misc(codemap::DUMMY_SP),
+                                    values: Types(expected_found(true, e, e)) };
+            let eq = self.equate(true, trace);
+            Combineable::combine(&eq, a, b)
+        }).to_ures()
+    }
 }
 
 impl<'tcx> TypeTrace<'tcx> {
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
index cd29ce28ac1..5227dc0a83a 100644
--- a/src/librustc/middle/subst.rs
+++ b/src/librustc/middle/subst.rs
@@ -394,7 +394,7 @@ impl<T> VecPerParamSpace<T> {
         self.content.as_slice()
     }
 
-    pub fn to_vec(self) -> Vec<T> {
+    pub fn into_vec(self) -> Vec<T> {
         self.content
     }
 
diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs
index 65f7ad296db..df246a9ed41 100644
--- a/src/librustc/middle/traits/project.rs
+++ b/src/librustc/middle/traits/project.rs
@@ -641,7 +641,7 @@ fn confirm_candidate<'cx,'tcx>(
             }
 
             match impl_ty {
-                Some(ty) => (ty, impl_vtable.nested.to_vec()),
+                Some(ty) => (ty, impl_vtable.nested.into_vec()),
                 None => {
                     // This means that the impl is missing a
                     // definition for the associated type. This error
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index 2393b7d733d..cdd040a7bc6 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -835,7 +835,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                bounds.repr(self.tcx()));
 
         let matching_bound =
-            util::elaborate_predicates(self.tcx(), bounds.predicates.to_vec())
+            util::elaborate_predicates(self.tcx(), bounds.predicates.into_vec())
             .filter_to_traits()
             .find(
                 |bound| self.infcx.probe(
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 6f2bbb7df2b..d7a42b59a12 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -62,6 +62,7 @@ enum CandidateKind<'tcx> {
                            subst::Substs<'tcx>, MethodIndex),
     UnboxedClosureCandidate(/* Trait */ ast::DefId, MethodIndex),
     WhereClauseCandidate(ty::PolyTraitRef<'tcx>, MethodIndex),
+    ProjectionCandidate(ast::DefId, MethodIndex),
 }
 
 pub struct Pick<'tcx> {
@@ -479,6 +480,10 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                                                  method.clone(),
                                                  matching_index);
 
+        self.assemble_projection_candidates(trait_def_id,
+                                            method.clone(),
+                                            matching_index);
+
         self.assemble_where_clause_candidates(trait_def_id,
                                               method,
                                               matching_index);
@@ -608,6 +613,64 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         }
     }
 
+    fn assemble_projection_candidates(&mut self,
+                                      trait_def_id: ast::DefId,
+                                      method: Rc<ty::Method<'tcx>>,
+                                      method_index: uint)
+    {
+        debug!("assemble_projection_candidates(\
+               trait_def_id={}, \
+               method={}, \
+               method_index={})",
+               trait_def_id.repr(self.tcx()),
+               method.repr(self.tcx()),
+               method_index);
+
+        for step in self.steps.iter() {
+            debug!("assemble_projection_candidates: step={}",
+                   step.repr(self.tcx()));
+
+            let projection_trait_ref = match step.self_ty.sty {
+                ty::ty_projection(ref data) => &data.trait_ref,
+                _ => continue,
+            };
+
+            debug!("assemble_projection_candidates: projection_trait_ref={}",
+                   projection_trait_ref.repr(self.tcx()));
+
+            let trait_def = ty::lookup_trait_def(self.tcx(), projection_trait_ref.def_id);
+            let bounds = trait_def.generics.to_bounds(self.tcx(), projection_trait_ref.substs);
+            let predicates = bounds.predicates.into_vec();
+            debug!("assemble_projection_candidates: predicates={}",
+                   predicates.repr(self.tcx()));
+            for poly_bound in
+                traits::elaborate_predicates(self.tcx(), predicates)
+                .filter_map(|p| p.to_opt_poly_trait_ref()) // TODO filter_to_traits()
+                .filter(|b| b.def_id() == trait_def_id)
+            {
+                let bound = self.erase_late_bound_regions(&poly_bound);
+
+                debug!("assemble_projection_candidates: projection_trait_ref={} bound={}",
+                       projection_trait_ref.repr(self.tcx()),
+                       bound.repr(self.tcx()));
+
+                if self.infcx().can_equate(&step.self_ty, &bound.self_ty()).is_ok() {
+                    let xform_self_ty = self.xform_self_ty(&method, bound.substs);
+
+                    debug!("assemble_projection_candidates: bound={} xform_self_ty={}",
+                           bound.repr(self.tcx()),
+                           xform_self_ty.repr(self.tcx()));
+
+                    self.extension_candidates.push(Candidate {
+                        xform_self_ty: xform_self_ty,
+                        method_ty: method.clone(),
+                        kind: ProjectionCandidate(trait_def_id, method_index)
+                    });
+                }
+            }
+        }
+    }
+
     fn assemble_where_clause_candidates(&mut self,
                                         trait_def_id: ast::DefId,
                                         method_ty: Rc<ty::Method<'tcx>>,
@@ -616,7 +679,6 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         debug!("assemble_where_clause_candidates(trait_def_id={})",
                trait_def_id.repr(self.tcx()));
 
-        // Check whether there are any where-clauses pertaining to this trait.
         let caller_predicates =
             self.fcx.inh.param_env.caller_bounds.predicates.as_slice().to_vec();
         for poly_bound in traits::elaborate_predicates(self.tcx(), caller_predicates)
@@ -835,6 +897,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                         norm_obligations.iter().all(|o| selcx.evaluate_obligation(o))
                 }
 
+                ProjectionCandidate(..) |
                 ObjectCandidate(..) |
                 UnboxedClosureCandidate(..) |
                 WhereClauseCandidate(..) => {
@@ -1072,6 +1135,9 @@ impl<'tcx> Candidate<'tcx> {
 
                     WhereClausePick((*trait_ref).clone(), index)
                 }
+                ProjectionCandidate(def_id, index) => {
+                    TraitPick(def_id, index)
+                }
             }
         }
     }
@@ -1083,6 +1149,7 @@ impl<'tcx> Candidate<'tcx> {
             ExtensionImplCandidate(def_id, _, _, _) => ImplSource(def_id),
             UnboxedClosureCandidate(trait_def_id, _) => TraitSource(trait_def_id),
             WhereClauseCandidate(ref trait_ref, _) => TraitSource(trait_ref.def_id()),
+            ProjectionCandidate(trait_def_id, _) => TraitSource(trait_def_id),
         }
     }
 
@@ -1101,6 +1168,9 @@ impl<'tcx> Candidate<'tcx> {
             WhereClauseCandidate(ref trait_ref, method_num) => {
                 Some((trait_ref.def_id(), method_num))
             }
+            ProjectionCandidate(trait_def_id, method_num) => {
+                Some((trait_def_id, method_num))
+            }
         }
     }
 }
@@ -1127,6 +1197,8 @@ impl<'tcx> Repr<'tcx> for CandidateKind<'tcx> {
                 format!("UnboxedClosureCandidate({},{})", a.repr(tcx), b),
             WhereClauseCandidate(ref a, ref b) =>
                 format!("WhereClauseCandidate({},{})", a.repr(tcx), b),
+            ProjectionCandidate(ref a, ref b) =>
+                format!("ProjectionCandidate({},{})", a.repr(tcx), b),
         }
     }
 }
diff --git a/src/test/run-pass/method-projection.rs b/src/test/run-pass/method-projection.rs
new file mode 100644
index 00000000000..6f72a163981
--- /dev/null
+++ b/src/test/run-pass/method-projection.rs
@@ -0,0 +1,78 @@
+// Copyright 2014 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 that we can use method notation to call methods based on a
+// projection bound from a trait. Issue #20469.
+
+///////////////////////////////////////////////////////////////////////////
+
+trait MakeString {
+    fn make_string(&self) -> String;
+}
+
+impl MakeString for int {
+    fn make_string(&self) -> String {
+        format!("{}", *self)
+    }
+}
+
+impl MakeString for uint {
+    fn make_string(&self) -> String {
+        format!("{}", *self)
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+trait Foo {
+    type F: MakeString;
+
+    fn get(&self) -> &Self::F;
+}
+
+fn foo<F:Foo>(f: &F) -> String {
+    f.get().make_string()
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+struct SomeStruct {
+    field: int,
+}
+
+impl Foo for SomeStruct {
+    type F = int;
+
+    fn get(&self) -> &int {
+        &self.field
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+struct SomeOtherStruct {
+    field: uint,
+}
+
+impl Foo for SomeOtherStruct {
+    type F = uint;
+
+    fn get(&self) -> &uint {
+        &self.field
+    }
+}
+
+fn main() {
+    let x = SomeStruct { field: 22 };
+    assert_eq!(foo(&x), format!("22"));
+
+    let x = SomeOtherStruct { field: 44 };
+    assert_eq!(foo(&x), format!("44"));
+}