From 3b6314c39bfc13b5a41c53f13c3fafa7ad91e062 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 7 Aug 2013 09:47:28 -0700 Subject: [PATCH] librustc: Add support for type parameters in the middle of paths. For example, `foo::::bar::`. This doesn't enforce that the type parameters are in the right positions, however. --- src/librustc/front/std_inject.rs | 17 +- src/librustc/front/test.rs | 31 +- src/librustc/metadata/encoder.rs | 3 +- src/librustc/metadata/tydecode.rs | 20 +- src/librustc/middle/check_const.rs | 2 +- src/librustc/middle/privacy.rs | 14 +- src/librustc/middle/region.rs | 6 +- src/librustc/middle/resolve.rs | 101 +++--- src/librustc/middle/trans/consts.rs | 4 +- src/librustc/middle/typeck/astconv.rs | 32 +- src/librustc/middle/typeck/check/mod.rs | 26 +- src/libsyntax/ast.rs | 21 +- src/libsyntax/ast_util.rs | 24 +- src/libsyntax/ext/base.rs | 20 +- src/libsyntax/ext/build.rs | 25 +- src/libsyntax/ext/concat_idents.rs | 11 +- src/libsyntax/ext/expand.rs | 31 +- src/libsyntax/ext/tt/macro_parser.rs | 6 +- src/libsyntax/fold.rs | 8 +- src/libsyntax/oldvisit.rs | 6 +- src/libsyntax/parse/mod.rs | 227 ++++++++----- src/libsyntax/parse/parser.rs | 370 +++++++++++++--------- src/libsyntax/print/pprust.rs | 63 ++-- src/libsyntax/visit.rs | 6 +- src/test/run-pass/mid-path-type-params.rs | 16 + 25 files changed, 697 insertions(+), 393 deletions(-) create mode 100644 src/test/run-pass/mid-path-type-params.rs 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) -> codemap::spanned { } 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:: 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 { // 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( // 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( } (&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.types.map(|a_t| ast_ty_to_ty(this, rscope, a_t)); + 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} + substs { + regions: ty::NonerasedRegions(regions), + self_ty: self_ty, + tps: tps + } } pub fn ast_path_to_substs_and_ty( 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( } 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, - /// These are the type parameters, ie, the `a, b` in `foo::bar::` - 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, + /// The type parameters for this path segment, if present. + types: OptVec, } 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, 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(t: &Ty, (e, v): (E, vt)) { } pub fn visit_path(p: &Path, (e, v): (E, vt)) { - 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(p: &pat, (e, v): (E, vt)) { 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; 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` + LifetimeAndTypesWithoutColons, + /// A path with a lifetime and type parameters with double colons before + /// the type parameters; e.g. `foo::bar::<'self>::Baz::` + 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` 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>, +} + +/// A path paired with optional type bounds. +struct PathAndBounds { + path: ast::Path, + bounds: Option>, +} + 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: ~[] } - } - - pub fn parse_bounded_path_with_tps(&self, colons: bool, - before_tps: Option<&fn()>) -> ast::Path { - debug!("parse_path_with_tps(colons=%b)", colons); - - 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 - 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 - }) - } - _ => { - self.fatal(fmt!("Expected a lifetime name")); - } - } + // Next, parse a colon and bounded type parameters, if applicable. + let bound_set = if mode == LifetimeAndTypesAndBounds { + self.parse_optional_ty_param_bounds() } else { None + }; + + // 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 } - }; - // Parse any lifetime or type parameters which may appear: - let (lifetimes, tps) = self.parse_generic_values(); - let hi = self.span.lo; + // 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, + }; - 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)")); + // 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. + } + + if need_comma { + self.expect(&token::COMMA) + } else { + need_comma = true + } + + types.push(self.parse_ty(false)) + } + + (true, optional_lifetime, types) + } else { + (false, None, opt_vec::Empty) + }; + + // Assemble and push the result. + segments.push(PathSegmentAndBoundSet { + segment: ast::PathSegment { + identifier: identifier, + lifetime: optional_lifetime, + types: types, + }, + bound_set: bound_set + }); + + // 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 } } + } + + // Assemble the span. + let span = mk_sp(lo, self.last_span.hi); + + // Assemble the path segments. + let mut path_segments = ~[]; + let mut bounds = None; + 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, }; - 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:: - pub fn parse_path_with_tps(&self, colons: bool) -> ast::Path { - self.parse_bounded_path_with_tps(colons, None) - } - - // 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>) { - 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) + 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>) { 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() { - word(s.s, "<"); + let mut first = true; + for (i, segment) in path.segments.iter().enumerate() { + if first { + first = false + } else { + word(s.s, "::") + } - for r in path.rp.iter() { - print_lifetime(s, r); - if !path.types.is_empty() { - word_space(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), } } - commasep(s, inconsistent, path.types, print_type); + if colons_before_params { + word(s.s, "::") + } + word(s.s, "<"); - word(s.s, ">"); + for lifetime in segment.lifetime.iter() { + print_lifetime(s, lifetime); + if !segment.types.is_empty() { + word_space(s, ",") + } + } + + commasep(s, + inconsistent, + segment.types.map_to_vec(|t| (*t).clone()), + print_type); + + 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>(visitor: &mut V, typ: &Ty, env: E) { } pub fn walk_path>(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 { + contents: T, +} + +impl S { + fn new(x: T, _: U) -> S { + S { + contents: x, + } + } +} + +fn main() { + let _ = S::::new::(1, 1.0); +} +