about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-08-07 09:47:28 -0700
committerPatrick Walton <pcwalton@mimiga.net>2013-08-27 18:46:51 -0700
commit3b6314c39bfc13b5a41c53f13c3fafa7ad91e062 (patch)
treef43929dd9d9874c8cd95a69f7c47313b2bcf055a /src
parent5c3504799deb78d986f8267f753a87fb9e73a452 (diff)
downloadrust-3b6314c39bfc13b5a41c53f13c3fafa7ad91e062.tar.gz
rust-3b6314c39bfc13b5a41c53f13c3fafa7ad91e062.zip
librustc: Add support for type parameters in the middle of paths.
For example, `foo::<T>::bar::<U>`.

This doesn't enforce that the type parameters are in the right
positions, however.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/front/std_inject.rs17
-rw-r--r--src/librustc/front/test.rs31
-rw-r--r--src/librustc/metadata/encoder.rs3
-rw-r--r--src/librustc/metadata/tydecode.rs20
-rw-r--r--src/librustc/middle/check_const.rs2
-rw-r--r--src/librustc/middle/privacy.rs14
-rw-r--r--src/librustc/middle/region.rs6
-rw-r--r--src/librustc/middle/resolve.rs101
-rw-r--r--src/librustc/middle/trans/consts.rs4
-rw-r--r--src/librustc/middle/typeck/astconv.rs34
-rw-r--r--src/librustc/middle/typeck/check/mod.rs26
-rw-r--r--src/libsyntax/ast.rs21
-rw-r--r--src/libsyntax/ast_util.rs24
-rw-r--r--src/libsyntax/ext/base.rs20
-rw-r--r--src/libsyntax/ext/build.rs25
-rw-r--r--src/libsyntax/ext/concat_idents.rs11
-rw-r--r--src/libsyntax/ext/expand.rs31
-rw-r--r--src/libsyntax/ext/tt/macro_parser.rs6
-rw-r--r--src/libsyntax/fold.rs8
-rw-r--r--src/libsyntax/oldvisit.rs6
-rw-r--r--src/libsyntax/parse/mod.rs227
-rw-r--r--src/libsyntax/parse/parser.rs360
-rw-r--r--src/libsyntax/print/pprust.rs61
-rw-r--r--src/libsyntax/visit.rs6
-rw-r--r--src/test/run-pass/mid-path-type-params.rs16
25 files changed, 692 insertions, 388 deletions
diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs
index 2a61ea28e0c..429a1c35b34 100644
--- a/src/librustc/front/std_inject.rs
+++ b/src/librustc/front/std_inject.rs
@@ -17,6 +17,7 @@ use syntax::attr;
 use syntax::codemap::dummy_sp;
 use syntax::codemap;
 use syntax::fold;
+use syntax::opt_vec;
 
 static STD_VERSION: &'static str = "0.8-pre";
 
@@ -90,12 +91,18 @@ fn inject_libstd_ref(sess: Session, crate: &ast::Crate) -> @ast::Crate {
             let prelude_path = ast::Path {
                 span: dummy_sp(),
                 global: false,
-                idents: ~[
-                    sess.ident_of("std"),
-                    sess.ident_of("prelude")
+                segments: ~[
+                    ast::PathSegment {
+                        identifier: sess.ident_of("std"),
+                        lifetime: None,
+                        types: opt_vec::Empty,
+                    },
+                    ast::PathSegment {
+                        identifier: sess.ident_of("prelude"),
+                        lifetime: None,
+                        types: opt_vec::Empty,
+                    },
                 ],
-                rp: None,
-                types: ~[]
             };
 
             let vp = @spanned(ast::view_path_glob(prelude_path, n2));
diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs
index 0aacd4c5063..e70e58342eb 100644
--- a/src/librustc/front/test.rs
+++ b/src/librustc/front/test.rs
@@ -16,14 +16,15 @@ use front::config;
 
 use std::vec;
 use syntax::ast_util::*;
+use syntax::attr::AttrMetaMethods;
 use syntax::attr;
 use syntax::codemap::{dummy_sp, span, ExpnInfo, NameAndSpan};
 use syntax::codemap;
 use syntax::ext::base::ExtCtxt;
 use syntax::fold;
+use syntax::opt_vec;
 use syntax::print::pprust;
 use syntax::{ast, ast_util};
-use syntax::attr::AttrMetaMethods;
 
 type node_id_gen = @fn() -> ast::NodeId;
 
@@ -383,19 +384,27 @@ fn nospan<T>(t: T) -> codemap::spanned<T> {
 }
 
 fn path_node(ids: ~[ast::ident]) -> ast::Path {
-    ast::Path { span: dummy_sp(),
-                global: false,
-                idents: ids,
-                rp: None,
-                types: ~[] }
+    ast::Path {
+        span: dummy_sp(),
+        global: false,
+        segments: ids.consume_iter().transform(|identifier| ast::PathSegment {
+            identifier: identifier,
+            lifetime: None,
+            types: opt_vec::Empty,
+        }).collect()
+    }
 }
 
 fn path_node_global(ids: ~[ast::ident]) -> ast::Path {
-    ast::Path { span: dummy_sp(),
-                 global: true,
-                 idents: ids,
-                 rp: None,
-                 types: ~[] }
+    ast::Path {
+        span: dummy_sp(),
+        global: true,
+        segments: ids.consume_iter().transform(|identifier| ast::PathSegment {
+            identifier: identifier,
+            lifetime: None,
+            types: opt_vec::Empty,
+        }).collect()
+    }
 }
 
 #[cfg(stage0)]
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 6a2d1901aef..1b19384af33 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -988,7 +988,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_name(ecx, ebml_w, item.ident);
         encode_attributes(ebml_w, item.attrs);
         match ty.node {
-            ast::ty_path(ref path, ref bounds, _) if path.idents.len() == 1 => {
+            ast::ty_path(ref path, ref bounds, _) if path.segments
+                                                         .len() == 1 => {
                 assert!(bounds.is_none());
                 encode_impl_type_basename(ecx, ebml_w,
                                           ast_util::path_to_ident(path));
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index a03266649ab..3606ecd8d24 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -138,12 +138,20 @@ fn parse_path(st: &mut PState) -> @ast::Path {
           ':' => { next(st); next(st); }
           c => {
             if c == '(' {
-                return @ast::Path { span: dummy_sp(),
-                                    global: false,
-                                    idents: idents,
-                                    rp: None,
-                                    types: ~[] };
-            } else { idents.push(parse_ident_(st, is_last)); }
+                return @ast::Path {
+                    span: dummy_sp(),
+                    global: false,
+                    segments: idents.consume_iter().transform(|identifier| {
+                        ast::PathSegment {
+                            identifier: identifier,
+                            lifetime: None,
+                            types: opt_vec::Empty,
+                        }
+                    }).collect()
+                };
+            } else {
+                idents.push(parse_ident_(st, is_last));
+            }
           }
         }
     };
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 0530ffd30b4..fc779f73060 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -141,7 +141,7 @@ pub fn check_expr(v: &mut CheckCrateVisitor,
             // to handle on-demand instantiation of functions via
             // foo::<bar> in a const. Currently that is only done on
             // a path in trans::callee that only works in block contexts.
-            if pth.types.len() != 0 {
+            if !pth.segments.iter().all(|segment| segment.types.is_empty()) {
                 sess.span_err(
                     e.span, "paths in constants may only refer to \
                              items without type parameters");
diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs
index 222bef641d2..ec6bb17fb24 100644
--- a/src/librustc/middle/privacy.rs
+++ b/src/librustc/middle/privacy.rs
@@ -251,7 +251,9 @@ impl PrivacyVisitor {
         match def {
             def_static_method(method_id, _, _) => {
                 debug!("found static method def, checking it");
-                self.check_method_common(span, method_id, path.idents.last())
+                self.check_method_common(span,
+                                         method_id,
+                                         &path.segments.last().identifier)
             }
             def_fn(def_id, _) => {
                 if def_id.crate == LOCAL_CRATE {
@@ -259,13 +261,19 @@ impl PrivacyVisitor {
                             !self.privileged_items.iter().any(|x| x == &def_id.node) {
                         self.tcx.sess.span_err(span,
                                           fmt!("function `%s` is private",
-                                               token::ident_to_str(path.idents.last())));
+                                               token::ident_to_str(
+                                                &path.segments
+                                                     .last()
+                                                     .identifier)));
                     }
                 } else if csearch::get_item_visibility(self.tcx.sess.cstore,
                                                        def_id) != public {
                     self.tcx.sess.span_err(span,
                                       fmt!("function `%s` is private",
-                                           token::ident_to_str(path.idents.last())));
+                                           token::ident_to_str(
+                                                &path.segments
+                                                     .last()
+                                                     .identifier)));
                 }
             }
             _ => {}
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index ddd22a0add4..d6b6a948a57 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -827,7 +827,7 @@ fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor,
           Some(&ast::def_trait(did)) |
           Some(&ast::def_struct(did)) => {
             if did.crate == ast::LOCAL_CRATE {
-                if cx.region_is_relevant(&path.rp) {
+                if cx.region_is_relevant(&path.segments.last().lifetime) {
                     cx.add_dep(did.node);
                 }
             } else {
@@ -837,7 +837,7 @@ fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor,
                   Some(variance) => {
                     debug!("reference to external, rp'd type %s",
                            pprust::ty_to_str(ty, sess.intr()));
-                    if cx.region_is_relevant(&path.rp) {
+                    if cx.region_is_relevant(&path.segments.last().lifetime) {
                         let rv = cx.add_variance(variance);
                         cx.add_rp(cx.item_id, rv)
                     }
@@ -860,7 +860,7 @@ fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor,
       ast::ty_path(ref path, _, _) => {
         // type parameters are---for now, anyway---always invariant
         do cx.with_ambient_variance(rv_invariant) {
-            for tp in path.types.iter() {
+            for tp in path.segments.iter().flat_map(|s| s.types.iter()) {
                 visitor.visit_ty(tp, cx);
             }
         }
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index d60d1e2b845..6ae2ac8cffc 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -1277,7 +1277,7 @@ impl Resolver {
                     &Ty {
                         node: ty_path(ref path, _, _),
                         _
-                    } if path.idents.len() == 1 => {
+                    } if path.segments.len() == 1 => {
                         let name = path_to_ident(path);
 
                         let new_parent = match parent.children.find(&name) {
@@ -1476,20 +1476,22 @@ impl Resolver {
                     let mut module_path = ~[];
                     match view_path.node {
                         view_path_simple(_, ref full_path, _) => {
-                            let path_len = full_path.idents.len();
+                            let path_len = full_path.segments.len();
                             assert!(path_len != 0);
 
-                            for (i, ident) in full_path.idents.iter().enumerate() {
+                            for (i, segment) in full_path.segments
+                                                         .iter()
+                                                         .enumerate() {
                                 if i != path_len - 1 {
-                                    module_path.push(*ident);
+                                    module_path.push(segment.identifier)
                                 }
                             }
                         }
 
                         view_path_glob(ref module_ident_path, _) |
                         view_path_list(ref module_ident_path, _, _) => {
-                            for ident in module_ident_path.idents.iter() {
-                                module_path.push(*ident);
+                            for segment in module_ident_path.segments.iter() {
+                                module_path.push(segment.identifier)
                             }
                         }
                     }
@@ -1498,7 +1500,8 @@ impl Resolver {
                     let module_ = self.get_module_from_parent(parent);
                     match view_path.node {
                         view_path_simple(binding, ref full_path, id) => {
-                            let source_ident = *full_path.idents.last();
+                            let source_ident =
+                                full_path.segments.last().identifier;
                             let subclass = @SingleImport(binding,
                                                          source_ident);
                             self.build_import_directive(privacy,
@@ -2109,6 +2112,14 @@ impl Resolver {
         return result;
     }
 
+    fn path_idents_to_str(@mut self, path: &Path) -> ~str {
+        let identifiers: ~[ast::ident] = path.segments
+                                             .iter()
+                                             .transform(|seg| seg.identifier)
+                                             .collect();
+        self.idents_to_str(identifiers)
+    }
+
     pub fn import_directive_subclass_to_str(@mut self,
                                             subclass: ImportDirectiveSubclass)
                                             -> @str {
@@ -3841,8 +3852,7 @@ impl Resolver {
                                    reference_type: TraitReferenceType) {
         match self.resolve_path(id, &trait_reference.path, TypeNS, true, visitor) {
             None => {
-                let path_str = self.idents_to_str(trait_reference.path.idents);
-
+                let path_str = self.path_idents_to_str(&trait_reference.path);
                 let usage_str = match reference_type {
                     TraitBoundingTypeParameter => "bound type parameter with",
                     TraitImplementation        => "implement",
@@ -4141,8 +4151,8 @@ impl Resolver {
                 let mut result_def = None;
 
                 // First, check to see whether the name is a primitive type.
-                if path.idents.len() == 1 {
-                    let name = *path.idents.last();
+                if path.segments.len() == 1 {
+                    let name = path.segments.last().identifier;
 
                     match self.primitive_type_table
                             .primitive_types
@@ -4165,7 +4175,7 @@ impl Resolver {
                                 debug!("(resolving type) resolved `%s` to \
                                         type %?",
                                        self.session.str_of(
-                                            *path.idents.last()),
+                                            path.segments.last().identifier),
                                        def);
                                 result_def = Some(def);
                             }
@@ -4184,14 +4194,15 @@ impl Resolver {
                         // Write the result into the def map.
                         debug!("(resolving type) writing resolution for `%s` \
                                 (id %d)",
-                               self.idents_to_str(path.idents),
+                               self.path_idents_to_str(path),
                                path_id);
                         self.record_def(path_id, def);
                     }
                     None => {
                         self.resolve_error
-                            (ty.span, fmt!("use of undeclared type name `%s`",
-                                           self.idents_to_str(path.idents)));
+                            (ty.span,
+                             fmt!("use of undeclared type name `%s`",
+                                  self.path_idents_to_str(path)))
                     }
                 }
 
@@ -4230,7 +4241,7 @@ impl Resolver {
         do walk_pat(pattern) |pattern| {
             match pattern.node {
                 pat_ident(binding_mode, ref path, _)
-                        if !path.global && path.idents.len() == 1 => {
+                        if !path.global && path.segments.len() == 1 => {
 
                     // The meaning of pat_ident with no type parameters
                     // depends on whether an enum variant or unit-like struct
@@ -4241,7 +4252,7 @@ impl Resolver {
                     // such a value is simply disallowed (since it's rarely
                     // what you want).
 
-                    let ident = path.idents[0];
+                    let ident = path.segments[0].identifier;
 
                     match self.resolve_bare_identifier_pattern(ident) {
                         FoundStructOrEnumVariant(def)
@@ -4351,7 +4362,9 @@ impl Resolver {
                     }
 
                     // Check the types in the path pattern.
-                    for ty in path.types.iter() {
+                    for ty in path.segments
+                                  .iter()
+                                  .flat_map_(|seg| seg.types.iter()) {
                         self.resolve_type(ty, visitor);
                     }
                 }
@@ -4375,7 +4388,7 @@ impl Resolver {
                                 path.span,
                                 fmt!("`%s` is not an enum variant or constant",
                                      self.session.str_of(
-                                         *path.idents.last())));
+                                         path.segments.last().identifier)))
                         }
                         None => {
                             self.resolve_error(path.span,
@@ -4384,7 +4397,9 @@ impl Resolver {
                     }
 
                     // Check the types in the path pattern.
-                    for ty in path.types.iter() {
+                    for ty in path.segments
+                                  .iter()
+                                  .flat_map_(|s| s.types.iter()) {
                         self.resolve_type(ty, visitor);
                     }
                 }
@@ -4402,8 +4417,10 @@ impl Resolver {
                             self.resolve_error(
                                 path.span,
                                 fmt!("`%s` is not an enum variant, struct or const",
-                                     self.session.str_of(
-                                         *path.idents.last())));
+                                     self.session
+                                         .str_of(path.segments
+                                                     .last()
+                                                     .identifier)));
                         }
                         None => {
                             self.resolve_error(path.span,
@@ -4413,7 +4430,9 @@ impl Resolver {
                     }
 
                     // Check the types in the path pattern.
-                    for ty in path.types.iter() {
+                    for ty in path.segments
+                                  .iter()
+                                  .flat_map_(|s| s.types.iter()) {
                         self.resolve_type(ty, visitor);
                     }
                 }
@@ -4448,7 +4467,7 @@ impl Resolver {
                             self.resolve_error(
                                 path.span,
                                 fmt!("`%s` does not name a structure",
-                                     self.idents_to_str(path.idents)));
+                                     self.path_idents_to_str(path)));
                         }
                     }
                 }
@@ -4510,7 +4529,7 @@ impl Resolver {
                         visitor: &mut ResolveVisitor)
                         -> Option<def> {
         // First, resolve the types.
-        for ty in path.types.iter() {
+        for ty in path.segments.iter().flat_map_(|s| s.types.iter()) {
             self.resolve_type(ty, visitor);
         }
 
@@ -4520,12 +4539,17 @@ impl Resolver {
                                                     namespace);
         }
 
-        let unqualified_def = self.resolve_identifier(
-            *path.idents.last(), namespace, check_ribs, path.span);
+        let unqualified_def = self.resolve_identifier(path.segments
+                                                          .last()
+                                                          .identifier,
+                                                      namespace,
+                                                      check_ribs,
+                                                      path.span);
 
-        if path.idents.len() > 1 {
-            let def = self.resolve_module_relative_path(
-                path, self.xray_context, namespace);
+        if path.segments.len() > 1 {
+            let def = self.resolve_module_relative_path(path,
+                                                        self.xray_context,
+                                                        namespace);
             match (def, unqualified_def) {
                 (Some(d), Some(ud)) if d == ud => {
                     self.session.add_lint(unnecessary_qualification,
@@ -4640,12 +4664,12 @@ impl Resolver {
 
     pub fn intern_module_part_of_path(@mut self, path: &Path) -> ~[ident] {
         let mut module_path_idents = ~[];
-        for (index, ident) in path.idents.iter().enumerate() {
-            if index == path.idents.len() - 1 {
+        for (index, segment) in path.segments.iter().enumerate() {
+            if index == path.segments.len() - 1 {
                 break;
             }
 
-            module_path_idents.push(*ident);
+            module_path_idents.push(segment.identifier);
         }
 
         return module_path_idents;
@@ -4681,7 +4705,7 @@ impl Resolver {
             }
         }
 
-        let name = *path.idents.last();
+        let name = path.segments.last().identifier;
         let def = match self.resolve_definition_of_name_in_module(containing_module,
                                                         name,
                                                         namespace,
@@ -4749,7 +4773,7 @@ impl Resolver {
             }
         }
 
-        let name = *path.idents.last();
+        let name = path.segments.last().identifier;
         match self.resolve_definition_of_name_in_module(containing_module,
                                                         name,
                                                         namespace,
@@ -4969,7 +4993,7 @@ impl Resolver {
                     Some(def) => {
                         // Write the result into the def map.
                         debug!("(resolving expr) resolved `%s`",
-                               self.idents_to_str(path.idents));
+                               self.path_idents_to_str(path));
 
                         // First-class methods are not supported yet; error
                         // out here.
@@ -4989,8 +5013,7 @@ impl Resolver {
                         self.record_def(expr.id, def);
                     }
                     None => {
-                        let wrong_name = self.idents_to_str(
-                            path.idents);
+                        let wrong_name = self.path_idents_to_str(path);
                         if self.name_exists_in_scope_struct(wrong_name) {
                             self.resolve_error(expr.span,
                                         fmt!("unresolved name `%s`. \
@@ -5066,7 +5089,7 @@ impl Resolver {
                         self.resolve_error(
                             path.span,
                             fmt!("`%s` does not name a structure",
-                                 self.idents_to_str(path.idents)));
+                                 self.path_idents_to_str(path)));
                     }
                 }
 
diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs
index 2ac481cf730..0dfce6f42c3 100644
--- a/src/librustc/middle/trans/consts.rs
+++ b/src/librustc/middle/trans/consts.rs
@@ -559,7 +559,9 @@ fn const_expr_unadjusted(cx: @mut CrateContext, e: &ast::expr) -> ValueRef {
             v
           }
           ast::expr_path(ref pth) => {
-            assert_eq!(pth.types.len(), 0);
+            // Assert that there are no type parameters in this path.
+            assert!(pth.segments.iter().all(|seg| seg.types.is_empty()));
+
             let tcx = cx.tcx;
             match tcx.def_map.find(&e.id) {
                 Some(&ast::def_fn(def_id, _purity)) => {
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index 8c96df9b8f0..6425867425f 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -63,7 +63,6 @@ use middle::typeck::rscope::RegionParamNames;
 use middle::typeck::lookup_def_tcx;
 
 use std::result;
-use std::vec;
 use syntax::abi::AbiSet;
 use syntax::{ast, ast_util};
 use syntax::codemap::span;
@@ -150,7 +149,8 @@ fn ast_path_substs<AC:AstConv,RS:region_scope + Clone + 'static>(
     // If the type is parameterized by the this region, then replace this
     // region with the current anon region binding (in other words,
     // whatever & would get replaced with).
-    let regions = match (&decl_generics.region_param, &path.rp) {
+    let regions = match (&decl_generics.region_param,
+                         &path.segments.last().lifetime) {
         (&None, &None) => {
             opt_vec::Empty
         }
@@ -169,20 +169,34 @@ fn ast_path_substs<AC:AstConv,RS:region_scope + Clone + 'static>(
         }
         (&Some(_), &Some(_)) => {
             opt_vec::with(
-                ast_region_to_region(this, rscope, path.span, &path.rp))
+                ast_region_to_region(this,
+                                     rscope,
+                                     path.span,
+                                     &path.segments.last().lifetime))
         }
     };
 
     // Convert the type parameters supplied by the user.
-    if !vec::same_length(*decl_generics.type_param_defs, path.types) {
+    let supplied_type_parameter_count =
+        path.segments.iter().flat_map_(|s| s.types.iter()).len_();
+    if decl_generics.type_param_defs.len() != supplied_type_parameter_count {
         this.tcx().sess.span_fatal(
             path.span,
             fmt!("wrong number of type arguments: expected %u but found %u",
-                 decl_generics.type_param_defs.len(), path.types.len()));
+                 decl_generics.type_param_defs.len(),
+                 supplied_type_parameter_count));
+    }
+    let tps = path.segments
+                  .iter()
+                  .flat_map_(|s| s.types.iter())
+                  .transform(|a_t| ast_ty_to_ty(this, rscope, a_t))
+                  .collect();
+
+    substs {
+        regions: ty::NonerasedRegions(regions),
+        self_ty: self_ty,
+        tps: tps
     }
-    let tps = path.types.map(|a_t| ast_ty_to_ty(this, rscope, a_t));
-
-    substs {regions:ty::NonerasedRegions(regions), self_ty:self_ty, tps:tps}
 }
 
 pub fn ast_path_to_substs_and_ty<AC:AstConv,
@@ -325,7 +339,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:region_scope + Clone + 'static>(
                        path: &ast::Path,
                        flags: uint) {
         if (flags & NO_TPS) != 0u {
-            if path.types.len() > 0u {
+            if !path.segments.iter().all(|s| s.types.is_empty()) {
                 tcx.sess.span_err(
                     path.span,
                     "type parameters are not allowed on this type");
@@ -333,7 +347,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:region_scope + Clone + 'static>(
         }
 
         if (flags & NO_REGIONS) != 0u {
-            if path.rp.is_some() {
+            if path.segments.last().lifetime.is_some() {
                 tcx.sess.span_err(
                     path.span,
                     "region parameters are not allowed on this type");
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 0c8f2229424..fcd62eb92b5 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -3146,7 +3146,10 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
     debug!(">>> instantiate_path");
 
     let ty_param_count = tpt.generics.type_param_defs.len();
-    let ty_substs_len = pth.types.len();
+    let mut ty_substs_len = 0;
+    for segment in pth.segments.iter() {
+        ty_substs_len += segment.types.len()
+    }
 
     debug!("tpt=%s ty_param_count=%? ty_substs_len=%?",
            tpt.repr(fcx.tcx()),
@@ -3155,7 +3158,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
 
     // determine the region bound, using the value given by the user
     // (if any) and otherwise using a fresh region variable
-    let regions = match pth.rp {
+    let regions = match pth.segments.last().lifetime {
         Some(_) => { // user supplied a lifetime parameter...
             match tpt.generics.region_param {
                 None => { // ...but the type is not lifetime parameterized!
@@ -3165,7 +3168,10 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
                 }
                 Some(_) => { // ...and the type is lifetime parameterized, ok.
                     opt_vec::with(
-                        ast_region_to_region(fcx, fcx, span, &pth.rp))
+                        ast_region_to_region(fcx,
+                                             fcx,
+                                             span,
+                                             &pth.segments.last().lifetime))
                 }
             }
         }
@@ -3204,12 +3210,18 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
         }
         fcx.infcx().next_ty_vars(ty_param_count)
     } else {
-        pth.types.map(|aty| fcx.to_ty(aty))
+        pth.segments
+           .iter()
+           .flat_map_(|s| s.types.iter())
+           .transform(|aty| fcx.to_ty(aty))
+           .collect()
     };
 
-    let substs = substs {regions: ty::NonerasedRegions(regions),
-                         self_ty: None,
-                         tps: tps };
+    let substs = substs {
+        regions: ty::NonerasedRegions(regions),
+        self_ty: None,
+        tps: tps
+    };
     fcx.write_ty_substs(node_id, tpt.ty, substs);
 
     debug!("<<<");
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 8ae9453cf11..0a13f1a8a5e 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -109,12 +109,21 @@ pub struct Path {
     /// A `::foo` path, is relative to the crate root rather than current
     /// module (like paths in an import).
     global: bool,
-    /// The segments in the path (the things separated by ::)
-    idents: ~[ident],
-    /// "Region parameter", currently only one lifetime is allowed in a path.
-    rp: Option<Lifetime>,
-    /// These are the type parameters, ie, the `a, b` in `foo::bar::<a, b>`
-    types: ~[Ty],
+    /// The segments in the path: the things separated by `::`.
+    segments: ~[PathSegment],
+}
+
+/// A segment of a path: an identifier, an optional lifetime, and a set of
+/// types.
+#[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]
+pub struct PathSegment {
+    /// The identifier portion of this path segment.
+    identifier: ident,
+    /// The lifetime parameter for this path segment. Currently only one
+    /// lifetime parameter is allowed.
+    lifetime: Option<Lifetime>,
+    /// The type parameters for this path segment, if present.
+    types: OptVec<Ty>,
 }
 
 pub type CrateNum = int;
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index a39da4301ba..2fe42af65ca 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -28,8 +28,8 @@ pub fn path_name_i(idents: &[ident]) -> ~str {
     idents.map(|i| token::interner_get(i.name)).connect("::")
 }
 
-pub fn path_to_ident(p: &Path) -> ident {
-    *p.idents.last()
+pub fn path_to_ident(path: &Path) -> ident {
+    path.segments.last().identifier
 }
 
 pub fn local_def(id: NodeId) -> def_id {
@@ -217,12 +217,18 @@ pub fn default_block(
     }
 }
 
-pub fn ident_to_path(s: span, i: ident) -> Path {
-    ast::Path { span: s,
-                 global: false,
-                 idents: ~[i],
-                 rp: None,
-                 types: ~[] }
+pub fn ident_to_path(s: span, identifier: ident) -> Path {
+    ast::Path {
+        span: s,
+        global: false,
+        segments: ~[
+            ast::PathSegment {
+                identifier: identifier,
+                lifetime: None,
+                types: opt_vec::Empty,
+            }
+        ],
+    }
 }
 
 pub fn ident_to_pat(id: NodeId, s: span, i: ident) -> @pat {
@@ -420,7 +426,7 @@ impl IdVisitor {
 impl Visitor<()> for IdVisitor {
     fn visit_mod(&mut self,
                  module: &_mod,
-                 _span: span,
+                 _: span,
                  node_id: NodeId,
                  env: ()) {
         (self.visit_callback)(node_id);
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 6f9585652bd..7432cf80a41 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -329,20 +329,6 @@ pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: &str) -> @str {
     }
 }
 
-pub fn expr_to_ident(cx: @ExtCtxt,
-                     expr: @ast::expr,
-                     err_msg: &str) -> ast::ident {
-    match expr.node {
-      ast::expr_path(ref p) => {
-        if p.types.len() > 0u || p.idents.len() != 1u {
-            cx.span_fatal(expr.span, err_msg);
-        }
-        return p.idents[0];
-      }
-      _ => cx.span_fatal(expr.span, err_msg)
-    }
-}
-
 pub fn check_zero_tts(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
                       name: &str) {
     if tts.len() != 0 {
@@ -353,15 +339,15 @@ pub fn check_zero_tts(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
 pub fn get_single_str_from_tts(cx: @ExtCtxt,
                                sp: span,
                                tts: &[ast::token_tree],
-                               name: &str) -> @str {
+                               name: &str)
+                               -> @str {
     if tts.len() != 1 {
         cx.span_fatal(sp, fmt!("%s takes 1 argument.", name));
     }
 
     match tts[0] {
         ast::tt_tok(_, token::LIT_STR(ident)) => cx.str_of(ident),
-        _ =>
-        cx.span_fatal(sp, fmt!("%s requires a string.", name))
+        _ => cx.span_fatal(sp, fmt!("%s requires a string.", name)),
     }
 }
 
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 9f179672422..a62a1d121e2 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -233,18 +233,31 @@ impl AstBuilder for @ExtCtxt {
     fn path_global(&self, span: span, strs: ~[ast::ident]) -> ast::Path {
         self.path_all(span, true, strs, None, ~[])
     }
-    fn path_all(&self, sp: span,
+    fn path_all(&self,
+                sp: span,
                 global: bool,
-                idents: ~[ast::ident],
+                mut idents: ~[ast::ident],
                 rp: Option<ast::Lifetime>,
                 types: ~[ast::Ty])
-        -> ast::Path {
+                -> ast::Path {
+        let last_identifier = idents.pop();
+        let mut segments: ~[ast::PathSegment] = idents.consume_iter()
+                                                      .transform(|ident| {
+            ast::PathSegment {
+                identifier: ident,
+                lifetime: None,
+                types: opt_vec::Empty,
+            }
+        }).collect();
+        segments.push(ast::PathSegment {
+            identifier: last_identifier,
+            lifetime: rp,
+            types: opt_vec::from(types),
+        });
         ast::Path {
             span: sp,
             global: global,
-            idents: idents,
-            rp: rp,
-            types: types
+            segments: segments,
         }
     }
 
diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs
index edb5c634d56..477f3fde99c 100644
--- a/src/libsyntax/ext/concat_idents.rs
+++ b/src/libsyntax/ext/concat_idents.rs
@@ -12,6 +12,7 @@ use ast;
 use codemap::span;
 use ext::base::*;
 use ext::base;
+use opt_vec;
 use parse::token;
 use parse::token::{str_to_ident};
 
@@ -39,9 +40,13 @@ pub fn expand_syntax_ext(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree])
             ast::Path {
                  span: sp,
                  global: false,
-                 idents: ~[res],
-                 rp: None,
-                 types: ~[],
+                 segments: ~[
+                    ast::PathSegment {
+                        identifier: res,
+                        lifetime: None,
+                        types: opt_vec::Empty,
+                    }
+                ]
             }
         ),
         span: sp,
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 7b493e11ef7..86639c6f121 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -19,6 +19,7 @@ use codemap;
 use codemap::{span, spanned, ExpnInfo, NameAndSpan};
 use ext::base::*;
 use fold::*;
+use opt_vec;
 use parse;
 use parse::{parse_item_from_source_str};
 use parse::token;
@@ -42,13 +43,13 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
             match (*mac).node {
                 // Token-tree macros:
                 mac_invoc_tt(ref pth, ref tts) => {
-                    if (pth.idents.len() > 1u) {
+                    if (pth.segments.len() > 1u) {
                         cx.span_fatal(
                             pth.span,
                             fmt!("expected macro name without module \
                                   separators"));
                     }
-                    let extname = &pth.idents[0];
+                    let extname = &pth.segments[0].identifier;
                     let extnamestr = ident_to_str(extname);
                     // leaving explicit deref here to highlight unbox op:
                     match (*extsbox).find(&extname.name) {
@@ -143,9 +144,13 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
                 ast::Path {
                     span: span,
                     global: false,
-                    idents: ~[ident],
-                    rp: None,
-                    types: ~[]
+                    segments: ~[
+                        ast::PathSegment {
+                            identifier: ident,
+                            lifetime: None,
+                            types: opt_vec::Empty,
+                        }
+                    ],
                 }
             }
 
@@ -368,7 +373,7 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
         _ => cx.span_bug(it.span, "invalid item macro invocation")
     };
 
-    let extname = &pth.idents[0];
+    let extname = &pth.segments[0].identifier;
     let extnamestr = ident_to_str(extname);
     let expanded = match (*extsbox).find(&extname.name) {
         None => cx.span_fatal(pth.span,
@@ -459,13 +464,13 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
         }
         _ => return orig(s, sp, fld)
     };
-    if (pth.idents.len() > 1u) {
+    if (pth.segments.len() > 1u) {
         cx.span_fatal(
             pth.span,
             fmt!("expected macro name without module \
                   separators"));
     }
-    let extname = &pth.idents[0];
+    let extname = &pth.segments[0].identifier;
     let extnamestr = ident_to_str(extname);
     let (fully_expanded, sp) = match (*extsbox).find(&extname.name) {
         None =>
@@ -534,10 +539,14 @@ impl Visitor<()> for NewNameFinderContext {
                     // a path of length one:
                     &ast::Path {
                         global: false,
-                        idents: [id],
                         span: _,
-                        rp: _,
-                        types: _
+                        segments: [
+                            ast::PathSegment {
+                                identifier: id,
+                                lifetime: _,
+                                types: _
+                            }
+                        ]
                     } => self.ident_accumulator.push(id),
                     // I believe these must be enums...
                     _ => ()
diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index c208a7f7e3e..327ee331c38 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -16,8 +16,8 @@ use codemap::{BytePos, mk_sp};
 use codemap;
 use parse::lexer::*; //resolve bug?
 use parse::ParseSess;
-use parse::parser::Parser;
 use parse::attr::parser_attr;
+use parse::parser::{LifetimeAndTypesWithoutColons, Parser};
 use parse::token::{Token, EOF, to_str, nonterminal, get_ident_interner, ident_to_str};
 use parse::token;
 
@@ -430,7 +430,9 @@ pub fn parse_nt(p: &Parser, name: &str) -> nonterminal {
         _ => p.fatal(~"expected ident, found "
                      + token::to_str(get_ident_interner(), p.token))
       },
-      "path" => token::nt_path(~p.parse_path_with_tps(false)),
+      "path" => {
+        token::nt_path(~p.parse_path(LifetimeAndTypesWithoutColons).path)
+      }
       "attr" => token::nt_attr(@p.parse_attribute(false)),
       "tt" => {
         *p.quote_depth += 1u; //but in theory, non-quoted tts might be useful
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 65694f013f7..458737e2fbf 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -765,9 +765,11 @@ fn noop_fold_path(p: &Path, fld: @ast_fold) -> Path {
     ast::Path {
         span: fld.new_span(p.span),
         global: p.global,
-        idents: p.idents.map(|x| fld.fold_ident(*x)),
-        rp: p.rp,
-        types: p.types.map(|x| fld.fold_ty(x)),
+        segments: p.segments.map(|segment| ast::PathSegment {
+            identifier: fld.fold_ident(segment.identifier),
+            lifetime: segment.lifetime,
+            types: segment.types.map(|typ| fld.fold_ty(typ)),
+        })
     }
 }
 
diff --git a/src/libsyntax/oldvisit.rs b/src/libsyntax/oldvisit.rs
index 295003c6ef5..56576ee3599 100644
--- a/src/libsyntax/oldvisit.rs
+++ b/src/libsyntax/oldvisit.rs
@@ -284,7 +284,11 @@ pub fn visit_ty<E:Clone>(t: &Ty, (e, v): (E, vt<E>)) {
 }
 
 pub fn visit_path<E:Clone>(p: &Path, (e, v): (E, vt<E>)) {
-    for tp in p.types.iter() { (v.visit_ty)(tp, (e.clone(), v)); }
+    for segment in p.segments.iter() {
+        for typ in segment.types.iter() {
+            (v.visit_ty)(typ, (e.clone(), v))
+        }
+    }
 }
 
 pub fn visit_pat<E:Clone>(p: &pat, (e, v): (E, vt<E>)) {
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 23c6a8b9720..73e17f551c9 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -361,27 +361,47 @@ mod test {
         span{lo:BytePos(a),hi:BytePos(b),expn_info:None}
     }
 
-    #[test] fn path_exprs_1 () {
+    #[test] fn path_exprs_1() {
         assert_eq!(string_to_expr(@"a"),
-                   @ast::expr{id:1,
-                              node:ast::expr_path(ast::Path {span:sp(0,1),
-                                                              global:false,
-                                                              idents:~[str_to_ident("a")],
-                                                              rp:None,
-                                                              types:~[]}),
-                              span:sp(0,1)})
+                   @ast::expr{
+                    id: 1,
+                    node: ast::expr_path(ast::Path {
+                        span: sp(0, 1),
+                        global: false,
+                        segments: ~[
+                            ast::PathSegment {
+                                identifier: str_to_ident("a"),
+                                lifetime: None,
+                                types: ~[],
+                            }
+                        ],
+                    }),
+                    span: sp(0, 1)
+                   })
     }
 
     #[test] fn path_exprs_2 () {
         assert_eq!(string_to_expr(@"::a::b"),
-                   @ast::expr{id:1,
-                               node:ast::expr_path(
-                                    ast::Path {span:sp(0,6),
-                                               global:true,
-                                               idents:strs_to_idents(~["a","b"]),
-                                               rp:None,
-                                               types:~[]}),
-                              span:sp(0,6)})
+                   @ast::expr {
+                    id:1,
+                    node: ast::expr_path(ast::Path {
+                            span: sp(0, 6),
+                            global: true,
+                            segments: ~[
+                                ast::PathSegment {
+                                    identifier: str_to_ident("a"),
+                                    lifetime: None,
+                                    types: ~[],
+                                },
+                                ast::PathSegment {
+                                    identifier: str_to_ident("b"),
+                                    lifetime: None,
+                                    types: ~[],
+                                }
+                            ]
+                        },
+                        span: sp(0, 6))
+                   })
     }
 
     #[should_fail]
@@ -420,32 +440,43 @@ mod test {
 
     #[test] fn ret_expr() {
         assert_eq!(string_to_expr(@"return d"),
-                   @ast::expr{id:2,
-                              node:ast::expr_ret(
-                                  Some(@ast::expr{id:1,
-                                                  node:ast::expr_path(
-                                                       ast::Path{span:sp(7,8),
-                                                                 global:false,
-                                                                 idents:~[str_to_ident("d")],
-                                                                 rp:None,
-                                                                 types:~[]
-                                                                }),
-                                                  span:sp(7,8)})),
-                              span:sp(0,8)})
+                   @ast::expr{
+                    id:2,
+                    node:ast::expr_ret(Some(@ast::expr{
+                        id:1,
+                        node:ast::expr_path(ast::Path{
+                            span: sp(7, 8),
+                            global: false,
+                            segments: ~[
+                                ast::PathSegment {
+                                    identifier: str_to_ident("d"),
+                                    lifetime: None,
+                                    types: opt_vec::Empty,
+                                }
+                            ],
+                        }),
+                        span:sp(7,8)
+                    })),
+                    span:sp(0,8)
+                   })
     }
 
     #[test] fn parse_stmt_1 () {
         assert_eq!(string_to_stmt(@"b;"),
                    @spanned{
-                       node: ast::stmt_expr(@ast::expr{
+                       node: ast::stmt_expr(@ast::expr {
                            id: 1,
-                           node: ast::expr_path(
-                                ast::Path{
-                                   span:sp(0,1),
-                                   global:false,
-                                   idents:~[str_to_ident("b")],
-                                   rp:None,
-                                   types: ~[]}),
+                           node: ast::expr_path(ast::Path {
+                               span:sp(0,1),
+                               global:false,
+                               segments: ~[
+                                ast::PathSegment {
+                                    identifier: str_to_ident("b"),
+                                    lifetime: None,
+                                    types: opt_vec::Empty,
+                                }
+                               ],
+                            }),
                            span: sp(0,1)},
                                             2), // fixme
                        span: sp(0,1)})
@@ -460,15 +491,20 @@ mod test {
         let parser = string_to_parser(@"b");
         assert_eq!(parser.parse_pat(),
                    @ast::pat{id:1, // fixme
-                             node: ast::pat_ident(ast::bind_infer,
-                                                   ast::Path{
-                                                      span:sp(0,1),
-                                                      global:false,
-                                                      idents:~[str_to_ident("b")],
-                                                      rp: None,
-                                                      types: ~[]},
-                                                  None // no idea
-                                                 ),
+                             node: ast::pat_ident(
+                                ast::bind_infer,
+                                ast::Path {
+                                    span:sp(0,1),
+                                    global:false,
+                                    segments: ~[
+                                        ast::PathSegment {
+                                            identifier: str_to_ident("b"),
+                                            lifetime: None,
+                                            types: opt_vec::Empty,
+                                        }
+                                    ],
+                                },
+                                None /* no idea */),
                              span: sp(0,1)});
         parser_done(parser);
     }
@@ -483,21 +519,33 @@ mod test {
                                         span:sp(4,4), // this is bizarre...
                                         // check this in the original parser?
                                         global:false,
-                                        idents:~[str_to_ident("int")],
-                                        rp: None,
-                                        types: ~[]},
-                                                       None, 2),
+                                        segments: ~[
+                                            ast::PathSegment {
+                                                identifier:
+                                                    str_to_ident("int"),
+                                                lifetime: None,
+                                                types: opt_vec::Empty,
+                                            }
+                                        ],
+                                    }, None, 2),
                                     span:sp(4,7)},
                        pat: @ast::pat{id:1,
-                                      node: ast::pat_ident(ast::bind_infer,
-                                                            ast::Path{
-                                                               span:sp(0,1),
-                                                               global:false,
-                                                               idents:~[str_to_ident("b")],
-                                                               rp: None,
-                                                               types: ~[]},
-                                                           None // no idea
-                                                          ),
+                                      node: ast::pat_ident(
+                                        ast::bind_infer,
+                                        ast::Path {
+                                            span:sp(0,1),
+                                            global:false,
+                                            segments: ~[
+                                                ast::PathSegment {
+                                                    identifier:
+                                                        str_to_ident("b"),
+                                                    lifetime: None,
+                                                    types: opt_vec::Empty,
+                                                }
+                                            ],
+                                        },
+                                        None // no idea
+                                      ),
                                       span: sp(0,1)},
                        id: 4 // fixme
                    })
@@ -519,23 +567,37 @@ mod test {
                                                 node: ast::ty_path(ast::Path{
                                         span:sp(10,13),
                                         global:false,
-                                        idents:~[str_to_ident("int")],
-                                        rp: None,
-                                        types: ~[]},
-                                                       None, 2),
-                                                span:sp(10,13)},
-                                    pat: @ast::pat{id:1, // fixme
-                                                   node: ast::pat_ident(
-                                                       ast::bind_infer,
-                                                       ast::Path{
-                                                           span:sp(6,7),
-                                                           global:false,
-                                                           idents:~[str_to_ident("b")],
-                                                           rp: None,
-                                                           types: ~[]},
-                                                       None // no idea
-                                                   ),
-                                                  span: sp(6,7)},
+                                        segments: ~[
+                                            ast::PathSegment {
+                                                identifier:
+                                                    str_to_ident("int"),
+                                                lifetime: None,
+                                                types: opt_vec::Empty,
+                                            }
+                                        ],
+                                        }, None, 2),
+                                        span:sp(10,13)
+                                    },
+                                    pat: @ast::pat {
+                                        id:1, // fixme
+                                        node: ast::pat_ident(
+                                            ast::bind_infer,
+                                            ast::Path {
+                                                span:sp(6,7),
+                                                global:false,
+                                                segments: ~[
+                                                    ast::PathSegment {
+                                                        identifier:
+                                                            str_to_ident("b"),
+                                                        lifetime: None,
+                                                        types: opt_vec::Empty,
+                                                    }
+                                                ],
+                                            },
+                                            None // no idea
+                                        ),
+                                        span: sp(6,7)
+                                    },
                                     id: 4 // fixme
                                 }],
                                 output: ast::Ty{id:5, // fixme
@@ -558,9 +620,18 @@ mod test {
                                                       ast::Path{
                                                         span:sp(17,18),
                                                         global:false,
-                                                        idents:~[str_to_ident("b")],
-                                                        rp:None,
-                                                        types: ~[]}),
+                                                        segments: ~[
+                                                            ast::PathSegment {
+                                                                identifier:
+                                                                str_to_ident(
+                                                                    "b"),
+                                                                lifetime:
+                                                                    None,
+                                                                types:
+                                                                opt_vec::Empty
+                                                            }
+                                                        ],
+                                                      }),
                                                 span: sp(17,18)},
                                                                  7), // fixme
                                             span: sp(17,18)}],
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index fe5e98d6036..0882a5e3143 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -97,6 +97,37 @@ enum restriction {
 type arg_or_capture_item = Either<arg, ()>;
 type item_info = (ident, item_, Option<~[Attribute]>);
 
+/// How to parse a path. There are four different kinds of paths, all of which
+/// are parsed somewhat differently.
+#[deriving(Eq)]
+pub enum PathParsingMode {
+    /// A path with no type parameters; e.g. `foo::bar::Baz`
+    NoTypesAllowed,
+    /// A path with a lifetime and type parameters, with no double colons
+    /// before the type parameters; e.g. `foo::bar<'self>::Baz<T>`
+    LifetimeAndTypesWithoutColons,
+    /// A path with a lifetime and type parameters with double colons before
+    /// the type parameters; e.g. `foo::bar::<'self>::Baz::<T>`
+    LifetimeAndTypesWithColons,
+    /// A path with a lifetime and type parameters with bounds before the last
+    /// set of type parameters only; e.g. `foo::bar<'self>::Baz:X+Y<T>` This
+    /// form does not use extra double colons.
+    LifetimeAndTypesAndBounds,
+}
+
+/// A pair of a path segment and group of type parameter bounds. (See `ast.rs`
+/// for the definition of a path segment.)
+struct PathSegmentAndBoundSet {
+    segment: ast::PathSegment,
+    bound_set: Option<OptVec<TyParamBound>>,
+}
+
+/// A path paired with optional type bounds.
+struct PathAndBounds {
+    path: ast::Path,
+    bounds: Option<OptVec<TyParamBound>>,
+}
+
 pub enum item_or_view_item {
     // Indicates a failure to parse any kind of item. The attributes are
     // returned.
@@ -1108,7 +1139,10 @@ impl Parser {
         } else if *self.token == token::MOD_SEP
             || is_ident_or_path(self.token) {
             // NAMED TYPE
-            let (path, bounds) = self.parse_type_path();
+            let PathAndBounds {
+                path,
+                bounds
+            } = self.parse_path(LifetimeAndTypesAndBounds);
             ty_path(path, bounds, self.get_id())
         } else {
             self.fatal(fmt!("expected type, found token %?",
@@ -1329,139 +1363,155 @@ impl Parser {
         }
     }
 
-    // parse a path into a vector of idents, whether the path starts
-    // with ::, and a span.
-    pub fn parse_path(&self) -> (~[ast::ident],bool,span) {
+    /// Parses a path and optional type parameter bounds, depending on the
+    /// mode. The `mode` parameter determines whether lifetimes, types, and/or
+    /// bounds are permitted and whether `::` must precede type parameter
+    /// groups.
+    pub fn parse_path(&self, mode: PathParsingMode) -> PathAndBounds {
+        // Check for a whole path...
+        let found = match *self.token {
+            INTERPOLATED(token::nt_path(_)) => Some(self.bump_and_get()),
+            _ => None,
+        };
+        match found {
+            Some(INTERPOLATED(token::nt_path(path))) => {
+                return PathAndBounds {
+                    path: path,
+                    bounds: None,
+                }
+            }
+            _ => {}
+        }
+
         let lo = self.span.lo;
         let is_global = self.eat(&token::MOD_SEP);
-        let (ids,span{lo:_,hi,expn_info}) = self.parse_path_non_global();
-        (ids,is_global,span{lo:lo,hi:hi,expn_info:expn_info})
-    }
 
-    // parse a path beginning with an identifier into a vector of idents and a span
-    pub fn parse_path_non_global(&self) -> (~[ast::ident],span) {
-        let lo = self.span.lo;
-        let mut ids = ~[];
-        // must be at least one to begin:
-        ids.push(self.parse_ident());
+        // Parse any number of segments and bound sets. A segment is an
+        // identifier followed by an optional lifetime and a set of types.
+        // A bound set is a set of type parameter bounds.
+        let mut segments = ~[];
         loop {
+            // First, parse an identifier.
             match *self.token {
-                token::MOD_SEP => {
-                    let is_ident = do self.look_ahead(1) |t| {
-                        match *t {
-                            token::IDENT(*) => true,
-                            _ => false,
-                        }
-                    };
-                    if is_ident {
-                        self.bump();
-                        ids.push(self.parse_ident());
-                    } else {
-                        break
-                    }
-                }
-                _ => break
+                token::IDENT(*) => {}
+                _ => break,
             }
-        }
-        (ids, mk_sp(lo, self.last_span.hi))
-    }
+            let identifier = self.parse_ident();
 
-    // parse a path that doesn't have type parameters attached
-    pub fn parse_path_without_tps(&self) -> ast::Path {
-        maybe_whole!(deref self, nt_path);
-        let (ids,is_global,sp) = self.parse_path();
-        ast::Path { span: sp,
-                     global: is_global,
-                     idents: ids,
-                     rp: None,
-                     types: ~[] }
-    }
+            // Next, parse a colon and bounded type parameters, if applicable.
+            let bound_set = if mode == LifetimeAndTypesAndBounds {
+                self.parse_optional_ty_param_bounds()
+            } else {
+                None
+            };
 
-    pub fn parse_bounded_path_with_tps(&self, colons: bool,
-                                        before_tps: Option<&fn()>) -> ast::Path {
-        debug!("parse_path_with_tps(colons=%b)", colons);
+            // Parse the '::' before type parameters if it's required. If
+            // it is required and wasn't present, then we're done.
+            if mode == LifetimeAndTypesWithColons &&
+                    !self.eat(&token::MOD_SEP) {
+                segments.push(PathSegmentAndBoundSet {
+                    segment: ast::PathSegment {
+                        identifier: identifier,
+                        lifetime: None,
+                        types: opt_vec::Empty,
+                    },
+                    bound_set: bound_set
+                });
+                break
+            }
 
-        maybe_whole!(deref self, nt_path);
-        let lo = self.span.lo;
-        let path = self.parse_path_without_tps();
-        if colons && !self.eat(&token::MOD_SEP) {
-            return path;
-        }
-
-        // If the path might have bounds on it, they should be parsed before
-        // the parameters, e.g. module::TraitName:B1+B2<T>
-        before_tps.map_move(|callback| callback());
-
-        // Parse the (obsolete) trailing region parameter, if any, which will
-        // be written "foo/&x"
-        let rp_slash = {
-            if *self.token == token::BINOP(token::SLASH)
-                && self.look_ahead(1, |t| *t == token::BINOP(token::AND))
-            {
-                self.bump(); self.bump();
-                self.obsolete(*self.last_span, ObsoleteLifetimeNotation);
-                match *self.token {
-                    token::IDENT(sid, _) => {
-                        let span = self.span;
-                        self.bump();
-                        Some(ast::Lifetime {
-                            id: self.get_id(),
-                            span: *span,
-                            ident: sid
-                        })
+            // Parse the `<` before the lifetime and types, if applicable.
+            let (any_lifetime_or_types, optional_lifetime, types) =
+                    if mode != NoTypesAllowed && self.eat(&token::LT) {
+                // Parse an optional lifetime.
+                let optional_lifetime = match *self.token {
+                    token::LIFETIME(*) => Some(self.parse_lifetime()),
+                    _ => None,
+                };
+
+                // Parse type parameters.
+                let mut types = opt_vec::Empty;
+                let mut need_comma = optional_lifetime.is_some();
+                loop {
+                    // We're done if we see a `>`.
+                    match *self.token {
+                        token::GT | token::BINOP(token::SHR) => {
+                            self.expect_gt();
+                            break
+                        }
+                        _ => {} // Go on.
                     }
-                    _ => {
-                        self.fatal(fmt!("Expected a lifetime name"));
+
+                    if need_comma {
+                        self.expect(&token::COMMA)
+                    } else {
+                        need_comma = true
                     }
+
+                    types.push(self.parse_ty(false))
                 }
+
+                (true, optional_lifetime, types)
             } else {
-                None
-            }
-        };
+                (false, None, opt_vec::Empty)
+            };
 
-        // Parse any lifetime or type parameters which may appear:
-        let (lifetimes, tps) = self.parse_generic_values();
-        let hi = self.span.lo;
+            // Assemble and push the result.
+            segments.push(PathSegmentAndBoundSet {
+                segment: ast::PathSegment {
+                    identifier: identifier,
+                    lifetime: optional_lifetime,
+                    types: types,
+                },
+                bound_set: bound_set
+            });
 
-        let rp = match (&rp_slash, &lifetimes) {
-            (&Some(_), _) => rp_slash,
-            (&None, v) => {
-                if v.len() == 0 {
-                    None
-                } else if v.len() == 1 {
-                    Some(*v.get(0))
-                } else {
-                    self.fatal(fmt!("Expected at most one \
-                                     lifetime name (for now)"));
+            // We're done if we don't see a '::', unless the mode required
+            // a double colon to get here in the first place.
+            if !(mode == LifetimeAndTypesWithColons &&
+                    !any_lifetime_or_types) {
+                if !self.eat(&token::MOD_SEP) {
+                    break
                 }
             }
-        };
-
-        ast::Path {
-            span: mk_sp(lo, hi),
-            rp: rp,
-            types: tps,
-            .. path.clone()
         }
-    }
 
-    // parse a path optionally with type parameters. If 'colons'
-    // is true, then type parameters must be preceded by colons,
-    // as in a::t::<t1,t2>
-    pub fn parse_path_with_tps(&self, colons: bool) -> ast::Path {
-        self.parse_bounded_path_with_tps(colons, None)
-    }
+        // Assemble the span.
+        let span = mk_sp(lo, self.last_span.hi);
 
-    // Like the above, but can also parse kind bounds in the case of a
-    // path to be used as a type that might be a trait.
-    pub fn parse_type_path(&self) -> (ast::Path, Option<OptVec<TyParamBound>>) {
+        // Assemble the path segments.
+        let mut path_segments = ~[];
         let mut bounds = None;
-        let path = self.parse_bounded_path_with_tps(false, Some(|| {
-            // Note: this closure might not even get called in the case of a
-            // macro-generated path. But that's the macro parser's job.
-            bounds = self.parse_optional_ty_param_bounds();
-        }));
-        (path, bounds)
+        let last_segment_index = segments.len() - 1;
+        for (i, segment_and_bounds) in segments.consume_iter().enumerate() {
+            let PathSegmentAndBoundSet {
+                segment: segment,
+                bound_set: bound_set
+            } = segment_and_bounds;
+            path_segments.push(segment);
+
+            if bound_set.is_some() {
+                if i != last_segment_index {
+                    self.span_err(span,
+                                  "type parameter bounds are allowed only \
+                                   before the last segment in a path")
+                }
+
+                bounds = bound_set
+            }
+        }
+
+        // Assemble the result.
+        let path_and_bounds = PathAndBounds {
+            path: ast::Path {
+                span: span,
+                global: is_global,
+                segments: path_segments,
+            },
+            bounds: bounds,
+        };
+
+        path_and_bounds
     }
 
     /// parses 0 or 1 lifetime
@@ -1789,7 +1839,7 @@ impl Parser {
         } else if *self.token == token::MOD_SEP ||
                 is_ident(&*self.token) && !self.is_keyword(keywords::True) &&
                 !self.is_keyword(keywords::False) {
-            let pth = self.parse_path_with_tps(true);
+            let pth = self.parse_path(LifetimeAndTypesWithColons).path;
 
             // `!`, as an operator, is prefix, so we know this isn't that
             if *self.token == token::NOT {
@@ -2880,7 +2930,8 @@ impl Parser {
             let val = self.parse_literal_maybe_minus();
             if self.eat(&token::DOTDOT) {
                 let end = if is_ident_or_path(tok) {
-                    let path = self.parse_path_with_tps(true);
+                    let path = self.parse_path(LifetimeAndTypesWithColons)
+                                   .path;
                     let hi = self.span.hi;
                     self.mk_expr(lo, hi, expr_path(path))
                 } else {
@@ -2909,7 +2960,7 @@ impl Parser {
                 let end = self.parse_expr_res(RESTRICT_NO_BAR_OP);
                 pat = pat_range(start, end);
             } else if is_plain_ident(&*self.token) && !can_be_enum_or_struct {
-                let name = self.parse_path_without_tps();
+                let name = self.parse_path(NoTypesAllowed).path;
                 let sub;
                 if self.eat(&token::AT) {
                     // parse foo @ pat
@@ -2921,7 +2972,8 @@ impl Parser {
                 pat = pat_ident(bind_infer, name, sub);
             } else {
                 // parse an enum pat
-                let enum_path = self.parse_path_with_tps(true);
+                let enum_path = self.parse_path(LifetimeAndTypesWithColons)
+                                    .path;
                 match *self.token {
                     token::LBRACE => {
                         self.bump();
@@ -2957,7 +3009,7 @@ impl Parser {
                             }
                           },
                           _ => {
-                              if enum_path.idents.len()==1u {
+                              if enum_path.segments.len() == 1 {
                                   // it could still be either an enum
                                   // or an identifier pattern, resolve
                                   // will sort it out:
@@ -2992,7 +3044,7 @@ impl Parser {
                             "expected identifier, found path");
         }
         // why a path here, and not just an identifier?
-        let name = self.parse_path_without_tps();
+        let name = self.parse_path(NoTypesAllowed).path;
         let sub = if self.eat(&token::AT) {
             Some(self.parse_pat())
         } else {
@@ -3109,7 +3161,7 @@ impl Parser {
 
             // Potential trouble: if we allow macros with paths instead of
             // idents, we'd need to look ahead past the whole path here...
-            let pth = self.parse_path_without_tps();
+            let pth = self.parse_path(NoTypesAllowed).path;
             self.bump();
 
             let id = if *self.token == token::LPAREN {
@@ -3785,7 +3837,7 @@ impl Parser {
     // parse a::B<~str,int>
     fn parse_trait_ref(&self) -> trait_ref {
         ast::trait_ref {
-            path: self.parse_path_with_tps(false),
+            path: self.parse_path(LifetimeAndTypesWithoutColons).path,
             ref_id: self.get_id(),
         }
     }
@@ -4701,7 +4753,7 @@ impl Parser {
             }
 
             // item macro.
-            let pth = self.parse_path_without_tps();
+            let pth = self.parse_path(NoTypesAllowed).path;
             self.expect(&token::NOT);
 
             // a 'special' identifier (like what `macro_rules!` uses)
@@ -4785,11 +4837,17 @@ impl Parser {
                 let id = self.parse_ident();
                 path.push(id);
             }
-            let path = ast::Path { span: mk_sp(lo, self.span.hi),
-                                    global: false,
-                                    idents: path,
-                                    rp: None,
-                                    types: ~[] };
+            let path = ast::Path {
+                span: mk_sp(lo, self.span.hi),
+                global: false,
+                segments: path.consume_iter().transform(|identifier| {
+                    ast::PathSegment {
+                        identifier: identifier,
+                        lifetime: None,
+                        types: opt_vec::Empty,
+                    }
+                }).collect()
+            };
             return @spanned(lo, self.span.hi,
                             view_path_simple(first_ident,
                                              path,
@@ -4815,11 +4873,17 @@ impl Parser {
                         seq_sep_trailing_allowed(token::COMMA),
                         |p| p.parse_path_list_ident()
                     );
-                    let path = ast::Path { span: mk_sp(lo, self.span.hi),
-                                            global: false,
-                                            idents: path,
-                                            rp: None,
-                                            types: ~[] };
+                    let path = ast::Path {
+                        span: mk_sp(lo, self.span.hi),
+                        global: false,
+                        segments: path.consume_iter().transform(|identifier| {
+                            ast::PathSegment {
+                                identifier: identifier,
+                                lifetime: None,
+                                types: opt_vec::Empty,
+                            }
+                        }).collect()
+                    };
                     return @spanned(lo, self.span.hi,
                                  view_path_list(path, idents, self.get_id()));
                   }
@@ -4827,11 +4891,17 @@ impl Parser {
                   // foo::bar::*
                   token::BINOP(token::STAR) => {
                     self.bump();
-                    let path = ast::Path { span: mk_sp(lo, self.span.hi),
-                                            global: false,
-                                            idents: path,
-                                            rp: None,
-                                            types: ~[] };
+                    let path = ast::Path {
+                        span: mk_sp(lo, self.span.hi),
+                        global: false,
+                        segments: path.consume_iter().transform(|identifier| {
+                            ast::PathSegment {
+                                identifier: identifier,
+                                lifetime: None,
+                                types: opt_vec::Empty,
+                            }
+                        }).collect()
+                    };
                     return @spanned(lo, self.span.hi,
                                     view_path_glob(path, self.get_id()));
                   }
@@ -4843,11 +4913,17 @@ impl Parser {
           _ => ()
         }
         let last = path[path.len() - 1u];
-        let path = ast::Path { span: mk_sp(lo, self.span.hi),
-                                global: false,
-                                idents: path,
-                                rp: None,
-                                types: ~[] };
+        let path = ast::Path {
+            span: mk_sp(lo, self.span.hi),
+            global: false,
+            segments: path.consume_iter().transform(|identifier| {
+                ast::PathSegment {
+                    identifier: identifier,
+                    lifetime: None,
+                    types: opt_vec::Empty,
+                }
+            }).collect()
+        };
         return @spanned(lo,
                         self.last_span.hi,
                         view_path_simple(last, path, self.get_id()));
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index ee9b2f4760d..d449ba4eb5f 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1501,34 +1501,52 @@ pub fn print_for_decl(s: @ps, loc: &ast::Local, coll: &ast::expr) {
     print_expr(s, coll);
 }
 
-fn print_path_(s: @ps, path: &ast::Path, colons_before_params: bool,
+fn print_path_(s: @ps,
+               path: &ast::Path,
+               colons_before_params: bool,
                opt_bounds: &Option<OptVec<ast::TyParamBound>>) {
     maybe_print_comment(s, path.span.lo);
-    if path.global { word(s.s, "::"); }
-    let mut first = true;
-    for id in path.idents.iter() {
-        if first { first = false; } else { word(s.s, "::"); }
-        print_ident(s, *id);
+    if path.global {
+        word(s.s, "::");
     }
-    do opt_bounds.map |bounds| {
-        print_bounds(s, bounds, true);
-    };
-    if path.rp.is_some() || !path.types.is_empty() {
-        if colons_before_params { word(s.s, "::"); }
 
-        if path.rp.is_some() || !path.types.is_empty() {
+    let mut first = true;
+    for (i, segment) in path.segments.iter().enumerate() {
+        if first {
+            first = false
+        } else {
+            word(s.s, "::")
+        }
+
+        print_ident(s, segment.identifier);
+
+        if segment.lifetime.is_some() || !segment.types.is_empty() {
+            // If this is the last segment, print the bounds.
+            if i == path.segments.len() - 1 {
+                match *opt_bounds {
+                    None => {}
+                    Some(ref bounds) => print_bounds(s, bounds, true),
+                }
+            }
+
+            if colons_before_params {
+                word(s.s, "::")
+            }
             word(s.s, "<");
 
-            for r in path.rp.iter() {
-                print_lifetime(s, r);
-                if !path.types.is_empty() {
-                    word_space(s, ",");
+            for lifetime in segment.lifetime.iter() {
+                print_lifetime(s, lifetime);
+                if !segment.types.is_empty() {
+                    word_space(s, ",")
                 }
             }
 
-            commasep(s, inconsistent, path.types, print_type);
+            commasep(s,
+                     inconsistent,
+                     segment.types.map_to_vec(|t| (*t).clone()),
+                     print_type);
 
-            word(s.s, ">");
+            word(s.s, ">")
         }
     }
 }
@@ -1819,7 +1837,7 @@ pub fn print_meta_item(s: @ps, item: &ast::MetaItem) {
 pub fn print_view_path(s: @ps, vp: &ast::view_path) {
     match vp.node {
       ast::view_path_simple(ident, ref path, _) => {
-        if path.idents[path.idents.len()-1u] != ident {
+        if path.segments.last().identifier != ident {
             print_ident(s, ident);
             space(s.s);
             word_space(s, "=");
@@ -1899,8 +1917,9 @@ pub fn print_arg(s: @ps, input: &ast::arg) {
       _ => {
         match input.pat.node {
             ast::pat_ident(_, ref path, _) if
-                path.idents.len() == 1 &&
-                path.idents[0] == parse::token::special_idents::invalid => {
+                path.segments.len() == 1 &&
+                path.segments[0].identifier ==
+                    parse::token::special_idents::invalid => {
                 // Do nothing.
             }
             _ => {
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 8178e7f3760..ef44a368ab5 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -319,8 +319,10 @@ pub fn walk_ty<E:Clone, V:Visitor<E>>(visitor: &mut V, typ: &Ty, env: E) {
 }
 
 pub fn walk_path<E:Clone, V:Visitor<E>>(visitor: &mut V, path: &Path, env: E) {
-    for typ in path.types.iter() {
-        visitor.visit_ty(typ, env.clone())
+    for segment in path.segments.iter() {
+        for typ in path.types.iter() {
+            visitor.visit_ty(typ, env.clone())
+        }
     }
 }
 
diff --git a/src/test/run-pass/mid-path-type-params.rs b/src/test/run-pass/mid-path-type-params.rs
new file mode 100644
index 00000000000..8f01bd5e5ea
--- /dev/null
+++ b/src/test/run-pass/mid-path-type-params.rs
@@ -0,0 +1,16 @@
+struct S<T> {
+    contents: T,
+}
+
+impl<T> S<T> {
+    fn new<U>(x: T, _: U) -> S<T> {
+        S {
+            contents: x,
+        }
+    }
+}
+
+fn main() {
+    let _ = S::<int>::new::<float>(1, 1.0);
+}
+