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.
This commit is contained in:
Patrick Walton 2013-08-07 09:47:28 -07:00
parent 5c3504799d
commit 3b6314c39b
25 changed files with 697 additions and 393 deletions

View File

@ -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));

View File

@ -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)]

View File

@ -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));

View File

@ -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));
}
}
}
};

View File

@ -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");

View File

@ -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)));
}
}
_ => {}

View File

@ -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);
}
}

View File

@ -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)));
}
}

View File

@ -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)) => {

View File

@ -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.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<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");

View File

@ -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!("<<<");

View File

@ -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;

View File

@ -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);

View File

@ -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)),
}
}

View File

@ -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,
}
}

View File

@ -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,

View File

@ -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...
_ => ()

View File

@ -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

View File

@ -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)),
})
}
}

View File

@ -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>)) {

View File

@ -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)}],

View File

@ -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: ~[] }
}
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<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
})
}
_ => {
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::<t1,t2>
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<OptVec<TyParamBound>>) {
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()));

View File

@ -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() {
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.
}
_ => {

View File

@ -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())
}
}
}

View File

@ -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);
}