about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2014-12-04 11:58:52 -0800
committerNick Cameron <ncameron@mozilla.com>2014-12-16 13:50:24 +1300
commit65616644af943c6de7879d16799e5fb488055fd5 (patch)
treeb65964b963cfd4ea5f771d72ed6a8997b5d41685
parent0669a432a2e09ad08886cb2138dbe9f5d681fb7f (diff)
downloadrust-65616644af943c6de7879d16799e5fb488055fd5.tar.gz
rust-65616644af943c6de7879d16799e5fb488055fd5.zip
Path types to associated types with form `T::A`
Closes #18433
-rw-r--r--src/librustc/middle/astencode.rs4
-rw-r--r--src/librustc/middle/def.rs25
-rw-r--r--src/librustc/middle/mem_categorization.rs2
-rw-r--r--src/librustc/middle/resolve.rs44
-rw-r--r--src/librustc_trans/save/mod.rs1
-rw-r--r--src/librustc_trans/trans/callee.rs2
-rw-r--r--src/librustc_typeck/astconv.rs77
-rw-r--r--src/librustc_typeck/check/mod.rs6
-rw-r--r--src/test/compile-fail/assoc-eq-1.rs2
-rw-r--r--src/test/compile-fail/assoc-path-1.rs26
-rw-r--r--src/test/compile-fail/assoc-path-2.rs34
-rw-r--r--src/test/run-pass/assoc-sugar-path.rs46
12 files changed, 244 insertions, 25 deletions
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 26d70502a5b..dafe372c9a9 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -443,6 +443,10 @@ impl tr for def::Def {
           def::DefTrait(did) => def::DefTrait(did.tr(dcx)),
           def::DefTy(did, is_enum) => def::DefTy(did.tr(dcx), is_enum),
           def::DefAssociatedTy(did) => def::DefAssociatedTy(did.tr(dcx)),
+          def::DefAssociatedPath(def::TyParamProvenance::FromSelf(did), ident) =>
+              def::DefAssociatedPath(def::TyParamProvenance::FromSelf(did.tr(dcx)), ident),
+          def::DefAssociatedPath(def::TyParamProvenance::FromParam(did), ident) =>
+              def::DefAssociatedPath(def::TyParamProvenance::FromParam(did.tr(dcx)), ident),
           def::DefPrimTy(p) => def::DefPrimTy(p),
           def::DefTyParam(s, did, v) => def::DefTyParam(s, did.tr(dcx), v),
           def::DefUse(did) => def::DefUse(did.tr(dcx)),
diff --git a/src/librustc/middle/def.rs b/src/librustc/middle/def.rs
index ca60ac45e26..8573dc747bb 100644
--- a/src/librustc/middle/def.rs
+++ b/src/librustc/middle/def.rs
@@ -28,6 +28,10 @@ pub enum Def {
     DefVariant(ast::DefId /* enum */, ast::DefId /* variant */, bool /* is_structure */),
     DefTy(ast::DefId, bool /* is_enum */),
     DefAssociatedTy(ast::DefId),
+    // A partially resolved path to an associated type `T::U` where `T` is a concrete
+    // type (indicated by the DefId) which implements a trait which has an associated
+    // type `U` (indicated by the Ident).
+    DefAssociatedPath(TyParamProvenance, ast::Ident),
     DefTrait(ast::DefId),
     DefPrimTy(ast::PrimTy),
     DefTyParam(ParamSpace, ast::DefId, uint),
@@ -60,6 +64,12 @@ pub enum MethodProvenance {
     FromImpl(ast::DefId),
 }
 
+#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
+pub enum TyParamProvenance {
+    FromSelf(ast::DefId),
+    FromParam(ast::DefId),
+}
+
 impl MethodProvenance {
     pub fn map<F>(self, f: F) -> MethodProvenance where
         F: FnOnce(ast::DefId) -> ast::DefId,
@@ -73,6 +83,17 @@ impl MethodProvenance {
 
 impl Copy for MethodProvenance {}
 
+impl TyParamProvenance {
+    pub fn def_id(&self) -> ast::DefId {
+        match *self {
+            TyParamProvenance::FromSelf(ref did) => did.clone(),
+            TyParamProvenance::FromParam(ref did) => did.clone(),
+        }
+    }
+}
+
+impl Copy for TyParamProvenance {}
+
 impl Def {
     pub fn def_id(&self) -> ast::DefId {
         match *self {
@@ -80,7 +101,9 @@ impl Def {
             DefForeignMod(id) | DefStatic(id, _) |
             DefVariant(_, id, _) | DefTy(id, _) | DefAssociatedTy(id) |
             DefTyParam(_, id, _) | DefUse(id) | DefStruct(id) | DefTrait(id) |
-            DefMethod(id, _, _) | DefConst(id) => {
+            DefMethod(id, _, _) | DefConst(id) |
+            DefAssociatedPath(TyParamProvenance::FromSelf(id), _) |
+            DefAssociatedPath(TyParamProvenance::FromParam(id), _) => {
                 id
             }
             DefLocal(id) |
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 652847a6343..86b912a579f 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -595,7 +595,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
           def::DefTrait(_) | def::DefTy(..) | def::DefPrimTy(_) |
           def::DefTyParam(..) | def::DefTyParamBinder(..) | def::DefRegion(_) |
           def::DefLabel(_) | def::DefSelfTy(..) | def::DefMethod(..) |
-          def::DefAssociatedTy(..) => {
+          def::DefAssociatedTy(..) | def::DefAssociatedPath(..)=> {
               Ok(Rc::new(cmt_ {
                   id:id,
                   span:span,
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 30bad840e17..734453db693 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -2027,7 +2027,7 @@ impl<'a> Resolver<'a> {
                                                   is_public,
                                                   DUMMY_SP)
           }
-          DefTy(..) | DefAssociatedTy(..) => {
+          DefTy(..) | DefAssociatedTy(..) | DefAssociatedPath(..) => {
               debug!("(building reduced graph for external \
                       crate) building type {}", final_ident);
 
@@ -3361,8 +3361,7 @@ impl<'a> Resolver<'a> {
         let module_path_len = module_path.len();
         assert!(module_path_len > 0);
 
-        debug!("(resolving module path for import) processing `{}` rooted at \
-               `{}`",
+        debug!("(resolving module path for import) processing `{}` rooted at `{}`",
                self.names_to_string(module_path),
                self.module_to_string(&*module_));
 
@@ -4960,14 +4959,10 @@ impl<'a> Resolver<'a> {
                             result_def =
                                 Some((DefPrimTy(primitive_type), LastMod(AllPublic)));
 
-                            if path.segments
-                                   .iter()
-                                   .any(|s| s.parameters.has_lifetimes()) {
+                            if path.segments[0].parameters.has_lifetimes() {
                                 span_err!(self.session, path.span, E0157,
                                     "lifetime parameters are not allowed on this type");
-                            } else if path.segments
-                                          .iter()
-                                          .any(|s| !s.parameters.is_empty()) {
+                            } else if !path.segments[0].parameters.is_empty() {
                                 span_err!(self.session, path.span, E0153,
                                     "type parameters are not allowed on this type");
                             }
@@ -5309,6 +5304,34 @@ impl<'a> Resolver<'a> {
             self.resolve_type(&*binding.ty);
         }
 
+        // A special case for sugared associated type paths `T::A` where `T` is
+        // a type parameter and `A` is an associated type on some bound of `T`.
+        if namespace == TypeNS && path.segments.len() == 2 {
+            match self.resolve_identifier(path.segments[0].identifier,
+                                          TypeNS,
+                                          true,
+                                          path.span) {
+                Some((def, last_private)) => {
+                    match def {
+                        DefTyParam(_, did, _) => {
+                            let def = DefAssociatedPath(TyParamProvenance::FromParam(did),
+                                                        path.segments.last()
+                                                            .unwrap().identifier);
+                            return Some((def, last_private));
+                        }
+                        DefSelfTy(nid) => {
+                            let def = DefAssociatedPath(TyParamProvenance::FromSelf(local_def(nid)),
+                                                        path.segments.last()
+                                                            .unwrap().identifier);
+                            return Some((def, last_private));
+                        }
+                        _ => {}
+                    }
+                }
+                _ => {}
+            }
+        }
+
         if path.global {
             return self.resolve_crate_relative_path(path, namespace);
         }
@@ -5561,8 +5584,7 @@ impl<'a> Resolver<'a> {
         let search_result = match namespace {
             ValueNS => {
                 let renamed = mtwt::resolve(ident);
-                self.search_ribs(self.value_ribs.as_slice(),
-                                 renamed, span)
+                self.search_ribs(self.value_ribs.as_slice(), renamed, span)
             }
             TypeNS => {
                 let name = ident.name;
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 712d6217dde..1a4f06663ef 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -221,6 +221,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
             def::DefStruct(_) => Some(recorder::StructRef),
             def::DefTy(..) |
             def::DefAssociatedTy(..) |
+            def::DefAssociatedPath(..) |
             def::DefTrait(_) => Some(recorder::TypeRef),
             def::DefStatic(_, _) |
             def::DefConst(_) |
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs
index 68d39a7e2ac..ff2f686fff8 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -205,7 +205,7 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr)
             def::DefTy(..) | def::DefPrimTy(..) | def::DefAssociatedTy(..) |
             def::DefUse(..) | def::DefTyParamBinder(..) |
             def::DefRegion(..) | def::DefLabel(..) | def::DefTyParam(..) |
-            def::DefSelfTy(..) => {
+            def::DefSelfTy(..) | def::DefAssociatedPath(..) => {
                 bcx.tcx().sess.span_bug(
                     ref_expr.span,
                     format!("cannot translate def {} \
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index b3272a14753..02fdd59a1c9 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -73,6 +73,9 @@ pub trait AstConv<'tcx> {
     fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
     fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype<'tcx>;
     fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef<'tcx>>;
+    fn get_free_substs(&self) -> Option<&Substs<'tcx>> {
+        None
+    }
 
     /// What type should we use when a type is omitted?
     fn ty_infer(&self, span: Span) -> Ty<'tcx>;
@@ -517,9 +520,9 @@ fn convert_parenthesized_parameters<'tcx,AC>(this: &AC,
 }
 
 
-/// Instantiates the path for the given trait reference, assuming that it's bound to a valid trait
-/// type. Returns the def_id for the defining trait. Fails if the type is a type other than a trait
-/// type.
+/// Instantiates the path for the given trait reference, assuming that it's
+/// bound to a valid trait type. Returns the def_id for the defining trait.
+/// Fails if the type is a type other than a trait type.
 pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
                                          rscope: &RS,
                                          ast_trait_ref: &ast::TraitRef,
@@ -846,18 +849,29 @@ fn qpath_to_ty<'tcx,AC,RS>(this: &AC,
 
     debug!("qpath_to_ty: trait_ref={}", trait_ref.repr(this.tcx()));
 
+    if let Some(ty) = find_assoc_ty(this, &*trait_ref, qpath.item_name) {
+        return ty;
+    }
+
+    this.tcx().sess.span_bug(ast_ty.span,
+                             "this associated type didn't get added \
+                              as a parameter for some reason")
+}
+
+fn find_assoc_ty<'tcx, AC>(this: &AC,
+                           trait_ref: &ty::TraitRef<'tcx>,
+                           type_name: ast::Ident)
+                           -> Option<Ty<'tcx>>
+where AC: AstConv<'tcx> {
     let trait_def = this.get_trait_def(trait_ref.def_id);
 
     for ty_param_def in trait_def.generics.types.get_slice(AssocSpace).iter() {
-        if ty_param_def.name == qpath.item_name.name {
-            debug!("qpath_to_ty: corresponding ty_param_def={}", ty_param_def);
-            return trait_ref.substs.type_for_def(ty_param_def);
+        if ty_param_def.name == type_name.name {
+            return Some(trait_ref.substs.type_for_def(ty_param_def));
         }
     }
 
-    this.tcx().sess.span_bug(ast_ty.span,
-                             "this associated type didn't get added \
-                              as a parameter for some reason")
+    None
 }
 
 // Parses the programmer's textual representation of a type into our
@@ -1011,6 +1025,51 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                                                   .get()).as_slice());
                         ty::mk_err()
                     }
+                    def::DefAssociatedPath(typ, assoc_ident) => {
+                        // FIXME(#19541): in both branches we should consider
+                        // associated types in super-traits.
+                        let (assoc_tys, tp_name): (Vec<_>, _) = match typ {
+                            def::TyParamProvenance::FromParam(did) => {
+                                let ty_param_defs = tcx.ty_param_defs.borrow();
+                                let tp_def = &(*ty_param_defs)[did.node];
+                                let assoc_tys = tp_def.bounds.trait_bounds.iter()
+                                    .filter_map(|b| find_assoc_ty(this, &**b, assoc_ident))
+                                    .collect();
+                                (assoc_tys, token::get_name(tp_def.name).to_string())
+                            }
+                            def::TyParamProvenance::FromSelf(did) => {
+                                let assoc_tys = find_assoc_ty(this,
+                                                              &*this.get_trait_def(did).trait_ref,
+                                                              assoc_ident)
+                                    .into_iter().collect();
+                                (assoc_tys, "Self".to_string())
+                            }
+                        };
+
+                        if assoc_tys.len() == 0 {
+                            tcx.sess.span_err(ast_ty.span,
+                                              format!("associated type `{}` not \
+                                                       found for type parameter `{}`",
+                                                      token::get_ident(assoc_ident),
+                                                      tp_name).as_slice());
+                            return ty::mk_err()
+                        }
+
+                        if assoc_tys.len() > 1 {
+                            tcx.sess.span_err(ast_ty.span,
+                                              format!("ambiguous associated type \
+                                                       `{}` in bounds of `{}`",
+                                                      token::get_ident(assoc_ident),
+                                                      tp_name).as_slice());
+                        }
+
+                        let mut result_ty = assoc_tys[0];
+                        if let Some(substs) = this.get_free_substs() {
+                            result_ty = result_ty.subst(tcx, substs);
+                        }
+
+                        result_ty
+                    }
                     _ => {
                         tcx.sess.span_fatal(ast_ty.span,
                                             format!("found value name used \
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index cf7868148e7..d4e29892c9f 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1535,6 +1535,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
         ty::lookup_trait_def(self.tcx(), id)
     }
 
+    fn get_free_substs(&self) -> Option<&Substs<'tcx>> {
+        Some(&self.inh.param_env.free_substs)
+    }
+
     fn ty_infer(&self, _span: Span) -> Ty<'tcx> {
         self.infcx().next_ty_var()
     }
@@ -4866,6 +4870,7 @@ pub fn polytype_for_def<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
       def::DefTrait(_) |
       def::DefTy(..) |
       def::DefAssociatedTy(..) |
+      def::DefAssociatedPath(..) |
       def::DefPrimTy(_) |
       def::DefTyParam(..)=> {
         fcx.ccx.tcx.sess.span_bug(sp, "expected value, found type");
@@ -4974,6 +4979,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         def::DefTyParamBinder(..) |
         def::DefTy(..) |
         def::DefAssociatedTy(..) |
+        def::DefAssociatedPath(..) |
         def::DefTrait(..) |
         def::DefPrimTy(..) |
         def::DefTyParam(..) => {
diff --git a/src/test/compile-fail/assoc-eq-1.rs b/src/test/compile-fail/assoc-eq-1.rs
index 4fd53150618..e93d9db28cf 100644
--- a/src/test/compile-fail/assoc-eq-1.rs
+++ b/src/test/compile-fail/assoc-eq-1.rs
@@ -20,8 +20,6 @@ pub trait Foo {
 
 fn foo2<I: Foo>(x: I) {
     let _: A = x.boo(); //~ERROR use of undeclared
-    let _: I::A = x.boo(); //~ERROR failed to resolve
-    //~^ERROR use of undeclared type name `I::A`
 }
 
 pub fn main() {}
diff --git a/src/test/compile-fail/assoc-path-1.rs b/src/test/compile-fail/assoc-path-1.rs
new file mode 100644
index 00000000000..41f5bc17b56
--- /dev/null
+++ b/src/test/compile-fail/assoc-path-1.rs
@@ -0,0 +1,26 @@
+// 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 have one and only one associated type per ref.
+
+#![feature(associated_types)]
+
+pub trait Foo {
+    type A;
+}
+pub trait Bar {
+    type A;
+}
+
+pub fn f1<T>(a: T, x: T::A) {} //~ERROR associated type `A` not found
+pub fn f2<T: Foo + Bar>(a: T, x: T::A) {} //~ERROR ambiguous associated type `A`
+
+pub fn main() {}
+
diff --git a/src/test/compile-fail/assoc-path-2.rs b/src/test/compile-fail/assoc-path-2.rs
new file mode 100644
index 00000000000..caf8ab3695d
--- /dev/null
+++ b/src/test/compile-fail/assoc-path-2.rs
@@ -0,0 +1,34 @@
+// 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 type checking of uses of associated types via sugary paths.
+
+#![feature(associated_types)]
+
+pub trait Foo {
+    type A;
+}
+
+impl Foo for int {
+    type A = uint;
+}
+
+pub fn f1<T: Foo>(a: T, x: T::A) {}
+pub fn f2<T: Foo>(a: T) -> T::A {
+    panic!();
+}
+
+pub fn main() {
+    f1(2i, 4i); //~ERROR the trait `Foo` is not implemented
+    f1(2u, 4u); //~ERROR the trait `Foo` is not implemented
+    f1(2u, 4i); //~ERROR the trait `Foo` is not implemented
+
+    let _: int = f2(2i); //~ERROR mismatched types: expected `int`, found `uint`
+}
diff --git a/src/test/run-pass/assoc-sugar-path.rs b/src/test/run-pass/assoc-sugar-path.rs
new file mode 100644
index 00000000000..28c06f51ceb
--- /dev/null
+++ b/src/test/run-pass/assoc-sugar-path.rs
@@ -0,0 +1,46 @@
+// 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 paths to associated types using the type-parameter-only sugar.
+
+#![feature(associated_types)]
+
+pub trait Foo {
+    type A;
+    fn boo(&self) -> Self::A;
+}
+
+impl Foo for int {
+    type A = uint;
+    fn boo(&self) -> uint {
+        5
+    }
+}
+
+// Using a type via a function.
+pub fn bar<T: Foo>(a: T, x: T::A) -> T::A {
+    let _: T::A = a.boo();
+    x
+}
+
+// Using a type via an impl.
+trait C {
+    fn f();
+}
+struct B<X>;
+impl<T: Foo> C for B<T> {
+    fn f() {
+        let x: T::A = panic!();
+    }
+}
+
+pub fn main() {
+    let z: uint = bar(2i, 4u);
+}