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:
parent
5c3504799d
commit
3b6314c39b
|
@ -17,6 +17,7 @@ use syntax::attr;
|
||||||
use syntax::codemap::dummy_sp;
|
use syntax::codemap::dummy_sp;
|
||||||
use syntax::codemap;
|
use syntax::codemap;
|
||||||
use syntax::fold;
|
use syntax::fold;
|
||||||
|
use syntax::opt_vec;
|
||||||
|
|
||||||
static STD_VERSION: &'static str = "0.8-pre";
|
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 {
|
let prelude_path = ast::Path {
|
||||||
span: dummy_sp(),
|
span: dummy_sp(),
|
||||||
global: false,
|
global: false,
|
||||||
idents: ~[
|
segments: ~[
|
||||||
sess.ident_of("std"),
|
ast::PathSegment {
|
||||||
sess.ident_of("prelude")
|
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));
|
let vp = @spanned(ast::view_path_glob(prelude_path, n2));
|
||||||
|
|
|
@ -16,14 +16,15 @@ use front::config;
|
||||||
|
|
||||||
use std::vec;
|
use std::vec;
|
||||||
use syntax::ast_util::*;
|
use syntax::ast_util::*;
|
||||||
|
use syntax::attr::AttrMetaMethods;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::codemap::{dummy_sp, span, ExpnInfo, NameAndSpan};
|
use syntax::codemap::{dummy_sp, span, ExpnInfo, NameAndSpan};
|
||||||
use syntax::codemap;
|
use syntax::codemap;
|
||||||
use syntax::ext::base::ExtCtxt;
|
use syntax::ext::base::ExtCtxt;
|
||||||
use syntax::fold;
|
use syntax::fold;
|
||||||
|
use syntax::opt_vec;
|
||||||
use syntax::print::pprust;
|
use syntax::print::pprust;
|
||||||
use syntax::{ast, ast_util};
|
use syntax::{ast, ast_util};
|
||||||
use syntax::attr::AttrMetaMethods;
|
|
||||||
|
|
||||||
type node_id_gen = @fn() -> ast::NodeId;
|
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 {
|
fn path_node(ids: ~[ast::ident]) -> ast::Path {
|
||||||
ast::Path { span: dummy_sp(),
|
ast::Path {
|
||||||
global: false,
|
span: dummy_sp(),
|
||||||
idents: ids,
|
global: false,
|
||||||
rp: None,
|
segments: ids.consume_iter().transform(|identifier| ast::PathSegment {
|
||||||
types: ~[] }
|
identifier: identifier,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_node_global(ids: ~[ast::ident]) -> ast::Path {
|
fn path_node_global(ids: ~[ast::ident]) -> ast::Path {
|
||||||
ast::Path { span: dummy_sp(),
|
ast::Path {
|
||||||
global: true,
|
span: dummy_sp(),
|
||||||
idents: ids,
|
global: true,
|
||||||
rp: None,
|
segments: ids.consume_iter().transform(|identifier| ast::PathSegment {
|
||||||
types: ~[] }
|
identifier: identifier,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(stage0)]
|
#[cfg(stage0)]
|
||||||
|
|
|
@ -988,7 +988,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
|
||||||
encode_name(ecx, ebml_w, item.ident);
|
encode_name(ecx, ebml_w, item.ident);
|
||||||
encode_attributes(ebml_w, item.attrs);
|
encode_attributes(ebml_w, item.attrs);
|
||||||
match ty.node {
|
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());
|
assert!(bounds.is_none());
|
||||||
encode_impl_type_basename(ecx, ebml_w,
|
encode_impl_type_basename(ecx, ebml_w,
|
||||||
ast_util::path_to_ident(path));
|
ast_util::path_to_ident(path));
|
||||||
|
|
|
@ -138,12 +138,20 @@ fn parse_path(st: &mut PState) -> @ast::Path {
|
||||||
':' => { next(st); next(st); }
|
':' => { next(st); next(st); }
|
||||||
c => {
|
c => {
|
||||||
if c == '(' {
|
if c == '(' {
|
||||||
return @ast::Path { span: dummy_sp(),
|
return @ast::Path {
|
||||||
global: false,
|
span: dummy_sp(),
|
||||||
idents: idents,
|
global: false,
|
||||||
rp: None,
|
segments: idents.consume_iter().transform(|identifier| {
|
||||||
types: ~[] };
|
ast::PathSegment {
|
||||||
} else { idents.push(parse_ident_(st, is_last)); }
|
identifier: identifier,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
idents.push(parse_ident_(st, is_last));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -141,7 +141,7 @@ pub fn check_expr(v: &mut CheckCrateVisitor,
|
||||||
// to handle on-demand instantiation of functions via
|
// to handle on-demand instantiation of functions via
|
||||||
// foo::<bar> in a const. Currently that is only done on
|
// foo::<bar> in a const. Currently that is only done on
|
||||||
// a path in trans::callee that only works in block contexts.
|
// 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(
|
sess.span_err(
|
||||||
e.span, "paths in constants may only refer to \
|
e.span, "paths in constants may only refer to \
|
||||||
items without type parameters");
|
items without type parameters");
|
||||||
|
|
|
@ -251,7 +251,9 @@ impl PrivacyVisitor {
|
||||||
match def {
|
match def {
|
||||||
def_static_method(method_id, _, _) => {
|
def_static_method(method_id, _, _) => {
|
||||||
debug!("found static method def, checking it");
|
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, _) => {
|
def_fn(def_id, _) => {
|
||||||
if def_id.crate == LOCAL_CRATE {
|
if def_id.crate == LOCAL_CRATE {
|
||||||
|
@ -259,13 +261,19 @@ impl PrivacyVisitor {
|
||||||
!self.privileged_items.iter().any(|x| x == &def_id.node) {
|
!self.privileged_items.iter().any(|x| x == &def_id.node) {
|
||||||
self.tcx.sess.span_err(span,
|
self.tcx.sess.span_err(span,
|
||||||
fmt!("function `%s` is private",
|
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,
|
} else if csearch::get_item_visibility(self.tcx.sess.cstore,
|
||||||
def_id) != public {
|
def_id) != public {
|
||||||
self.tcx.sess.span_err(span,
|
self.tcx.sess.span_err(span,
|
||||||
fmt!("function `%s` is private",
|
fmt!("function `%s` is private",
|
||||||
token::ident_to_str(path.idents.last())));
|
token::ident_to_str(
|
||||||
|
&path.segments
|
||||||
|
.last()
|
||||||
|
.identifier)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -827,7 +827,7 @@ fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor,
|
||||||
Some(&ast::def_trait(did)) |
|
Some(&ast::def_trait(did)) |
|
||||||
Some(&ast::def_struct(did)) => {
|
Some(&ast::def_struct(did)) => {
|
||||||
if did.crate == ast::LOCAL_CRATE {
|
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);
|
cx.add_dep(did.node);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -837,7 +837,7 @@ fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor,
|
||||||
Some(variance) => {
|
Some(variance) => {
|
||||||
debug!("reference to external, rp'd type %s",
|
debug!("reference to external, rp'd type %s",
|
||||||
pprust::ty_to_str(ty, sess.intr()));
|
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);
|
let rv = cx.add_variance(variance);
|
||||||
cx.add_rp(cx.item_id, rv)
|
cx.add_rp(cx.item_id, rv)
|
||||||
}
|
}
|
||||||
|
@ -860,7 +860,7 @@ fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor,
|
||||||
ast::ty_path(ref path, _, _) => {
|
ast::ty_path(ref path, _, _) => {
|
||||||
// type parameters are---for now, anyway---always invariant
|
// type parameters are---for now, anyway---always invariant
|
||||||
do cx.with_ambient_variance(rv_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);
|
visitor.visit_ty(tp, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1277,7 +1277,7 @@ impl Resolver {
|
||||||
&Ty {
|
&Ty {
|
||||||
node: ty_path(ref path, _, _),
|
node: ty_path(ref path, _, _),
|
||||||
_
|
_
|
||||||
} if path.idents.len() == 1 => {
|
} if path.segments.len() == 1 => {
|
||||||
let name = path_to_ident(path);
|
let name = path_to_ident(path);
|
||||||
|
|
||||||
let new_parent = match parent.children.find(&name) {
|
let new_parent = match parent.children.find(&name) {
|
||||||
|
@ -1476,20 +1476,22 @@ impl Resolver {
|
||||||
let mut module_path = ~[];
|
let mut module_path = ~[];
|
||||||
match view_path.node {
|
match view_path.node {
|
||||||
view_path_simple(_, ref full_path, _) => {
|
view_path_simple(_, ref full_path, _) => {
|
||||||
let path_len = full_path.idents.len();
|
let path_len = full_path.segments.len();
|
||||||
assert!(path_len != 0);
|
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 {
|
if i != path_len - 1 {
|
||||||
module_path.push(*ident);
|
module_path.push(segment.identifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view_path_glob(ref module_ident_path, _) |
|
view_path_glob(ref module_ident_path, _) |
|
||||||
view_path_list(ref module_ident_path, _, _) => {
|
view_path_list(ref module_ident_path, _, _) => {
|
||||||
for ident in module_ident_path.idents.iter() {
|
for segment in module_ident_path.segments.iter() {
|
||||||
module_path.push(*ident);
|
module_path.push(segment.identifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1498,7 +1500,8 @@ impl Resolver {
|
||||||
let module_ = self.get_module_from_parent(parent);
|
let module_ = self.get_module_from_parent(parent);
|
||||||
match view_path.node {
|
match view_path.node {
|
||||||
view_path_simple(binding, ref full_path, id) => {
|
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,
|
let subclass = @SingleImport(binding,
|
||||||
source_ident);
|
source_ident);
|
||||||
self.build_import_directive(privacy,
|
self.build_import_directive(privacy,
|
||||||
|
@ -2109,6 +2112,14 @@ impl Resolver {
|
||||||
return result;
|
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,
|
pub fn import_directive_subclass_to_str(@mut self,
|
||||||
subclass: ImportDirectiveSubclass)
|
subclass: ImportDirectiveSubclass)
|
||||||
-> @str {
|
-> @str {
|
||||||
|
@ -3841,8 +3852,7 @@ impl Resolver {
|
||||||
reference_type: TraitReferenceType) {
|
reference_type: TraitReferenceType) {
|
||||||
match self.resolve_path(id, &trait_reference.path, TypeNS, true, visitor) {
|
match self.resolve_path(id, &trait_reference.path, TypeNS, true, visitor) {
|
||||||
None => {
|
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 {
|
let usage_str = match reference_type {
|
||||||
TraitBoundingTypeParameter => "bound type parameter with",
|
TraitBoundingTypeParameter => "bound type parameter with",
|
||||||
TraitImplementation => "implement",
|
TraitImplementation => "implement",
|
||||||
|
@ -4141,8 +4151,8 @@ impl Resolver {
|
||||||
let mut result_def = None;
|
let mut result_def = None;
|
||||||
|
|
||||||
// First, check to see whether the name is a primitive type.
|
// First, check to see whether the name is a primitive type.
|
||||||
if path.idents.len() == 1 {
|
if path.segments.len() == 1 {
|
||||||
let name = *path.idents.last();
|
let name = path.segments.last().identifier;
|
||||||
|
|
||||||
match self.primitive_type_table
|
match self.primitive_type_table
|
||||||
.primitive_types
|
.primitive_types
|
||||||
|
@ -4165,7 +4175,7 @@ impl Resolver {
|
||||||
debug!("(resolving type) resolved `%s` to \
|
debug!("(resolving type) resolved `%s` to \
|
||||||
type %?",
|
type %?",
|
||||||
self.session.str_of(
|
self.session.str_of(
|
||||||
*path.idents.last()),
|
path.segments.last().identifier),
|
||||||
def);
|
def);
|
||||||
result_def = Some(def);
|
result_def = Some(def);
|
||||||
}
|
}
|
||||||
|
@ -4184,14 +4194,15 @@ impl Resolver {
|
||||||
// Write the result into the def map.
|
// Write the result into the def map.
|
||||||
debug!("(resolving type) writing resolution for `%s` \
|
debug!("(resolving type) writing resolution for `%s` \
|
||||||
(id %d)",
|
(id %d)",
|
||||||
self.idents_to_str(path.idents),
|
self.path_idents_to_str(path),
|
||||||
path_id);
|
path_id);
|
||||||
self.record_def(path_id, def);
|
self.record_def(path_id, def);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.resolve_error
|
self.resolve_error
|
||||||
(ty.span, fmt!("use of undeclared type name `%s`",
|
(ty.span,
|
||||||
self.idents_to_str(path.idents)));
|
fmt!("use of undeclared type name `%s`",
|
||||||
|
self.path_idents_to_str(path)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4230,7 +4241,7 @@ impl Resolver {
|
||||||
do walk_pat(pattern) |pattern| {
|
do walk_pat(pattern) |pattern| {
|
||||||
match pattern.node {
|
match pattern.node {
|
||||||
pat_ident(binding_mode, ref path, _)
|
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
|
// The meaning of pat_ident with no type parameters
|
||||||
// depends on whether an enum variant or unit-like struct
|
// 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
|
// such a value is simply disallowed (since it's rarely
|
||||||
// what you want).
|
// what you want).
|
||||||
|
|
||||||
let ident = path.idents[0];
|
let ident = path.segments[0].identifier;
|
||||||
|
|
||||||
match self.resolve_bare_identifier_pattern(ident) {
|
match self.resolve_bare_identifier_pattern(ident) {
|
||||||
FoundStructOrEnumVariant(def)
|
FoundStructOrEnumVariant(def)
|
||||||
|
@ -4351,7 +4362,9 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the types in the path pattern.
|
// 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);
|
self.resolve_type(ty, visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4375,7 +4388,7 @@ impl Resolver {
|
||||||
path.span,
|
path.span,
|
||||||
fmt!("`%s` is not an enum variant or constant",
|
fmt!("`%s` is not an enum variant or constant",
|
||||||
self.session.str_of(
|
self.session.str_of(
|
||||||
*path.idents.last())));
|
path.segments.last().identifier)))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.resolve_error(path.span,
|
self.resolve_error(path.span,
|
||||||
|
@ -4384,7 +4397,9 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the types in the path pattern.
|
// 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);
|
self.resolve_type(ty, visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4402,8 +4417,10 @@ impl Resolver {
|
||||||
self.resolve_error(
|
self.resolve_error(
|
||||||
path.span,
|
path.span,
|
||||||
fmt!("`%s` is not an enum variant, struct or const",
|
fmt!("`%s` is not an enum variant, struct or const",
|
||||||
self.session.str_of(
|
self.session
|
||||||
*path.idents.last())));
|
.str_of(path.segments
|
||||||
|
.last()
|
||||||
|
.identifier)));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.resolve_error(path.span,
|
self.resolve_error(path.span,
|
||||||
|
@ -4413,7 +4430,9 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the types in the path pattern.
|
// 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);
|
self.resolve_type(ty, visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4448,7 +4467,7 @@ impl Resolver {
|
||||||
self.resolve_error(
|
self.resolve_error(
|
||||||
path.span,
|
path.span,
|
||||||
fmt!("`%s` does not name a structure",
|
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)
|
visitor: &mut ResolveVisitor)
|
||||||
-> Option<def> {
|
-> Option<def> {
|
||||||
// First, resolve the types.
|
// 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);
|
self.resolve_type(ty, visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4520,12 +4539,17 @@ impl Resolver {
|
||||||
namespace);
|
namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
let unqualified_def = self.resolve_identifier(
|
let unqualified_def = self.resolve_identifier(path.segments
|
||||||
*path.idents.last(), namespace, check_ribs, path.span);
|
.last()
|
||||||
|
.identifier,
|
||||||
|
namespace,
|
||||||
|
check_ribs,
|
||||||
|
path.span);
|
||||||
|
|
||||||
if path.idents.len() > 1 {
|
if path.segments.len() > 1 {
|
||||||
let def = self.resolve_module_relative_path(
|
let def = self.resolve_module_relative_path(path,
|
||||||
path, self.xray_context, namespace);
|
self.xray_context,
|
||||||
|
namespace);
|
||||||
match (def, unqualified_def) {
|
match (def, unqualified_def) {
|
||||||
(Some(d), Some(ud)) if d == ud => {
|
(Some(d), Some(ud)) if d == ud => {
|
||||||
self.session.add_lint(unnecessary_qualification,
|
self.session.add_lint(unnecessary_qualification,
|
||||||
|
@ -4640,12 +4664,12 @@ impl Resolver {
|
||||||
|
|
||||||
pub fn intern_module_part_of_path(@mut self, path: &Path) -> ~[ident] {
|
pub fn intern_module_part_of_path(@mut self, path: &Path) -> ~[ident] {
|
||||||
let mut module_path_idents = ~[];
|
let mut module_path_idents = ~[];
|
||||||
for (index, ident) in path.idents.iter().enumerate() {
|
for (index, segment) in path.segments.iter().enumerate() {
|
||||||
if index == path.idents.len() - 1 {
|
if index == path.segments.len() - 1 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
module_path_idents.push(*ident);
|
module_path_idents.push(segment.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
return module_path_idents;
|
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,
|
let def = match self.resolve_definition_of_name_in_module(containing_module,
|
||||||
name,
|
name,
|
||||||
namespace,
|
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,
|
match self.resolve_definition_of_name_in_module(containing_module,
|
||||||
name,
|
name,
|
||||||
namespace,
|
namespace,
|
||||||
|
@ -4969,7 +4993,7 @@ impl Resolver {
|
||||||
Some(def) => {
|
Some(def) => {
|
||||||
// Write the result into the def map.
|
// Write the result into the def map.
|
||||||
debug!("(resolving expr) resolved `%s`",
|
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
|
// First-class methods are not supported yet; error
|
||||||
// out here.
|
// out here.
|
||||||
|
@ -4989,8 +5013,7 @@ impl Resolver {
|
||||||
self.record_def(expr.id, def);
|
self.record_def(expr.id, def);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let wrong_name = self.idents_to_str(
|
let wrong_name = self.path_idents_to_str(path);
|
||||||
path.idents);
|
|
||||||
if self.name_exists_in_scope_struct(wrong_name) {
|
if self.name_exists_in_scope_struct(wrong_name) {
|
||||||
self.resolve_error(expr.span,
|
self.resolve_error(expr.span,
|
||||||
fmt!("unresolved name `%s`. \
|
fmt!("unresolved name `%s`. \
|
||||||
|
@ -5066,7 +5089,7 @@ impl Resolver {
|
||||||
self.resolve_error(
|
self.resolve_error(
|
||||||
path.span,
|
path.span,
|
||||||
fmt!("`%s` does not name a structure",
|
fmt!("`%s` does not name a structure",
|
||||||
self.idents_to_str(path.idents)));
|
self.path_idents_to_str(path)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -559,7 +559,9 @@ fn const_expr_unadjusted(cx: @mut CrateContext, e: &ast::expr) -> ValueRef {
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
ast::expr_path(ref pth) => {
|
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;
|
let tcx = cx.tcx;
|
||||||
match tcx.def_map.find(&e.id) {
|
match tcx.def_map.find(&e.id) {
|
||||||
Some(&ast::def_fn(def_id, _purity)) => {
|
Some(&ast::def_fn(def_id, _purity)) => {
|
||||||
|
|
|
@ -63,7 +63,6 @@ use middle::typeck::rscope::RegionParamNames;
|
||||||
use middle::typeck::lookup_def_tcx;
|
use middle::typeck::lookup_def_tcx;
|
||||||
|
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::vec;
|
|
||||||
use syntax::abi::AbiSet;
|
use syntax::abi::AbiSet;
|
||||||
use syntax::{ast, ast_util};
|
use syntax::{ast, ast_util};
|
||||||
use syntax::codemap::span;
|
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
|
// If the type is parameterized by the this region, then replace this
|
||||||
// region with the current anon region binding (in other words,
|
// region with the current anon region binding (in other words,
|
||||||
// whatever & would get replaced with).
|
// 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) => {
|
(&None, &None) => {
|
||||||
opt_vec::Empty
|
opt_vec::Empty
|
||||||
}
|
}
|
||||||
|
@ -169,20 +169,34 @@ fn ast_path_substs<AC:AstConv,RS:region_scope + Clone + 'static>(
|
||||||
}
|
}
|
||||||
(&Some(_), &Some(_)) => {
|
(&Some(_), &Some(_)) => {
|
||||||
opt_vec::with(
|
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.
|
// 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(
|
this.tcx().sess.span_fatal(
|
||||||
path.span,
|
path.span,
|
||||||
fmt!("wrong number of type arguments: expected %u but found %u",
|
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,
|
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,
|
path: &ast::Path,
|
||||||
flags: uint) {
|
flags: uint) {
|
||||||
if (flags & NO_TPS) != 0u {
|
if (flags & NO_TPS) != 0u {
|
||||||
if path.types.len() > 0u {
|
if !path.segments.iter().all(|s| s.types.is_empty()) {
|
||||||
tcx.sess.span_err(
|
tcx.sess.span_err(
|
||||||
path.span,
|
path.span,
|
||||||
"type parameters are not allowed on this type");
|
"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 (flags & NO_REGIONS) != 0u {
|
||||||
if path.rp.is_some() {
|
if path.segments.last().lifetime.is_some() {
|
||||||
tcx.sess.span_err(
|
tcx.sess.span_err(
|
||||||
path.span,
|
path.span,
|
||||||
"region parameters are not allowed on this type");
|
"region parameters are not allowed on this type");
|
||||||
|
|
|
@ -3146,7 +3146,10 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
|
||||||
debug!(">>> instantiate_path");
|
debug!(">>> instantiate_path");
|
||||||
|
|
||||||
let ty_param_count = tpt.generics.type_param_defs.len();
|
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=%?",
|
debug!("tpt=%s ty_param_count=%? ty_substs_len=%?",
|
||||||
tpt.repr(fcx.tcx()),
|
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
|
// determine the region bound, using the value given by the user
|
||||||
// (if any) and otherwise using a fresh region variable
|
// (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...
|
Some(_) => { // user supplied a lifetime parameter...
|
||||||
match tpt.generics.region_param {
|
match tpt.generics.region_param {
|
||||||
None => { // ...but the type is not lifetime parameterized!
|
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.
|
Some(_) => { // ...and the type is lifetime parameterized, ok.
|
||||||
opt_vec::with(
|
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)
|
fcx.infcx().next_ty_vars(ty_param_count)
|
||||||
} else {
|
} 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),
|
let substs = substs {
|
||||||
self_ty: None,
|
regions: ty::NonerasedRegions(regions),
|
||||||
tps: tps };
|
self_ty: None,
|
||||||
|
tps: tps
|
||||||
|
};
|
||||||
fcx.write_ty_substs(node_id, tpt.ty, substs);
|
fcx.write_ty_substs(node_id, tpt.ty, substs);
|
||||||
|
|
||||||
debug!("<<<");
|
debug!("<<<");
|
||||||
|
|
|
@ -109,12 +109,21 @@ pub struct Path {
|
||||||
/// A `::foo` path, is relative to the crate root rather than current
|
/// A `::foo` path, is relative to the crate root rather than current
|
||||||
/// module (like paths in an import).
|
/// module (like paths in an import).
|
||||||
global: bool,
|
global: bool,
|
||||||
/// The segments in the path (the things separated by ::)
|
/// The segments in the path: the things separated by `::`.
|
||||||
idents: ~[ident],
|
segments: ~[PathSegment],
|
||||||
/// "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>`
|
/// A segment of a path: an identifier, an optional lifetime, and a set of
|
||||||
types: ~[Ty],
|
/// 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;
|
pub type CrateNum = int;
|
||||||
|
|
|
@ -28,8 +28,8 @@ pub fn path_name_i(idents: &[ident]) -> ~str {
|
||||||
idents.map(|i| token::interner_get(i.name)).connect("::")
|
idents.map(|i| token::interner_get(i.name)).connect("::")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path_to_ident(p: &Path) -> ident {
|
pub fn path_to_ident(path: &Path) -> ident {
|
||||||
*p.idents.last()
|
path.segments.last().identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_def(id: NodeId) -> def_id {
|
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 {
|
pub fn ident_to_path(s: span, identifier: ident) -> Path {
|
||||||
ast::Path { span: s,
|
ast::Path {
|
||||||
global: false,
|
span: s,
|
||||||
idents: ~[i],
|
global: false,
|
||||||
rp: None,
|
segments: ~[
|
||||||
types: ~[] }
|
ast::PathSegment {
|
||||||
|
identifier: identifier,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ident_to_pat(id: NodeId, s: span, i: ident) -> @pat {
|
pub fn ident_to_pat(id: NodeId, s: span, i: ident) -> @pat {
|
||||||
|
@ -420,7 +426,7 @@ impl IdVisitor {
|
||||||
impl Visitor<()> for IdVisitor {
|
impl Visitor<()> for IdVisitor {
|
||||||
fn visit_mod(&mut self,
|
fn visit_mod(&mut self,
|
||||||
module: &_mod,
|
module: &_mod,
|
||||||
_span: span,
|
_: span,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
env: ()) {
|
env: ()) {
|
||||||
(self.visit_callback)(node_id);
|
(self.visit_callback)(node_id);
|
||||||
|
|
|
@ -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],
|
pub fn check_zero_tts(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
|
||||||
name: &str) {
|
name: &str) {
|
||||||
if tts.len() != 0 {
|
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,
|
pub fn get_single_str_from_tts(cx: @ExtCtxt,
|
||||||
sp: span,
|
sp: span,
|
||||||
tts: &[ast::token_tree],
|
tts: &[ast::token_tree],
|
||||||
name: &str) -> @str {
|
name: &str)
|
||||||
|
-> @str {
|
||||||
if tts.len() != 1 {
|
if tts.len() != 1 {
|
||||||
cx.span_fatal(sp, fmt!("%s takes 1 argument.", name));
|
cx.span_fatal(sp, fmt!("%s takes 1 argument.", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
match tts[0] {
|
match tts[0] {
|
||||||
ast::tt_tok(_, token::LIT_STR(ident)) => cx.str_of(ident),
|
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -233,18 +233,31 @@ impl AstBuilder for @ExtCtxt {
|
||||||
fn path_global(&self, span: span, strs: ~[ast::ident]) -> ast::Path {
|
fn path_global(&self, span: span, strs: ~[ast::ident]) -> ast::Path {
|
||||||
self.path_all(span, true, strs, None, ~[])
|
self.path_all(span, true, strs, None, ~[])
|
||||||
}
|
}
|
||||||
fn path_all(&self, sp: span,
|
fn path_all(&self,
|
||||||
|
sp: span,
|
||||||
global: bool,
|
global: bool,
|
||||||
idents: ~[ast::ident],
|
mut idents: ~[ast::ident],
|
||||||
rp: Option<ast::Lifetime>,
|
rp: Option<ast::Lifetime>,
|
||||||
types: ~[ast::Ty])
|
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 {
|
ast::Path {
|
||||||
span: sp,
|
span: sp,
|
||||||
global: global,
|
global: global,
|
||||||
idents: idents,
|
segments: segments,
|
||||||
rp: rp,
|
|
||||||
types: types
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use ast;
|
||||||
use codemap::span;
|
use codemap::span;
|
||||||
use ext::base::*;
|
use ext::base::*;
|
||||||
use ext::base;
|
use ext::base;
|
||||||
|
use opt_vec;
|
||||||
use parse::token;
|
use parse::token;
|
||||||
use parse::token::{str_to_ident};
|
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 {
|
ast::Path {
|
||||||
span: sp,
|
span: sp,
|
||||||
global: false,
|
global: false,
|
||||||
idents: ~[res],
|
segments: ~[
|
||||||
rp: None,
|
ast::PathSegment {
|
||||||
types: ~[],
|
identifier: res,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
span: sp,
|
span: sp,
|
||||||
|
|
|
@ -19,6 +19,7 @@ use codemap;
|
||||||
use codemap::{span, spanned, ExpnInfo, NameAndSpan};
|
use codemap::{span, spanned, ExpnInfo, NameAndSpan};
|
||||||
use ext::base::*;
|
use ext::base::*;
|
||||||
use fold::*;
|
use fold::*;
|
||||||
|
use opt_vec;
|
||||||
use parse;
|
use parse;
|
||||||
use parse::{parse_item_from_source_str};
|
use parse::{parse_item_from_source_str};
|
||||||
use parse::token;
|
use parse::token;
|
||||||
|
@ -42,13 +43,13 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
|
||||||
match (*mac).node {
|
match (*mac).node {
|
||||||
// Token-tree macros:
|
// Token-tree macros:
|
||||||
mac_invoc_tt(ref pth, ref tts) => {
|
mac_invoc_tt(ref pth, ref tts) => {
|
||||||
if (pth.idents.len() > 1u) {
|
if (pth.segments.len() > 1u) {
|
||||||
cx.span_fatal(
|
cx.span_fatal(
|
||||||
pth.span,
|
pth.span,
|
||||||
fmt!("expected macro name without module \
|
fmt!("expected macro name without module \
|
||||||
separators"));
|
separators"));
|
||||||
}
|
}
|
||||||
let extname = &pth.idents[0];
|
let extname = &pth.segments[0].identifier;
|
||||||
let extnamestr = ident_to_str(extname);
|
let extnamestr = ident_to_str(extname);
|
||||||
// leaving explicit deref here to highlight unbox op:
|
// leaving explicit deref here to highlight unbox op:
|
||||||
match (*extsbox).find(&extname.name) {
|
match (*extsbox).find(&extname.name) {
|
||||||
|
@ -143,9 +144,13 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
|
||||||
ast::Path {
|
ast::Path {
|
||||||
span: span,
|
span: span,
|
||||||
global: false,
|
global: false,
|
||||||
idents: ~[ident],
|
segments: ~[
|
||||||
rp: None,
|
ast::PathSegment {
|
||||||
types: ~[]
|
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")
|
_ => 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 extnamestr = ident_to_str(extname);
|
||||||
let expanded = match (*extsbox).find(&extname.name) {
|
let expanded = match (*extsbox).find(&extname.name) {
|
||||||
None => cx.span_fatal(pth.span,
|
None => cx.span_fatal(pth.span,
|
||||||
|
@ -459,13 +464,13 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
|
||||||
}
|
}
|
||||||
_ => return orig(s, sp, fld)
|
_ => return orig(s, sp, fld)
|
||||||
};
|
};
|
||||||
if (pth.idents.len() > 1u) {
|
if (pth.segments.len() > 1u) {
|
||||||
cx.span_fatal(
|
cx.span_fatal(
|
||||||
pth.span,
|
pth.span,
|
||||||
fmt!("expected macro name without module \
|
fmt!("expected macro name without module \
|
||||||
separators"));
|
separators"));
|
||||||
}
|
}
|
||||||
let extname = &pth.idents[0];
|
let extname = &pth.segments[0].identifier;
|
||||||
let extnamestr = ident_to_str(extname);
|
let extnamestr = ident_to_str(extname);
|
||||||
let (fully_expanded, sp) = match (*extsbox).find(&extname.name) {
|
let (fully_expanded, sp) = match (*extsbox).find(&extname.name) {
|
||||||
None =>
|
None =>
|
||||||
|
@ -534,10 +539,14 @@ impl Visitor<()> for NewNameFinderContext {
|
||||||
// a path of length one:
|
// a path of length one:
|
||||||
&ast::Path {
|
&ast::Path {
|
||||||
global: false,
|
global: false,
|
||||||
idents: [id],
|
|
||||||
span: _,
|
span: _,
|
||||||
rp: _,
|
segments: [
|
||||||
types: _
|
ast::PathSegment {
|
||||||
|
identifier: id,
|
||||||
|
lifetime: _,
|
||||||
|
types: _
|
||||||
|
}
|
||||||
|
]
|
||||||
} => self.ident_accumulator.push(id),
|
} => self.ident_accumulator.push(id),
|
||||||
// I believe these must be enums...
|
// I believe these must be enums...
|
||||||
_ => ()
|
_ => ()
|
||||||
|
|
|
@ -16,8 +16,8 @@ use codemap::{BytePos, mk_sp};
|
||||||
use codemap;
|
use codemap;
|
||||||
use parse::lexer::*; //resolve bug?
|
use parse::lexer::*; //resolve bug?
|
||||||
use parse::ParseSess;
|
use parse::ParseSess;
|
||||||
use parse::parser::Parser;
|
|
||||||
use parse::attr::parser_attr;
|
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::{Token, EOF, to_str, nonterminal, get_ident_interner, ident_to_str};
|
||||||
use parse::token;
|
use parse::token;
|
||||||
|
|
||||||
|
@ -430,7 +430,9 @@ pub fn parse_nt(p: &Parser, name: &str) -> nonterminal {
|
||||||
_ => p.fatal(~"expected ident, found "
|
_ => p.fatal(~"expected ident, found "
|
||||||
+ token::to_str(get_ident_interner(), p.token))
|
+ 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)),
|
"attr" => token::nt_attr(@p.parse_attribute(false)),
|
||||||
"tt" => {
|
"tt" => {
|
||||||
*p.quote_depth += 1u; //but in theory, non-quoted tts might be useful
|
*p.quote_depth += 1u; //but in theory, non-quoted tts might be useful
|
||||||
|
|
|
@ -765,9 +765,11 @@ fn noop_fold_path(p: &Path, fld: @ast_fold) -> Path {
|
||||||
ast::Path {
|
ast::Path {
|
||||||
span: fld.new_span(p.span),
|
span: fld.new_span(p.span),
|
||||||
global: p.global,
|
global: p.global,
|
||||||
idents: p.idents.map(|x| fld.fold_ident(*x)),
|
segments: p.segments.map(|segment| ast::PathSegment {
|
||||||
rp: p.rp,
|
identifier: fld.fold_ident(segment.identifier),
|
||||||
types: p.types.map(|x| fld.fold_ty(x)),
|
lifetime: segment.lifetime,
|
||||||
|
types: segment.types.map(|typ| fld.fold_ty(typ)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>)) {
|
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>)) {
|
pub fn visit_pat<E:Clone>(p: &pat, (e, v): (E, vt<E>)) {
|
||||||
|
|
|
@ -361,27 +361,47 @@ mod test {
|
||||||
span{lo:BytePos(a),hi:BytePos(b),expn_info:None}
|
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"),
|
assert_eq!(string_to_expr(@"a"),
|
||||||
@ast::expr{id:1,
|
@ast::expr{
|
||||||
node:ast::expr_path(ast::Path {span:sp(0,1),
|
id: 1,
|
||||||
global:false,
|
node: ast::expr_path(ast::Path {
|
||||||
idents:~[str_to_ident("a")],
|
span: sp(0, 1),
|
||||||
rp:None,
|
global: false,
|
||||||
types:~[]}),
|
segments: ~[
|
||||||
span:sp(0,1)})
|
ast::PathSegment {
|
||||||
|
identifier: str_to_ident("a"),
|
||||||
|
lifetime: None,
|
||||||
|
types: ~[],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
span: sp(0, 1)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test] fn path_exprs_2 () {
|
#[test] fn path_exprs_2 () {
|
||||||
assert_eq!(string_to_expr(@"::a::b"),
|
assert_eq!(string_to_expr(@"::a::b"),
|
||||||
@ast::expr{id:1,
|
@ast::expr {
|
||||||
node:ast::expr_path(
|
id:1,
|
||||||
ast::Path {span:sp(0,6),
|
node: ast::expr_path(ast::Path {
|
||||||
global:true,
|
span: sp(0, 6),
|
||||||
idents:strs_to_idents(~["a","b"]),
|
global: true,
|
||||||
rp:None,
|
segments: ~[
|
||||||
types:~[]}),
|
ast::PathSegment {
|
||||||
span:sp(0,6)})
|
identifier: str_to_ident("a"),
|
||||||
|
lifetime: None,
|
||||||
|
types: ~[],
|
||||||
|
},
|
||||||
|
ast::PathSegment {
|
||||||
|
identifier: str_to_ident("b"),
|
||||||
|
lifetime: None,
|
||||||
|
types: ~[],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
span: sp(0, 6))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[should_fail]
|
#[should_fail]
|
||||||
|
@ -420,32 +440,43 @@ mod test {
|
||||||
|
|
||||||
#[test] fn ret_expr() {
|
#[test] fn ret_expr() {
|
||||||
assert_eq!(string_to_expr(@"return d"),
|
assert_eq!(string_to_expr(@"return d"),
|
||||||
@ast::expr{id:2,
|
@ast::expr{
|
||||||
node:ast::expr_ret(
|
id:2,
|
||||||
Some(@ast::expr{id:1,
|
node:ast::expr_ret(Some(@ast::expr{
|
||||||
node:ast::expr_path(
|
id:1,
|
||||||
ast::Path{span:sp(7,8),
|
node:ast::expr_path(ast::Path{
|
||||||
global:false,
|
span: sp(7, 8),
|
||||||
idents:~[str_to_ident("d")],
|
global: false,
|
||||||
rp:None,
|
segments: ~[
|
||||||
types:~[]
|
ast::PathSegment {
|
||||||
}),
|
identifier: str_to_ident("d"),
|
||||||
span:sp(7,8)})),
|
lifetime: None,
|
||||||
span:sp(0,8)})
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
span:sp(7,8)
|
||||||
|
})),
|
||||||
|
span:sp(0,8)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test] fn parse_stmt_1 () {
|
#[test] fn parse_stmt_1 () {
|
||||||
assert_eq!(string_to_stmt(@"b;"),
|
assert_eq!(string_to_stmt(@"b;"),
|
||||||
@spanned{
|
@spanned{
|
||||||
node: ast::stmt_expr(@ast::expr{
|
node: ast::stmt_expr(@ast::expr {
|
||||||
id: 1,
|
id: 1,
|
||||||
node: ast::expr_path(
|
node: ast::expr_path(ast::Path {
|
||||||
ast::Path{
|
span:sp(0,1),
|
||||||
span:sp(0,1),
|
global:false,
|
||||||
global:false,
|
segments: ~[
|
||||||
idents:~[str_to_ident("b")],
|
ast::PathSegment {
|
||||||
rp:None,
|
identifier: str_to_ident("b"),
|
||||||
types: ~[]}),
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
span: sp(0,1)},
|
span: sp(0,1)},
|
||||||
2), // fixme
|
2), // fixme
|
||||||
span: sp(0,1)})
|
span: sp(0,1)})
|
||||||
|
@ -460,15 +491,20 @@ mod test {
|
||||||
let parser = string_to_parser(@"b");
|
let parser = string_to_parser(@"b");
|
||||||
assert_eq!(parser.parse_pat(),
|
assert_eq!(parser.parse_pat(),
|
||||||
@ast::pat{id:1, // fixme
|
@ast::pat{id:1, // fixme
|
||||||
node: ast::pat_ident(ast::bind_infer,
|
node: ast::pat_ident(
|
||||||
ast::Path{
|
ast::bind_infer,
|
||||||
span:sp(0,1),
|
ast::Path {
|
||||||
global:false,
|
span:sp(0,1),
|
||||||
idents:~[str_to_ident("b")],
|
global:false,
|
||||||
rp: None,
|
segments: ~[
|
||||||
types: ~[]},
|
ast::PathSegment {
|
||||||
None // no idea
|
identifier: str_to_ident("b"),
|
||||||
),
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
None /* no idea */),
|
||||||
span: sp(0,1)});
|
span: sp(0,1)});
|
||||||
parser_done(parser);
|
parser_done(parser);
|
||||||
}
|
}
|
||||||
|
@ -483,21 +519,33 @@ mod test {
|
||||||
span:sp(4,4), // this is bizarre...
|
span:sp(4,4), // this is bizarre...
|
||||||
// check this in the original parser?
|
// check this in the original parser?
|
||||||
global:false,
|
global:false,
|
||||||
idents:~[str_to_ident("int")],
|
segments: ~[
|
||||||
rp: None,
|
ast::PathSegment {
|
||||||
types: ~[]},
|
identifier:
|
||||||
None, 2),
|
str_to_ident("int"),
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}, None, 2),
|
||||||
span:sp(4,7)},
|
span:sp(4,7)},
|
||||||
pat: @ast::pat{id:1,
|
pat: @ast::pat{id:1,
|
||||||
node: ast::pat_ident(ast::bind_infer,
|
node: ast::pat_ident(
|
||||||
ast::Path{
|
ast::bind_infer,
|
||||||
span:sp(0,1),
|
ast::Path {
|
||||||
global:false,
|
span:sp(0,1),
|
||||||
idents:~[str_to_ident("b")],
|
global:false,
|
||||||
rp: None,
|
segments: ~[
|
||||||
types: ~[]},
|
ast::PathSegment {
|
||||||
None // no idea
|
identifier:
|
||||||
),
|
str_to_ident("b"),
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
None // no idea
|
||||||
|
),
|
||||||
span: sp(0,1)},
|
span: sp(0,1)},
|
||||||
id: 4 // fixme
|
id: 4 // fixme
|
||||||
})
|
})
|
||||||
|
@ -519,23 +567,37 @@ mod test {
|
||||||
node: ast::ty_path(ast::Path{
|
node: ast::ty_path(ast::Path{
|
||||||
span:sp(10,13),
|
span:sp(10,13),
|
||||||
global:false,
|
global:false,
|
||||||
idents:~[str_to_ident("int")],
|
segments: ~[
|
||||||
rp: None,
|
ast::PathSegment {
|
||||||
types: ~[]},
|
identifier:
|
||||||
None, 2),
|
str_to_ident("int"),
|
||||||
span:sp(10,13)},
|
lifetime: None,
|
||||||
pat: @ast::pat{id:1, // fixme
|
types: opt_vec::Empty,
|
||||||
node: ast::pat_ident(
|
}
|
||||||
ast::bind_infer,
|
],
|
||||||
ast::Path{
|
}, None, 2),
|
||||||
span:sp(6,7),
|
span:sp(10,13)
|
||||||
global:false,
|
},
|
||||||
idents:~[str_to_ident("b")],
|
pat: @ast::pat {
|
||||||
rp: None,
|
id:1, // fixme
|
||||||
types: ~[]},
|
node: ast::pat_ident(
|
||||||
None // no idea
|
ast::bind_infer,
|
||||||
),
|
ast::Path {
|
||||||
span: sp(6,7)},
|
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
|
id: 4 // fixme
|
||||||
}],
|
}],
|
||||||
output: ast::Ty{id:5, // fixme
|
output: ast::Ty{id:5, // fixme
|
||||||
|
@ -558,9 +620,18 @@ mod test {
|
||||||
ast::Path{
|
ast::Path{
|
||||||
span:sp(17,18),
|
span:sp(17,18),
|
||||||
global:false,
|
global:false,
|
||||||
idents:~[str_to_ident("b")],
|
segments: ~[
|
||||||
rp:None,
|
ast::PathSegment {
|
||||||
types: ~[]}),
|
identifier:
|
||||||
|
str_to_ident(
|
||||||
|
"b"),
|
||||||
|
lifetime:
|
||||||
|
None,
|
||||||
|
types:
|
||||||
|
opt_vec::Empty
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
span: sp(17,18)},
|
span: sp(17,18)},
|
||||||
7), // fixme
|
7), // fixme
|
||||||
span: sp(17,18)}],
|
span: sp(17,18)}],
|
||||||
|
|
|
@ -97,6 +97,37 @@ enum restriction {
|
||||||
type arg_or_capture_item = Either<arg, ()>;
|
type arg_or_capture_item = Either<arg, ()>;
|
||||||
type item_info = (ident, item_, Option<~[Attribute]>);
|
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 {
|
pub enum item_or_view_item {
|
||||||
// Indicates a failure to parse any kind of item. The attributes are
|
// Indicates a failure to parse any kind of item. The attributes are
|
||||||
// returned.
|
// returned.
|
||||||
|
@ -1108,7 +1139,10 @@ impl Parser {
|
||||||
} else if *self.token == token::MOD_SEP
|
} else if *self.token == token::MOD_SEP
|
||||||
|| is_ident_or_path(self.token) {
|
|| is_ident_or_path(self.token) {
|
||||||
// NAMED TYPE
|
// NAMED TYPE
|
||||||
let (path, bounds) = self.parse_type_path();
|
let PathAndBounds {
|
||||||
|
path,
|
||||||
|
bounds
|
||||||
|
} = self.parse_path(LifetimeAndTypesAndBounds);
|
||||||
ty_path(path, bounds, self.get_id())
|
ty_path(path, bounds, self.get_id())
|
||||||
} else {
|
} else {
|
||||||
self.fatal(fmt!("expected type, found token %?",
|
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
|
/// Parses a path and optional type parameter bounds, depending on the
|
||||||
// with ::, and a span.
|
/// mode. The `mode` parameter determines whether lifetimes, types, and/or
|
||||||
pub fn parse_path(&self) -> (~[ast::ident],bool,span) {
|
/// 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 lo = self.span.lo;
|
||||||
let is_global = self.eat(&token::MOD_SEP);
|
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
|
// Parse any number of segments and bound sets. A segment is an
|
||||||
pub fn parse_path_non_global(&self) -> (~[ast::ident],span) {
|
// identifier followed by an optional lifetime and a set of types.
|
||||||
let lo = self.span.lo;
|
// A bound set is a set of type parameter bounds.
|
||||||
let mut ids = ~[];
|
let mut segments = ~[];
|
||||||
// must be at least one to begin:
|
|
||||||
ids.push(self.parse_ident());
|
|
||||||
loop {
|
loop {
|
||||||
|
// First, parse an identifier.
|
||||||
match *self.token {
|
match *self.token {
|
||||||
token::MOD_SEP => {
|
token::IDENT(*) => {}
|
||||||
let is_ident = do self.look_ahead(1) |t| {
|
_ => break,
|
||||||
match *t {
|
|
||||||
token::IDENT(*) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if is_ident {
|
|
||||||
self.bump();
|
|
||||||
ids.push(self.parse_ident());
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => break
|
|
||||||
}
|
}
|
||||||
}
|
let identifier = self.parse_ident();
|
||||||
(ids, mk_sp(lo, self.last_span.hi))
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse a path that doesn't have type parameters attached
|
// Next, parse a colon and bounded type parameters, if applicable.
|
||||||
pub fn parse_path_without_tps(&self) -> ast::Path {
|
let bound_set = if mode == LifetimeAndTypesAndBounds {
|
||||||
maybe_whole!(deref self, nt_path);
|
self.parse_optional_ty_param_bounds()
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
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:
|
// Parse the `<` before the lifetime and types, if applicable.
|
||||||
let (lifetimes, tps) = self.parse_generic_values();
|
let (any_lifetime_or_types, optional_lifetime, types) =
|
||||||
let hi = self.span.lo;
|
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) {
|
// Parse type parameters.
|
||||||
(&Some(_), _) => rp_slash,
|
let mut types = opt_vec::Empty;
|
||||||
(&None, v) => {
|
let mut need_comma = optional_lifetime.is_some();
|
||||||
if v.len() == 0 {
|
loop {
|
||||||
None
|
// We're done if we see a `>`.
|
||||||
} else if v.len() == 1 {
|
match *self.token {
|
||||||
Some(*v.get(0))
|
token::GT | token::BINOP(token::SHR) => {
|
||||||
} else {
|
self.expect_gt();
|
||||||
self.fatal(fmt!("Expected at most one \
|
break
|
||||||
lifetime name (for now)"));
|
}
|
||||||
|
_ => {} // 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 {
|
path_and_bounds
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// parses 0 or 1 lifetime
|
/// parses 0 or 1 lifetime
|
||||||
|
@ -1789,7 +1839,7 @@ impl Parser {
|
||||||
} else if *self.token == token::MOD_SEP ||
|
} else if *self.token == token::MOD_SEP ||
|
||||||
is_ident(&*self.token) && !self.is_keyword(keywords::True) &&
|
is_ident(&*self.token) && !self.is_keyword(keywords::True) &&
|
||||||
!self.is_keyword(keywords::False) {
|
!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
|
// `!`, as an operator, is prefix, so we know this isn't that
|
||||||
if *self.token == token::NOT {
|
if *self.token == token::NOT {
|
||||||
|
@ -2880,7 +2930,8 @@ impl Parser {
|
||||||
let val = self.parse_literal_maybe_minus();
|
let val = self.parse_literal_maybe_minus();
|
||||||
if self.eat(&token::DOTDOT) {
|
if self.eat(&token::DOTDOT) {
|
||||||
let end = if is_ident_or_path(tok) {
|
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;
|
let hi = self.span.hi;
|
||||||
self.mk_expr(lo, hi, expr_path(path))
|
self.mk_expr(lo, hi, expr_path(path))
|
||||||
} else {
|
} else {
|
||||||
|
@ -2909,7 +2960,7 @@ impl Parser {
|
||||||
let end = self.parse_expr_res(RESTRICT_NO_BAR_OP);
|
let end = self.parse_expr_res(RESTRICT_NO_BAR_OP);
|
||||||
pat = pat_range(start, end);
|
pat = pat_range(start, end);
|
||||||
} else if is_plain_ident(&*self.token) && !can_be_enum_or_struct {
|
} 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;
|
let sub;
|
||||||
if self.eat(&token::AT) {
|
if self.eat(&token::AT) {
|
||||||
// parse foo @ pat
|
// parse foo @ pat
|
||||||
|
@ -2921,7 +2972,8 @@ impl Parser {
|
||||||
pat = pat_ident(bind_infer, name, sub);
|
pat = pat_ident(bind_infer, name, sub);
|
||||||
} else {
|
} else {
|
||||||
// parse an enum pat
|
// parse an enum pat
|
||||||
let enum_path = self.parse_path_with_tps(true);
|
let enum_path = self.parse_path(LifetimeAndTypesWithColons)
|
||||||
|
.path;
|
||||||
match *self.token {
|
match *self.token {
|
||||||
token::LBRACE => {
|
token::LBRACE => {
|
||||||
self.bump();
|
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
|
// it could still be either an enum
|
||||||
// or an identifier pattern, resolve
|
// or an identifier pattern, resolve
|
||||||
// will sort it out:
|
// will sort it out:
|
||||||
|
@ -2992,7 +3044,7 @@ impl Parser {
|
||||||
"expected identifier, found path");
|
"expected identifier, found path");
|
||||||
}
|
}
|
||||||
// why a path here, and not just an identifier?
|
// 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) {
|
let sub = if self.eat(&token::AT) {
|
||||||
Some(self.parse_pat())
|
Some(self.parse_pat())
|
||||||
} else {
|
} else {
|
||||||
|
@ -3109,7 +3161,7 @@ impl Parser {
|
||||||
|
|
||||||
// Potential trouble: if we allow macros with paths instead of
|
// Potential trouble: if we allow macros with paths instead of
|
||||||
// idents, we'd need to look ahead past the whole path here...
|
// 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();
|
self.bump();
|
||||||
|
|
||||||
let id = if *self.token == token::LPAREN {
|
let id = if *self.token == token::LPAREN {
|
||||||
|
@ -3785,7 +3837,7 @@ impl Parser {
|
||||||
// parse a::B<~str,int>
|
// parse a::B<~str,int>
|
||||||
fn parse_trait_ref(&self) -> trait_ref {
|
fn parse_trait_ref(&self) -> trait_ref {
|
||||||
ast::trait_ref {
|
ast::trait_ref {
|
||||||
path: self.parse_path_with_tps(false),
|
path: self.parse_path(LifetimeAndTypesWithoutColons).path,
|
||||||
ref_id: self.get_id(),
|
ref_id: self.get_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4701,7 +4753,7 @@ impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// item macro.
|
// item macro.
|
||||||
let pth = self.parse_path_without_tps();
|
let pth = self.parse_path(NoTypesAllowed).path;
|
||||||
self.expect(&token::NOT);
|
self.expect(&token::NOT);
|
||||||
|
|
||||||
// a 'special' identifier (like what `macro_rules!` uses)
|
// a 'special' identifier (like what `macro_rules!` uses)
|
||||||
|
@ -4785,11 +4837,17 @@ impl Parser {
|
||||||
let id = self.parse_ident();
|
let id = self.parse_ident();
|
||||||
path.push(id);
|
path.push(id);
|
||||||
}
|
}
|
||||||
let path = ast::Path { span: mk_sp(lo, self.span.hi),
|
let path = ast::Path {
|
||||||
global: false,
|
span: mk_sp(lo, self.span.hi),
|
||||||
idents: path,
|
global: false,
|
||||||
rp: None,
|
segments: path.consume_iter().transform(|identifier| {
|
||||||
types: ~[] };
|
ast::PathSegment {
|
||||||
|
identifier: identifier,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
};
|
||||||
return @spanned(lo, self.span.hi,
|
return @spanned(lo, self.span.hi,
|
||||||
view_path_simple(first_ident,
|
view_path_simple(first_ident,
|
||||||
path,
|
path,
|
||||||
|
@ -4815,11 +4873,17 @@ impl Parser {
|
||||||
seq_sep_trailing_allowed(token::COMMA),
|
seq_sep_trailing_allowed(token::COMMA),
|
||||||
|p| p.parse_path_list_ident()
|
|p| p.parse_path_list_ident()
|
||||||
);
|
);
|
||||||
let path = ast::Path { span: mk_sp(lo, self.span.hi),
|
let path = ast::Path {
|
||||||
global: false,
|
span: mk_sp(lo, self.span.hi),
|
||||||
idents: path,
|
global: false,
|
||||||
rp: None,
|
segments: path.consume_iter().transform(|identifier| {
|
||||||
types: ~[] };
|
ast::PathSegment {
|
||||||
|
identifier: identifier,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
};
|
||||||
return @spanned(lo, self.span.hi,
|
return @spanned(lo, self.span.hi,
|
||||||
view_path_list(path, idents, self.get_id()));
|
view_path_list(path, idents, self.get_id()));
|
||||||
}
|
}
|
||||||
|
@ -4827,11 +4891,17 @@ impl Parser {
|
||||||
// foo::bar::*
|
// foo::bar::*
|
||||||
token::BINOP(token::STAR) => {
|
token::BINOP(token::STAR) => {
|
||||||
self.bump();
|
self.bump();
|
||||||
let path = ast::Path { span: mk_sp(lo, self.span.hi),
|
let path = ast::Path {
|
||||||
global: false,
|
span: mk_sp(lo, self.span.hi),
|
||||||
idents: path,
|
global: false,
|
||||||
rp: None,
|
segments: path.consume_iter().transform(|identifier| {
|
||||||
types: ~[] };
|
ast::PathSegment {
|
||||||
|
identifier: identifier,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
};
|
||||||
return @spanned(lo, self.span.hi,
|
return @spanned(lo, self.span.hi,
|
||||||
view_path_glob(path, self.get_id()));
|
view_path_glob(path, self.get_id()));
|
||||||
}
|
}
|
||||||
|
@ -4843,11 +4913,17 @@ impl Parser {
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
let last = path[path.len() - 1u];
|
let last = path[path.len() - 1u];
|
||||||
let path = ast::Path { span: mk_sp(lo, self.span.hi),
|
let path = ast::Path {
|
||||||
global: false,
|
span: mk_sp(lo, self.span.hi),
|
||||||
idents: path,
|
global: false,
|
||||||
rp: None,
|
segments: path.consume_iter().transform(|identifier| {
|
||||||
types: ~[] };
|
ast::PathSegment {
|
||||||
|
identifier: identifier,
|
||||||
|
lifetime: None,
|
||||||
|
types: opt_vec::Empty,
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
};
|
||||||
return @spanned(lo,
|
return @spanned(lo,
|
||||||
self.last_span.hi,
|
self.last_span.hi,
|
||||||
view_path_simple(last, path, self.get_id()));
|
view_path_simple(last, path, self.get_id()));
|
||||||
|
|
|
@ -1501,34 +1501,52 @@ pub fn print_for_decl(s: @ps, loc: &ast::Local, coll: &ast::expr) {
|
||||||
print_expr(s, coll);
|
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>>) {
|
opt_bounds: &Option<OptVec<ast::TyParamBound>>) {
|
||||||
maybe_print_comment(s, path.span.lo);
|
maybe_print_comment(s, path.span.lo);
|
||||||
if path.global { word(s.s, "::"); }
|
if path.global {
|
||||||
let mut first = true;
|
word(s.s, "::");
|
||||||
for id in path.idents.iter() {
|
|
||||||
if first { first = false; } else { word(s.s, "::"); }
|
|
||||||
print_ident(s, *id);
|
|
||||||
}
|
}
|
||||||
do opt_bounds.map |bounds| {
|
|
||||||
print_bounds(s, bounds, true);
|
|
||||||
};
|
|
||||||
if path.rp.is_some() || !path.types.is_empty() {
|
|
||||||
if colons_before_params { word(s.s, "::"); }
|
|
||||||
|
|
||||||
if path.rp.is_some() || !path.types.is_empty() {
|
let mut first = true;
|
||||||
word(s.s, "<");
|
for (i, segment) in path.segments.iter().enumerate() {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
word(s.s, "::")
|
||||||
|
}
|
||||||
|
|
||||||
for r in path.rp.iter() {
|
print_ident(s, segment.identifier);
|
||||||
print_lifetime(s, r);
|
|
||||||
if !path.types.is_empty() {
|
if segment.lifetime.is_some() || !segment.types.is_empty() {
|
||||||
word_space(s, ",");
|
// 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) {
|
pub fn print_view_path(s: @ps, vp: &ast::view_path) {
|
||||||
match vp.node {
|
match vp.node {
|
||||||
ast::view_path_simple(ident, ref path, _) => {
|
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);
|
print_ident(s, ident);
|
||||||
space(s.s);
|
space(s.s);
|
||||||
word_space(s, "=");
|
word_space(s, "=");
|
||||||
|
@ -1899,8 +1917,9 @@ pub fn print_arg(s: @ps, input: &ast::arg) {
|
||||||
_ => {
|
_ => {
|
||||||
match input.pat.node {
|
match input.pat.node {
|
||||||
ast::pat_ident(_, ref path, _) if
|
ast::pat_ident(_, ref path, _) if
|
||||||
path.idents.len() == 1 &&
|
path.segments.len() == 1 &&
|
||||||
path.idents[0] == parse::token::special_idents::invalid => {
|
path.segments[0].identifier ==
|
||||||
|
parse::token::special_idents::invalid => {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -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) {
|
pub fn walk_path<E:Clone, V:Visitor<E>>(visitor: &mut V, path: &Path, env: E) {
|
||||||
for typ in path.types.iter() {
|
for segment in path.segments.iter() {
|
||||||
visitor.visit_ty(typ, env.clone())
|
for typ in path.types.iter() {
|
||||||
|
visitor.visit_ty(typ, env.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue