This is a rebase of my approved pull request from ... the end of June? It introduces hygiene for let-bound variables.
add_bytes_to_bits_tuple, FixedBuffer, FixedBuffer128, FixedBuffer64, StandardPadding};
use digest::Digest;
-
-// Sha-512 and Sha-256 use basically the same calculations which are implemented by these macros.
-// Inlining the calculations seems to result in better generated code.
-macro_rules! schedule_round( ($t:expr) => (
- W[$t] = sigma1(W[$t - 2]) + W[$t - 7] + sigma0(W[$t - 15]) + W[$t - 16];
- )
-)
-
-macro_rules! sha2_round(
- ($A:ident, $B:ident, $C:ident, $D:ident,
- $E:ident, $F:ident, $G:ident, $H:ident, $K:ident, $t:expr) => (
- {
- $H += sum1($E) + ch($E, $F, $G) + $K[$t] + W[$t];
- $D += $H;
- $H += sum0($A) + maj($A, $B, $C);
- }
- )
-)
-
-
-// A structure that represents that state of a digest computation for the SHA-2 512 family of digest
-// functions
+// A structure that represents that state of a digest computation for the SHA-2 512 family
+// of digest functions
struct Engine512State {
H0: u64,
H1: u64,
let mut W = [0u64, ..80];
+ // Sha-512 and Sha-256 use basically the same calculations which are implemented by
+ // these macros. Inlining the calculations seems to result in better generated code.
+ macro_rules! schedule_round( ($t:expr) => (
+ W[$t] = sigma1(W[$t - 2]) + W[$t - 7] + sigma0(W[$t - 15]) + W[$t - 16];
+ )
+ )
+
+ macro_rules! sha2_round(
+ ($A:ident, $B:ident, $C:ident, $D:ident,
+ $E:ident, $F:ident, $G:ident, $H:ident, $K:ident, $t:expr) => (
+ {
+ $H += sum1($E) + ch($E, $F, $G) + $K[$t] + W[$t];
+ $D += $H;
+ $H += sum0($A) + maj($A, $B, $C);
+ }
+ )
+ )
+
+
read_u64v_be(W.mut_slice(0, 16), data);
// Putting the message schedule inside the same loop as the round calculations allows for
let mut W = [0u32, ..64];
+ // Sha-512 and Sha-256 use basically the same calculations which are implemented
+ // by these macros. Inlining the calculations seems to result in better generated code.
+ macro_rules! schedule_round( ($t:expr) => (
+ W[$t] = sigma1(W[$t - 2]) + W[$t - 7] + sigma0(W[$t - 15]) + W[$t - 16];
+ )
+ )
+
+ macro_rules! sha2_round(
+ ($A:ident, $B:ident, $C:ident, $D:ident,
+ $E:ident, $F:ident, $G:ident, $H:ident, $K:ident, $t:expr) => (
+ {
+ $H += sum1($E) + ch($E, $F, $G) + $K[$t] + W[$t];
+ $D += $H;
+ $H += sum0($A) + maj($A, $B, $C);
+ }
+ )
+ )
+
+
read_u32v_be(W.mut_slice(0, 16), data);
// Putting the message schedule inside the same loop as the round calculations allows for
match fname {
mc::NamedField(ref fname) => {
out.push_char('.');
- out.push_str(token::ident_to_str(fname));
+ out.push_str(token::interner_get(*fname));
}
mc::PositionalField(idx) => {
out.push_char('#'); // invent a notation here
fn find_item(item: @item, ctxt: @mut EntryContext, visitor: &mut EntryVisitor) {
match item.node {
item_fn(*) => {
- if item.ident == special_idents::main {
+ if item.ident.name == special_idents::main.name {
match ctxt.ast_map.find(&item.id) {
Some(&ast_map::node_item(_, path)) => {
if path.len() == 0 {
use syntax::ast;
use syntax::codemap::Span;
use syntax::print::pprust;
+use syntax::parse::token;
#[deriving(Eq)]
pub enum categorization {
#[deriving(Eq, IterBytes)]
pub enum FieldName {
- NamedField(ast::Ident),
+ NamedField(ast::Name),
PositionalField(uint)
}
@cmt_ {
id: node.id(),
span: node.span(),
- cat: cat_interior(base_cmt, InteriorField(NamedField(f_name))),
+ cat: cat_interior(base_cmt, InteriorField(NamedField(f_name.name))),
mutbl: base_cmt.mutbl.inherit(),
ty: f_ty
}
}
impl Repr for InteriorKind {
- fn repr(&self, tcx: ty::ctxt) -> ~str {
+ fn repr(&self, _tcx: ty::ctxt) -> ~str {
match *self {
- InteriorField(NamedField(fld)) => tcx.sess.str_of(fld).to_owned(),
+ InteriorField(NamedField(fld)) => token::interner_get(fld).to_owned(),
InteriorField(PositionalField(i)) => fmt!("#%?", i),
InteriorElement(_) => ~"[]",
}
// specified and (2) have a type that
// moves-by-default:
let consume_with = with_fields.iter().any(|tf| {
- !fields.iter().any(|f| f.ident == tf.ident) &&
+ !fields.iter().any(|f| f.ident.name == tf.ident.name) &&
ty::type_moves_by_default(self.tcx, tf.mt.ty)
});
fn check_field(&mut self, span: Span, id: ast::DefId, ident: ast::Ident) {
let fields = ty::lookup_struct_fields(self.tcx, id);
for field in fields.iter() {
- if field.ident != ident { loop; }
+ if field.ident.name != ident.name { loop; }
if field.vis == private {
self.tcx.sess.span_err(span, fmt!("field `%s` is private",
token::ident_to_str(&ident)));
use syntax::ast::*;
use syntax::ast;
-use syntax::ast_util::{def_id_of_def, local_def};
+use syntax::ast_util::{def_id_of_def, local_def, mtwt_resolve};
use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method};
use syntax::ast_util::{Privacy, Public, Private};
use syntax::ast_util::{variant_visibility_to_privacy, visibility_to_privacy};
use syntax::attr;
use syntax::parse::token;
-use syntax::parse::token::ident_interner;
+use syntax::parse::token::{ident_interner, interner_get};
use syntax::parse::token::special_idents;
use syntax::print::pprust::path_to_str;
use syntax::codemap::{Span, dummy_sp, BytePos};
}
// Map from the name in a pattern to its binding mode.
-pub type BindingMap = HashMap<Ident,binding_info>;
+pub type BindingMap = HashMap<Name,binding_info>;
// Trait method resolution
pub type TraitMap = HashMap<NodeId,@mut ~[DefId]>;
/// One local scope.
pub struct Rib {
- bindings: @mut HashMap<Ident, DefLike>,
+ bindings: @mut HashMap<Name, DefLike>,
self_binding: @mut Option<DefLike>,
kind: RibKind,
}
def_id: Option<DefId>,
kind: ModuleKind,
- children: @mut HashMap<Ident, @mut NameBindings>,
+ children: @mut HashMap<Name, @mut NameBindings>,
imports: @mut ~[@ImportDirective],
// The external module children of this node that were declared with
// `extern mod`.
- external_module_children: @mut HashMap<Ident, @mut Module>,
+ external_module_children: @mut HashMap<Name, @mut Module>,
// The anonymous children of this node. Anonymous children are pseudo-
// modules that are implicitly created around items contained within
anonymous_children: @mut HashMap<NodeId,@mut Module>,
// The status of resolving each import in this module.
- import_resolutions: @mut HashMap<Ident, @mut ImportResolution>,
+ import_resolutions: @mut HashMap<Name, @mut ImportResolution>,
// The number of unresolved globs that this module exports.
glob_count: uint,
/// Interns the names of the primitive types.
pub struct PrimitiveTypeTable {
- primitive_types: HashMap<Ident,prim_ty>,
+ primitive_types: HashMap<Name,prim_ty>,
}
impl PrimitiveTypeTable {
pub fn intern(&mut self,
string: &str,
primitive_type: prim_ty) {
- let ident = token::str_to_ident(string);
- self.primitive_types.insert(ident, primitive_type);
+ self.primitive_types.insert(token::intern(string), primitive_type);
}
}
graph_root: @mut NameBindings,
- method_map: @mut HashMap<Ident, HashSet<DefId>>,
+ method_map: @mut HashMap<Name, HashSet<DefId>>,
structs: HashSet<DefId>,
// The number of imports that are currently unresolved.
// Add or reuse the child.
let new_parent = ModuleReducedGraphParent(module_);
- match module_.children.find(&name) {
+ match module_.children.find(&name.name) {
None => {
let child = @mut NameBindings();
- module_.children.insert(name, child);
+ module_.children.insert(name.name, child);
return (child, new_parent);
}
Some(&child) => {
} if path.segments.len() == 1 => {
let name = path_to_ident(path);
- let new_parent = match parent.children.find(&name) {
+ let new_parent = match parent.children.find(&name.name) {
// It already exists
Some(&child) if child.get_module_if_available()
.is_some() &&
match ty_m.explicit_self.node {
sty_static => {}
_ => {
- method_names.insert(ident, ());
+ method_names.insert(ident.name, ());
}
}
}
false);
parent.external_module_children.insert(
- name,
+ name.name,
external_module);
self.build_reduced_graph_for_external_crate(
// Add it to the trait info if not static.
if explicit_self != sty_static {
- interned_method_names.insert(method_name);
+ interned_method_names.insert(method_name.name);
}
}
for name in interned_method_names.iter() {
self.idents_to_str(directive.module_path),
self.session.str_of(target));
- match module_.import_resolutions.find(&target) {
+ match module_.import_resolutions.find(&target.name) {
Some(&resolution) => {
debug!("(building import directive) bumping \
reference");
debug!("(building import directive) creating new");
let resolution = @mut ImportResolution::new(privacy, id);
resolution.outstanding_references = 1;
- module_.import_resolutions.insert(target, resolution);
+ module_.import_resolutions.insert(target.name, resolution);
}
}
}
// Search for direct children of the containing module.
self.populate_module_if_necessary(containing_module);
- match containing_module.children.find(&source) {
+ match containing_module.children.find(&source.name) {
None => {
// Continue.
}
// Now search the exported imports within the containing
// module.
- match containing_module.import_resolutions.find(&source) {
+ match containing_module.import_resolutions.find(&source.name) {
None => {
// The containing module definitely doesn't have an
// exported import with the name in question. We can
BoundResult(*) => {}
_ => {
match containing_module.external_module_children
- .find(&source) {
+ .find(&source.name) {
None => {} // Continue.
Some(module) => {
let name_bindings =
}
// We've successfully resolved the import. Write the results in.
- assert!(module_.import_resolutions.contains_key(&target));
- let import_resolution = module_.import_resolutions.get(&target);
+ assert!(module_.import_resolutions.contains_key(&target.name));
+ let import_resolution = module_.import_resolutions.get(&target.name);
match value_result {
BoundResult(target_module, name_bindings) => {
}
}
- let merge_import_resolution = |ident,
+ let merge_import_resolution = |name,
name_bindings: @mut NameBindings| {
let dest_import_resolution;
- match module_.import_resolutions.find(&ident) {
+ match module_.import_resolutions.find(&name) {
None => {
// Create a new import resolution from this child.
dest_import_resolution = @mut ImportResolution::new(privacy, id);
module_.import_resolutions.insert
- (ident, dest_import_resolution);
+ (name, dest_import_resolution);
}
Some(&existing_import_resolution) => {
dest_import_resolution = existing_import_resolution;
debug!("(resolving glob import) writing resolution `%s` in `%s` \
to `%s`, privacy=%?",
- self.session.str_of(ident),
+ interner_get(name),
self.module_to_str(containing_module),
self.module_to_str(module_),
dest_import_resolution.privacy);
// Add all children from the containing module.
self.populate_module_if_necessary(containing_module);
- for (&ident, name_bindings) in containing_module.children.iter() {
- merge_import_resolution(ident, *name_bindings);
+ for (&name, name_bindings) in containing_module.children.iter() {
+ merge_import_resolution(name, *name_bindings);
}
// Add external module children from the containing module.
- for (&ident, module) in containing_module.external_module_children.iter() {
+ for (&name, module) in containing_module.external_module_children.iter() {
let name_bindings =
@mut Resolver::create_name_bindings_from_module(*module);
- merge_import_resolution(ident, name_bindings);
+ merge_import_resolution(name, name_bindings);
}
debug!("(resolving glob import) successfully resolved import");
// The current module node is handled specially. First, check for
// its immediate children.
self.populate_module_if_necessary(module_);
- match module_.children.find(&name) {
+ match module_.children.find(&name.name) {
Some(name_bindings)
if name_bindings.defined_in_namespace(namespace) => {
return Success(Target::new(module_, *name_bindings));
// all its imports in the usual way; this is because chains of
// adjacent import statements are processed as though they mutated the
// current scope.
- match module_.import_resolutions.find(&name) {
+ match module_.import_resolutions.find(&name.name) {
None => {
// Not found; continue.
}
// Search for external modules.
if namespace == TypeNS {
- match module_.external_module_children.find(&name) {
+ match module_.external_module_children.find(&name.name) {
None => {}
Some(module) => {
let name_bindings =
}
}
- /// Resolves a "module prefix". A module prefix is one of (a) `self::`;
+ /// Resolves a "module prefix". A module prefix is one or both of (a) `self::`;
/// (b) some chain of `super::`.
+ /// grammar: (SELF MOD_SEP ) ? (SUPER MOD_SEP) *
pub fn resolve_module_prefix(@mut self,
module_: @mut Module,
module_path: &[Ident])
// First, check the direct children of the module.
self.populate_module_if_necessary(module_);
- match module_.children.find(&name) {
+ match module_.children.find(&name.name) {
Some(name_bindings)
if name_bindings.defined_in_namespace(namespace) => {
debug!("(resolving name in module) found node as child");
}
// Check the list of resolved imports.
- match module_.import_resolutions.find(&name) {
+ match module_.import_resolutions.find(&name.name) {
Some(import_resolution) => {
if import_resolution.privacy == Public &&
import_resolution.outstanding_references != 0 {
// Finally, search through external children.
if namespace == TypeNS {
- match module_.external_module_children.find(&name) {
+ match module_.external_module_children.find(&name.name) {
None => {}
Some(module) => {
let name_bindings =
pub fn add_exports_of_namebindings(@mut self,
exports2: &mut ~[Export2],
- ident: Ident,
+ name: Name,
namebindings: @mut NameBindings,
ns: Namespace,
reexport: bool) {
(Some(d), Some(Public)) => {
debug!("(computing exports) YES: %s '%s' => %?",
if reexport { ~"reexport" } else { ~"export"},
- self.session.str_of(ident),
+ interner_get(name),
def_id_of_def(d));
exports2.push(Export2 {
reexport: reexport,
- name: self.session.str_of(ident),
+ name: interner_get(name),
def_id: def_id_of_def(d)
});
}
pub fn add_exports_for_module(@mut self,
exports2: &mut ~[Export2],
module_: @mut Module) {
- for (ident, importresolution) in module_.import_resolutions.iter() {
+ for (name, importresolution) in module_.import_resolutions.iter() {
if importresolution.privacy != Public {
debug!("(computing exports) not reexporting private `%s`",
- self.session.str_of(*ident));
+ interner_get(*name));
loop;
}
let xs = [TypeNS, ValueNS];
match importresolution.target_for_namespace(*ns) {
Some(target) => {
debug!("(computing exports) maybe reexport '%s'",
- self.session.str_of(*ident));
+ interner_get(*name));
self.add_exports_of_namebindings(&mut *exports2,
- *ident,
+ *name,
target.bindings,
*ns,
true)
}
Some(name) => {
self.populate_module_if_necessary(orig_module);
- match orig_module.children.find(&name) {
+ match orig_module.children.find(&name.name) {
None => {
debug!("!!! (with scope) didn't find `%s` in `%s`",
self.session.str_of(name),
pub fn search_ribs(@mut self,
ribs: &mut ~[@Rib],
- name: Ident,
+ name: Name,
span: Span,
allow_capturing_self: AllowCapturingSelfFlag)
-> Option<DefLike> {
// Create a new rib for the self type.
let self_type_rib = @Rib::new(NormalRibKind);
self.type_ribs.push(self_type_rib);
- self_type_rib.bindings.insert(self.type_self_ident,
+ // plain insert (no renaming)
+ let name = self.type_self_ident.name;
+ self_type_rib.bindings.insert(name,
DlDef(DefSelfTy(item.id)));
// Create a new rib for the trait-wide type parameters.
self.type_ribs.push(function_type_rib);
for (index, type_parameter) in generics.ty_params.iter().enumerate() {
- let name = type_parameter.ident;
+ let ident = type_parameter.ident;
debug!("with_type_parameter_rib: %d %d", node_id,
type_parameter.id);
let def_like = DlDef(DefTyParam
// the item that bound it
self.record_def(type_parameter.id,
DefTyParamBinder(node_id));
- function_type_rib.bindings.insert(name, def_like);
+ // plain insert (no renaming)
+ function_type_rib.bindings.insert(ident.name, def_like);
}
}
None, visitor);
}
+ // build a map from pattern identifiers to binding-info's.
+ // this is done hygienically. This could arise for a macro
+ // that expands into an or-pattern where one 'x' was from the
+ // user and one 'x' came from the macro.
pub fn binding_mode_map(@mut self, pat: @Pat) -> BindingMap {
let mut result = HashMap::new();
do pat_bindings(self.def_map, pat) |binding_mode, _id, sp, path| {
- let ident = path_to_ident(path);
- result.insert(ident,
+ let name = mtwt_resolve(path_to_ident(path));
+ result.insert(name,
binding_info {span: sp,
binding_mode: binding_mode});
}
return result;
}
+ // check that all of the arms in an or-pattern have exactly the
+ // same set of bindings, with the same binding modes for each.
pub fn check_consistent_bindings(@mut self, arm: &Arm) {
if arm.pats.len() == 0 { return; }
let map_0 = self.binding_mode_map(arm.pats[0]);
p.span,
fmt!("variable `%s` from pattern #1 is \
not bound in pattern #%u",
- self.session.str_of(key), i + 1));
+ interner_get(key), i + 1));
}
Some(binding_i) => {
if binding_0.binding_mode != binding_i.binding_mode {
binding_i.span,
fmt!("variable `%s` is bound with different \
mode in pattern #%u than in pattern #1",
- self.session.str_of(key), i + 1));
+ interner_get(key), i + 1));
}
}
}
binding.span,
fmt!("variable `%s` from pattern #%u is \
not bound in pattern #1",
- self.session.str_of(key), i + 1));
+ interner_get(key), i + 1));
}
}
}
// First, check to see whether the name is a primitive type.
if path.segments.len() == 1 {
- let name = path.segments.last().identifier;
+ let id = path.segments.last().identifier;
match self.primitive_type_table
.primitive_types
- .find(&name) {
+ .find(&id.name) {
Some(&primitive_type) => {
result_def =
mutability: Mutability,
// Maps idents to the node ID for the (outermost)
// pattern that binds them
- bindings_list: Option<@mut HashMap<Ident,NodeId>>,
+ bindings_list: Option<@mut HashMap<Name,NodeId>>,
visitor: &mut ResolveVisitor) {
let pat_id = pattern.id;
do walk_pat(pattern) |pattern| {
// what you want).
let ident = path.segments[0].identifier;
+ let renamed = mtwt_resolve(ident);
match self.resolve_bare_identifier_pattern(ident) {
FoundStructOrEnumVariant(def)
if mode == RefutableMode => {
debug!("(resolving pattern) resolving `%s` to \
struct or enum variant",
- self.session.str_of(ident));
+ interner_get(renamed));
self.enforce_default_binding_mode(
pattern,
shadows an enum \
variant or unit-like \
struct in scope",
- self.session
- .str_of(ident)));
+ interner_get(renamed)));
}
FoundConst(def) if mode == RefutableMode => {
debug!("(resolving pattern) resolving `%s` to \
constant",
- self.session.str_of(ident));
+ interner_get(renamed));
self.enforce_default_binding_mode(
pattern,
}
BareIdentifierPatternUnresolved => {
debug!("(resolving pattern) binding `%s`",
- self.session.str_of(ident));
+ interner_get(renamed));
let is_mutable = mutability == Mutable;
match bindings_list {
Some(bindings_list)
- if !bindings_list.contains_key(&ident) => {
+ if !bindings_list.contains_key(&renamed) => {
let this = &mut *self;
let last_rib = this.value_ribs[
this.value_ribs.len() - 1];
- last_rib.bindings.insert(ident,
+ last_rib.bindings.insert(renamed,
DlDef(def));
- bindings_list.insert(ident, pat_id);
+ bindings_list.insert(renamed, pat_id);
}
Some(b) => {
- if b.find(&ident) == Some(&pat_id) {
+ if b.find(&renamed) == Some(&pat_id) {
// Then this is a duplicate variable
// in the same disjunct, which is an
// error
let this = &mut *self;
let last_rib = this.value_ribs[
this.value_ribs.len() - 1];
- last_rib.bindings.insert(ident,
+ last_rib.bindings.insert(renamed,
DlDef(def));
}
}
return unqualified_def;
}
+ // resolve a single identifier (used as a varref)
pub fn resolve_identifier(@mut self,
identifier: Ident,
namespace: Namespace,
-> NameDefinition {
// First, search children.
self.populate_module_if_necessary(containing_module);
- match containing_module.children.find(&name) {
+ match containing_module.children.find(&name.name) {
Some(child_name_bindings) => {
match (child_name_bindings.def_for_namespace(namespace),
child_name_bindings.privacy_for_namespace(namespace)) {
}
// Next, search import resolutions.
- match containing_module.import_resolutions.find(&name) {
+ match containing_module.import_resolutions.find(&name.name) {
Some(import_resolution) if import_resolution.privacy == Public ||
xray == Xray => {
match (*import_resolution).target_for_namespace(namespace) {
// Finally, search through external children.
if namespace == TypeNS {
- match containing_module.external_module_children.find(&name) {
+ match containing_module.external_module_children.find(&name.name) {
None => {}
Some(module) => {
match module.def_id {
return NoNameDefinition;
}
- pub fn intern_module_part_of_path(@mut self, path: &Path) -> ~[Ident] {
- let mut module_path_idents = ~[];
- for (index, segment) in path.segments.iter().enumerate() {
- if index == path.segments.len() - 1 {
- break;
- }
-
- module_path_idents.push(segment.identifier);
- }
-
- return module_path_idents;
- }
-
+ // resolve a "module-relative" path, e.g. a::b::c
pub fn resolve_module_relative_path(@mut self,
path: &Path,
xray: XrayFlag,
namespace: Namespace)
-> Option<Def> {
- let module_path_idents = self.intern_module_part_of_path(path);
+ let module_path_idents = path.segments.init().map(|ps| ps.identifier);
let containing_module;
match self.resolve_module_path(self.current_module,
}
}
- let name = path.segments.last().identifier;
+ let ident = path.segments.last().identifier;
let def = match self.resolve_definition_of_name_in_module(containing_module,
- name,
+ ident,
namespace,
xray) {
NoNameDefinition => {
};
match containing_module.kind {
TraitModuleKind | ImplModuleKind => {
- match self.method_map.find(&name) {
+ match self.method_map.find(&ident.name) {
Some(s) => {
match containing_module.def_id {
Some(def_id) if s.contains(&def_id) => {
xray: XrayFlag,
namespace: Namespace)
-> Option<Def> {
- let module_path_idents = self.intern_module_part_of_path(path);
+ let module_path_idents = path.segments.init().map(|ps| ps.identifier);
let root_module = self.graph_root.get_module();
let search_result;
match namespace {
ValueNS => {
- search_result = self.search_ribs(self.value_ribs, ident,
+ let renamed = mtwt_resolve(ident);
+ search_result = self.search_ribs(self.value_ribs, renamed,
span,
DontAllowCapturingSelf);
}
TypeNS => {
- search_result = self.search_ribs(self.type_ribs, ident,
+ let name = ident.name;
+ search_result = self.search_ribs(self.type_ribs, name,
span, AllowCapturingSelf);
}
}
while j != 0 {
j -= 1;
for (&k, _) in this.value_ribs[j].bindings.iter() {
- maybes.push(this.session.str_of(k));
+ maybes.push(interner_get(k));
values.push(uint::max_value);
}
}
let this = &mut *self;
let def_like = DlDef(DefLabel(expr.id));
let rib = this.label_ribs[this.label_ribs.len() - 1];
- rib.bindings.insert(label, def_like);
+ // plain insert (no renaming)
+ rib.bindings.insert(label.name, def_like);
}
visit::walk_expr(visitor, expr, ());
ExprForLoop(*) => fail!("non-desugared expr_for_loop"),
ExprBreak(Some(label)) | ExprAgain(Some(label)) => {
- match self.search_ribs(self.label_ribs, label, expr.span,
+ let name = label.name;
+ match self.search_ribs(self.label_ribs, name, expr.span,
DontAllowCapturingSelf) {
None =>
self.resolve_error(expr.span,
let mut found_traits = ~[];
let mut search_module = self.current_module;
- match self.method_map.find(&name) {
+ match self.method_map.find(&name.name) {
Some(candidate_traits) => loop {
// Look for the current trait.
match self.current_trait_refs {
debug!("Children:");
self.populate_module_if_necessary(module_);
for (&name, _) in module_.children.iter() {
- debug!("* %s", self.session.str_of(name));
+ debug!("* %s", interner_get(name));
}
debug!("Import resolutions:");
}
}
- debug!("* %s:%s%s", self.session.str_of(*name),
+ debug!("* %s:%s%s", interner_get(*name),
value_repr, type_repr);
}
}
trait_map: resolver.trait_map.clone(),
}
}
+
let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| {
let rec_vals = rec_fields.map(|field_name| {
- let ix = ty::field_idx_strict(tcx, *field_name, field_tys);
+ let ix = ty::field_idx_strict(tcx, field_name.name, field_tys);
adt::trans_field_ptr(bcx, pat_repr, val, discr, ix)
});
compile_submatch(
let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| {
for f in fields.iter() {
- let ix = ty::field_idx_strict(tcx, f.ident, field_tys);
+ let ix = ty::field_idx_strict(tcx, f.ident.name, field_tys);
let fldptr = adt::trans_field_ptr(bcx, pat_repr, val,
discr, ix);
bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, binding_mode);
}
macro_rules! ifn (
- ($name:expr, $args:expr, $ret:expr) => ({
+ ($intrinsics:ident, $name:expr, $args:expr, $ret:expr) => ({
let name = $name;
let f = decl_cdecl_fn(llmod, name, Type::func($args, &$ret));
- intrinsics.insert(name, f);
+ $intrinsics.insert(name, f);
})
)
let i8p = Type::i8p();
let mut intrinsics = HashMap::new();
- ifn!("llvm.memcpy.p0i8.p0i8.i32",
+ ifn!(intrinsics, "llvm.memcpy.p0i8.p0i8.i32",
[i8p, i8p, Type::i32(), Type::i32(), Type::i1()], Type::void());
- ifn!("llvm.memcpy.p0i8.p0i8.i64",
+ ifn!(intrinsics, "llvm.memcpy.p0i8.p0i8.i64",
[i8p, i8p, Type::i64(), Type::i32(), Type::i1()], Type::void());
- ifn!("llvm.memmove.p0i8.p0i8.i32",
+ ifn!(intrinsics, "llvm.memmove.p0i8.p0i8.i32",
[i8p, i8p, Type::i32(), Type::i32(), Type::i1()], Type::void());
- ifn!("llvm.memmove.p0i8.p0i8.i64",
+ ifn!(intrinsics, "llvm.memmove.p0i8.p0i8.i64",
[i8p, i8p, Type::i64(), Type::i32(), Type::i1()], Type::void());
- ifn!("llvm.memset.p0i8.i32",
+ ifn!(intrinsics, "llvm.memset.p0i8.i32",
[i8p, Type::i8(), Type::i32(), Type::i32(), Type::i1()], Type::void());
- ifn!("llvm.memset.p0i8.i64",
+ ifn!(intrinsics, "llvm.memset.p0i8.i64",
[i8p, Type::i8(), Type::i64(), Type::i32(), Type::i1()], Type::void());
- ifn!("llvm.trap", [], Type::void());
- ifn!("llvm.frameaddress", [Type::i32()], i8p);
-
- ifn!("llvm.powi.f32", [Type::f32(), Type::i32()], Type::f32());
- ifn!("llvm.powi.f64", [Type::f64(), Type::i32()], Type::f64());
- ifn!("llvm.pow.f32", [Type::f32(), Type::f32()], Type::f32());
- ifn!("llvm.pow.f64", [Type::f64(), Type::f64()], Type::f64());
-
- ifn!("llvm.sqrt.f32", [Type::f32()], Type::f32());
- ifn!("llvm.sqrt.f64", [Type::f64()], Type::f64());
- ifn!("llvm.sin.f32", [Type::f32()], Type::f32());
- ifn!("llvm.sin.f64", [Type::f64()], Type::f64());
- ifn!("llvm.cos.f32", [Type::f32()], Type::f32());
- ifn!("llvm.cos.f64", [Type::f64()], Type::f64());
- ifn!("llvm.exp.f32", [Type::f32()], Type::f32());
- ifn!("llvm.exp.f64", [Type::f64()], Type::f64());
- ifn!("llvm.exp2.f32", [Type::f32()], Type::f32());
- ifn!("llvm.exp2.f64", [Type::f64()], Type::f64());
- ifn!("llvm.log.f32", [Type::f32()], Type::f32());
- ifn!("llvm.log.f64", [Type::f64()], Type::f64());
- ifn!("llvm.log10.f32",[Type::f32()], Type::f32());
- ifn!("llvm.log10.f64",[Type::f64()], Type::f64());
- ifn!("llvm.log2.f32", [Type::f32()], Type::f32());
- ifn!("llvm.log2.f64", [Type::f64()], Type::f64());
-
- ifn!("llvm.fma.f32", [Type::f32(), Type::f32(), Type::f32()], Type::f32());
- ifn!("llvm.fma.f64", [Type::f64(), Type::f64(), Type::f64()], Type::f64());
-
- ifn!("llvm.fabs.f32", [Type::f32()], Type::f32());
- ifn!("llvm.fabs.f64", [Type::f64()], Type::f64());
- ifn!("llvm.floor.f32",[Type::f32()], Type::f32());
- ifn!("llvm.floor.f64",[Type::f64()], Type::f64());
- ifn!("llvm.ceil.f32", [Type::f32()], Type::f32());
- ifn!("llvm.ceil.f64", [Type::f64()], Type::f64());
- ifn!("llvm.trunc.f32",[Type::f32()], Type::f32());
- ifn!("llvm.trunc.f64",[Type::f64()], Type::f64());
-
- ifn!("llvm.ctpop.i8", [Type::i8()], Type::i8());
- ifn!("llvm.ctpop.i16",[Type::i16()], Type::i16());
- ifn!("llvm.ctpop.i32",[Type::i32()], Type::i32());
- ifn!("llvm.ctpop.i64",[Type::i64()], Type::i64());
-
- ifn!("llvm.ctlz.i8", [Type::i8() , Type::i1()], Type::i8());
- ifn!("llvm.ctlz.i16", [Type::i16(), Type::i1()], Type::i16());
- ifn!("llvm.ctlz.i32", [Type::i32(), Type::i1()], Type::i32());
- ifn!("llvm.ctlz.i64", [Type::i64(), Type::i1()], Type::i64());
-
- ifn!("llvm.cttz.i8", [Type::i8() , Type::i1()], Type::i8());
- ifn!("llvm.cttz.i16", [Type::i16(), Type::i1()], Type::i16());
- ifn!("llvm.cttz.i32", [Type::i32(), Type::i1()], Type::i32());
- ifn!("llvm.cttz.i64", [Type::i64(), Type::i1()], Type::i64());
-
- ifn!("llvm.bswap.i16",[Type::i16()], Type::i16());
- ifn!("llvm.bswap.i32",[Type::i32()], Type::i32());
- ifn!("llvm.bswap.i64",[Type::i64()], Type::i64());
-
- ifn!("llvm.sadd.with.overflow.i8",
+ ifn!(intrinsics, "llvm.trap", [], Type::void());
+ ifn!(intrinsics, "llvm.frameaddress", [Type::i32()], i8p);
+
+ ifn!(intrinsics, "llvm.powi.f32", [Type::f32(), Type::i32()], Type::f32());
+ ifn!(intrinsics, "llvm.powi.f64", [Type::f64(), Type::i32()], Type::f64());
+ ifn!(intrinsics, "llvm.pow.f32", [Type::f32(), Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.pow.f64", [Type::f64(), Type::f64()], Type::f64());
+
+ ifn!(intrinsics, "llvm.sqrt.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.sqrt.f64", [Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.sin.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.sin.f64", [Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.cos.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.cos.f64", [Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.exp.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.exp.f64", [Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.exp2.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.exp2.f64", [Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.log.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.log.f64", [Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.log10.f32",[Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.log10.f64",[Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.log2.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.log2.f64", [Type::f64()], Type::f64());
+
+ ifn!(intrinsics, "llvm.fma.f32", [Type::f32(), Type::f32(), Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.fma.f64", [Type::f64(), Type::f64(), Type::f64()], Type::f64());
+
+ ifn!(intrinsics, "llvm.fabs.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.fabs.f64", [Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.floor.f32",[Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.floor.f64",[Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.ceil.f32", [Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.ceil.f64", [Type::f64()], Type::f64());
+ ifn!(intrinsics, "llvm.trunc.f32",[Type::f32()], Type::f32());
+ ifn!(intrinsics, "llvm.trunc.f64",[Type::f64()], Type::f64());
+
+ ifn!(intrinsics, "llvm.ctpop.i8", [Type::i8()], Type::i8());
+ ifn!(intrinsics, "llvm.ctpop.i16",[Type::i16()], Type::i16());
+ ifn!(intrinsics, "llvm.ctpop.i32",[Type::i32()], Type::i32());
+ ifn!(intrinsics, "llvm.ctpop.i64",[Type::i64()], Type::i64());
+
+ ifn!(intrinsics, "llvm.ctlz.i8", [Type::i8() , Type::i1()], Type::i8());
+ ifn!(intrinsics, "llvm.ctlz.i16", [Type::i16(), Type::i1()], Type::i16());
+ ifn!(intrinsics, "llvm.ctlz.i32", [Type::i32(), Type::i1()], Type::i32());
+ ifn!(intrinsics, "llvm.ctlz.i64", [Type::i64(), Type::i1()], Type::i64());
+
+ ifn!(intrinsics, "llvm.cttz.i8", [Type::i8() , Type::i1()], Type::i8());
+ ifn!(intrinsics, "llvm.cttz.i16", [Type::i16(), Type::i1()], Type::i16());
+ ifn!(intrinsics, "llvm.cttz.i32", [Type::i32(), Type::i1()], Type::i32());
+ ifn!(intrinsics, "llvm.cttz.i64", [Type::i64(), Type::i1()], Type::i64());
+
+ ifn!(intrinsics, "llvm.bswap.i16",[Type::i16()], Type::i16());
+ ifn!(intrinsics, "llvm.bswap.i32",[Type::i32()], Type::i32());
+ ifn!(intrinsics, "llvm.bswap.i64",[Type::i64()], Type::i64());
+
+ ifn!(intrinsics, "llvm.sadd.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
- ifn!("llvm.sadd.with.overflow.i16",
+ ifn!(intrinsics, "llvm.sadd.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
- ifn!("llvm.sadd.with.overflow.i32",
+ ifn!(intrinsics, "llvm.sadd.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
- ifn!("llvm.sadd.with.overflow.i64",
+ ifn!(intrinsics, "llvm.sadd.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));
- ifn!("llvm.uadd.with.overflow.i8",
+ ifn!(intrinsics, "llvm.uadd.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
- ifn!("llvm.uadd.with.overflow.i16",
+ ifn!(intrinsics, "llvm.uadd.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
- ifn!("llvm.uadd.with.overflow.i32",
+ ifn!(intrinsics, "llvm.uadd.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
- ifn!("llvm.uadd.with.overflow.i64",
+ ifn!(intrinsics, "llvm.uadd.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));
- ifn!("llvm.ssub.with.overflow.i8",
+ ifn!(intrinsics, "llvm.ssub.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
- ifn!("llvm.ssub.with.overflow.i16",
+ ifn!(intrinsics, "llvm.ssub.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
- ifn!("llvm.ssub.with.overflow.i32",
+ ifn!(intrinsics, "llvm.ssub.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
- ifn!("llvm.ssub.with.overflow.i64",
+ ifn!(intrinsics, "llvm.ssub.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));
- ifn!("llvm.usub.with.overflow.i8",
+ ifn!(intrinsics, "llvm.usub.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
- ifn!("llvm.usub.with.overflow.i16",
+ ifn!(intrinsics, "llvm.usub.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
- ifn!("llvm.usub.with.overflow.i32",
+ ifn!(intrinsics, "llvm.usub.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
- ifn!("llvm.usub.with.overflow.i64",
+ ifn!(intrinsics, "llvm.usub.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));
- ifn!("llvm.smul.with.overflow.i8",
+ ifn!(intrinsics, "llvm.smul.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
- ifn!("llvm.smul.with.overflow.i16",
+ ifn!(intrinsics, "llvm.smul.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
- ifn!("llvm.smul.with.overflow.i32",
+ ifn!(intrinsics, "llvm.smul.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
- ifn!("llvm.smul.with.overflow.i64",
+ ifn!(intrinsics, "llvm.smul.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));
- ifn!("llvm.umul.with.overflow.i8",
+ ifn!(intrinsics, "llvm.umul.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
- ifn!("llvm.umul.with.overflow.i16",
+ ifn!(intrinsics, "llvm.umul.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
- ifn!("llvm.umul.with.overflow.i32",
+ ifn!(intrinsics, "llvm.umul.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
- ifn!("llvm.umul.with.overflow.i64",
+ ifn!(intrinsics, "llvm.umul.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));
return intrinsics;
}
pub fn declare_dbg_intrinsics(llmod: ModuleRef, intrinsics: &mut HashMap<&'static str, ValueRef>) {
- ifn!("llvm.dbg.declare", [Type::metadata(), Type::metadata()], Type::void());
- ifn!("llvm.dbg.value", [Type::metadata(), Type::i64(), Type::metadata()], Type::void());
+ ifn!(intrinsics, "llvm.dbg.declare", [Type::metadata(), Type::metadata()], Type::void());
+ ifn!(intrinsics,
+ "llvm.dbg.value", [Type::metadata(), Type::i64(), Type::metadata()], Type::void());
}
pub fn trap(bcx: @mut Block) {
let brepr = adt::represent_type(cx, bt);
let bv = const_expr(cx, base);
do expr::with_field_tys(cx.tcx, bt, None) |discr, field_tys| {
- let ix = ty::field_idx_strict(cx.tcx, field, field_tys);
+ let ix = ty::field_idx_strict(cx.tcx, field.name, field_tys);
adt::const_get_field(cx, brepr, bv, discr, ix)
}
}
|discr, field_tys| {
let cs: ~[ValueRef] = field_tys.iter().enumerate()
.map(|(ix, &field_ty)| {
- match fs.iter().find(|f| field_ty.ident == f.ident) {
+ match fs.iter().find(|f| field_ty.ident.name == f.ident.name) {
Some(f) => const_expr(cx, (*f).expr),
None => {
match base_val {
// Cache of external const values
extern_const_values: HashMap<ast::DefId, ValueRef>,
- impl_method_cache: HashMap<(ast::DefId, ast::Ident), ast::DefId>,
+ impl_method_cache: HashMap<(ast::DefId, ast::Name), ast::DefId>,
module_data: HashMap<~str, ValueRef>,
lltypes: HashMap<ty::t, Type>,
let field_llvm_types = do fields.map |field| { type_of::type_of(cx, field.mt.ty) };
let field_names = do fields.map |field| {
- if field.ident == special_idents::unnamed_field {
+ if field.ident.name == special_idents::unnamed_field.name {
~""
} else {
cx.sess.str_of(field.ident).to_owned()
// }
// Is there already a binding with that name?
+ // N.B.: this comparison must be UNhygienic... because
+ // gdb knows nothing about the context, so any two
+ // variables with the same name will cause the problem.
let need_new_scope = scope_stack
.iter()
- .any(|entry| entry.ident.iter().any(|i| *i == ident));
+ .any(|entry| entry.ident.iter().any(|i| i.name == ident.name));
if need_new_scope {
// Create a new lexical scope and push it onto the stack
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
let repr = adt::represent_type(bcx.ccx(), base_datum.ty);
do with_field_tys(bcx.tcx(), base_datum.ty, None) |discr, field_tys| {
- let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
+ let ix = ty::field_idx_strict(bcx.tcx(), field.name, field_tys);
DatumBlock {
datum: do base_datum.get_element(bcx,
field_tys[ix].mt.ty,
let mut need_base = vec::from_elem(field_tys.len(), true);
let numbered_fields = do fields.map |field| {
- let opt_pos = field_tys.iter().position(|field_ty| field_ty.ident == field.ident);
+ let opt_pos =
+ field_tys.iter().position(|field_ty|
+ field_ty.ident.name == field.ident.name);
match opt_pos {
Some(i) => {
need_base[i] = false;
typeck::vtable_static(impl_did, ref rcvr_substs, rcvr_origins) => {
assert!(rcvr_substs.iter().all(|t| !ty::type_needs_infer(*t)));
- let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
+ let mth_id = method_with_name(bcx.ccx(), impl_did, mname.name);
let (callee_substs, callee_origins) =
combine_impl_and_methods_tps(
bcx, mth_id, callee_id,
pub fn method_with_name(ccx: &mut CrateContext,
impl_id: ast::DefId,
- name: ast::Ident) -> ast::DefId {
+ name: ast::Name) -> ast::DefId {
let meth_id_opt = ccx.impl_method_cache.find_copy(&(impl_id, name));
match meth_id_opt {
Some(m) => return m,
let imp = ccx.tcx.impls.find(&impl_id)
.expect("could not find impl while translating");
- let meth = imp.methods.iter().find(|m| m.ident == name)
+ let meth = imp.methods.iter().find(|m| m.ident.name == name)
.expect("could not find method while translating");
ccx.impl_method_cache.insert((impl_id, name), meth.def_id);
typeck::vtable_static(impl_did, ref rcvr_substs, rcvr_origins) => {
let ccx = bcx.ccx();
let mname = ty::trait_method(ccx.tcx, trait_id, n_method).ident;
- let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
+ let mth_id = method_with_name(bcx.ccx(), impl_did, mname.name);
// obtain the `self` value:
let mut temp_cleanups = ~[];
let ident = ty::method(tcx, *method_def_id).ident;
// The substitutions we have are on the impl, so we grab
// the method type from the impl to substitute into.
- let m_id = method_with_name(ccx, impl_id, ident);
+ let m_id = method_with_name(ccx, impl_id, ident.name);
let m = ty::method(tcx, m_id);
debug!("(making impl vtable) emitting method %s at subst %s",
m.repr(tcx),
}
}
-pub fn field_idx(id: ast::Ident, fields: &[field]) -> Option<uint> {
+pub fn field_idx(name: ast::Name, fields: &[field]) -> Option<uint> {
let mut i = 0u;
- for f in fields.iter() { if f.ident == id { return Some(i); } i += 1u; }
+ for f in fields.iter() { if f.ident.name == name { return Some(i); } i += 1u; }
return None;
}
-pub fn field_idx_strict(tcx: ty::ctxt, id: ast::Ident, fields: &[field])
+pub fn field_idx_strict(tcx: ty::ctxt, name: ast::Name, fields: &[field])
-> uint {
let mut i = 0u;
- for f in fields.iter() { if f.ident == id { return i; } i += 1u; }
+ for f in fields.iter() { if f.ident.name == name { return i; } i += 1u; }
tcx.sess.bug(fmt!(
"No field named `%s` found in the list of fields `%?`",
- tcx.sess.str_of(id),
+ token::interner_get(name),
fields.map(|f| tcx.sess.str_of(f.ident))));
}
// Index the class fields.
let mut field_map = HashMap::new();
for (i, class_field) in class_fields.iter().enumerate() {
- field_map.insert(class_field.ident, i);
+ field_map.insert(class_field.ident.name, i);
}
// Typecheck each field.
let mut found_fields = HashSet::new();
for field in fields.iter() {
- match field_map.find(&field.ident) {
+ match field_map.find(&field.ident.name) {
Some(&index) => {
let class_field = class_fields[index];
let field_type = ty::lookup_field_type(tcx,
use syntax::ast::{MutMutable, MutImmutable};
use syntax::ast;
use syntax::ast_map;
+use syntax::parse::token;
#[deriving(Eq)]
pub enum CheckTraitsFlag {
self_expr: @ast::Expr, // The expression `a`.
callee_id: NodeId, /* Where to store `a.b`'s type,
* also the scope of the call */
- m_name: ast::Ident, // The ident `b`.
+ m_name: ast::Name, // The name `b`.
self_ty: ty::t, // The type of `a`.
supplied_tps: &[ty::t], // The list of types X, Y, ... .
deref_args: check::DerefArgs, // Whether we autopointer first.
expr: @ast::Expr,
self_expr: @ast::Expr,
callee_id: NodeId,
- m_name: ast::Ident,
+ m_name: ast::Name,
supplied_tps: &'self [ty::t],
impl_dups: @mut HashSet<DefId>,
inherent_candidates: @mut ~[Candidate],
let trait_methods = ty::trait_methods(tcx, bound_trait_ref.def_id);
match trait_methods.iter().position(|m| {
m.explicit_self != ast::sty_static &&
- m.ident == self.m_name })
+ m.ident.name == self.m_name })
{
Some(pos) => {
let method = trait_methods[pos];
return; // already visited
}
debug!("push_candidates_from_impl: %s %s %s",
- self.m_name.repr(self.tcx()),
+ token::interner_get(self.m_name),
impl_info.ident.repr(self.tcx()),
impl_info.methods.map(|m| m.ident).repr(self.tcx()));
let idx = {
- match impl_info.methods.iter().position(|m| m.ident == self.m_name) {
+ match impl_info.methods.iter().position(|m| m.ident.name == self.m_name) {
Some(idx) => idx,
None => { return; } // No method with the right name.
}
pub fn lookup_field_ty(tcx: ty::ctxt,
class_id: ast::DefId,
items: &[ty::field_ty],
- fieldname: ast::Ident,
+ fieldname: ast::Name,
substs: &ty::substs) -> Option<ty::t> {
- let o_field = items.iter().find(|f| f.ident == fieldname);
+ let o_field = items.iter().find(|f| f.ident.name == fieldname);
do o_field.map() |f| {
ty::lookup_field_type(tcx, class_id, f.id, substs)
}
expr,
rcvr,
callee_id,
- method_name,
+ method_name.name,
expr_t,
tps,
DontDerefArgs,
op_ex: @ast::Expr,
self_ex: @ast::Expr,
self_t: ty::t,
- opname: ast::Ident,
+ opname: ast::Name,
args: ~[@ast::Expr],
deref_args: DerefArgs,
autoderef_receiver: AutoderefReceiverFlag,
lhs_resolved_t, None)
};
return lookup_op_method(fcx, callee_id, ex, lhs_expr, lhs_resolved_t,
- fcx.tcx().sess.ident_of(*name),
+ token::intern(*name),
~[rhs], DoDerefArgs, DontAutoderefReceiver, if_op_unbound,
expected_result);
}
-> ty::t {
lookup_op_method(
fcx, callee_id, ex, rhs_expr, rhs_t,
- fcx.tcx().sess.ident_of(mname), ~[],
+ token::intern(mname), ~[],
DoDerefArgs, DontAutoderefReceiver,
|| {
fcx.type_error_message(ex.span, |actual| {
fn check_field(fcx: @mut FnCtxt,
expr: @ast::Expr,
base: @ast::Expr,
- field: ast::Ident,
+ field: ast::Name,
tys: &[ast::Ty]) {
let tcx = fcx.ccx.tcx;
let bot = check_expr(fcx, base);
|actual| {
fmt!("attempted to take value of method `%s` on type `%s` \
(try writing an anonymous function)",
- tcx.sess.str_of(field), actual)
+ token::interner_get(field), actual)
},
expr_t, None);
}
|actual| {
fmt!("attempted access of field `%s` on type `%s`, \
but no field with that name was found",
- tcx.sess.str_of(field), actual)
+ token::interner_get(field), actual)
},
expr_t, None);
}
let mut class_field_map = HashMap::new();
let mut fields_found = 0;
for field in field_types.iter() {
- class_field_map.insert(field.ident, (field.id, false));
+ class_field_map.insert(field.ident.name, (field.id, false));
}
let mut error_happened = false;
for field in ast_fields.iter() {
let mut expected_field_type = ty::mk_err();
- let pair = class_field_map.find(&field.ident).map_move(|x| *x);
+ let pair = class_field_map.find(&field.ident.name).map_move(|x| *x);
match pair {
None => {
tcx.sess.span_err(
ty::lookup_field_type(
tcx, class_id, field_id, &substitutions);
class_field_map.insert(
- field.ident, (field_id, true));
+ field.ident.name, (field_id, true));
fields_found += 1;
}
}
if fields_found < field_types.len() {
let mut missing_fields = ~[];
for class_field in field_types.iter() {
- let name = class_field.ident;
+ let name = class_field.ident.name;
let (_, seen) = *class_field_map.get(&name);
if !seen {
missing_fields.push(
- ~"`" + tcx.sess.str_of(name) + "`");
+ ~"`" + token::interner_get(name) + "`");
}
}
}
}
ast::ExprField(base, field, ref tys) => {
- check_field(fcx, expr, base, field, *tys);
+ check_field(fcx, expr, base, field.name, *tys);
}
ast::ExprIndex(callee_id, base, idx) => {
check_expr(fcx, base);
expr,
base,
resolved,
- index_ident,
+ index_ident.name,
~[idx],
DoDerefArgs,
AutoderefReceiver,
let mut provided_names = HashSet::new();
// Implemented methods
for elt in all_methods.iter() {
- provided_names.insert(elt.ident);
+ provided_names.insert(elt.ident.name);
}
let r = ty::trait_methods(tcx, trait_did);
for method in r.iter() {
debug!("checking for %s", method.ident.repr(tcx));
- if provided_names.contains(&method.ident) { loop; }
+ if provided_names.contains(&method.ident.name) { loop; }
tcx.sess.span_err(trait_ref_span,
fmt!("missing method `%s`",
// we'll catch it in coherence
let trait_ms = ty::trait_methods(tcx, trait_ref.def_id);
for impl_m in impl_ms.iter() {
- match trait_ms.iter().find(|trait_m| trait_m.ident == impl_m.mty.ident) {
+ match trait_ms.iter().find(|trait_m| trait_m.ident.name == impl_m.mty.ident.name) {
Some(trait_m) => {
let num_impl_tps = generics.ty_params.len();
compare_impl_method(
}
}
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ // just test to see if it compiles:
+ #[test] fn iterbytes_compiles () {
+ takes_iterbytes((3,4,5,false));
+ }
+ fn takes_iterbytes<T : IterBytes>(x : T) {}
+}
use std::to_str::ToStr;
use extra::serialize::{Encodable, Decodable, Encoder, Decoder};
+
+// FIXME #6993: in librustc, uses of "ident" should be replaced
+// by just "Name".
+
// an identifier contains a Name (index into the interner
// table) and a SyntaxContext to track renaming and
// macro expansion per Flatt et al., "Macros
pub fn new(name: Name) -> Ident { Ident {name: name, ctxt: EMPTY_CTXT}}
}
+// defining eq in this way is a way of guaranteeing that later stages of the
+// compiler don't compare identifiers unhygienically. Unfortunately, some tests
+// (specifically debuginfo in no-opt) want to do these comparisons, and that
+// seems fine. If only I could find a nice way to statically ensure that
+// the compiler "proper" never compares identifiers.... I'm leaving this
+// code here (commented out) for potential use in debugging. Specifically, if
+// there's a bug where "identifiers aren't matching", it may be because
+// they should be compared using mtwt_resolve. In such a case, re-enabling this
+// code (and disabling deriving(Eq) for Idents) could help to isolate the
+// problem
+/* impl Eq for Ident {
+ fn eq(&self, other: &Ident) -> bool {
+ if (self.ctxt == other.ctxt) {
+ self.name == other.name
+ } else {
+ // IF YOU SEE ONE OF THESE FAILS: it means that you're comparing
+ // idents that have different contexts. You can't fix this without
+ // knowing whether the comparison should be hygienic or non-hygienic.
+ // if it should be non-hygienic (most things are), just compare the
+ // 'name' fields of the idents. Or, even better, replace the idents
+ // with Name's.
+ fail!(fmt!("not allowed to compare these idents: %?, %?", self, other));
+ }
+ }
+ fn ne(&self, other: &Ident) -> bool {
+ ! self.eq(other)
+ }
+}
+*/
+
/// A SyntaxContext represents a chain of macro-expandings
/// and renamings. Each macro expansion corresponds to
/// a fresh uint
// storage.
pub type SyntaxContext = uint;
+// the SCTable contains a table of SyntaxContext_'s. It
+// represents a flattened tree structure, to avoid having
+// managed pointers everywhere (that caused an ICE).
+// the mark_memo and rename_memo fields are side-tables
+// that ensure that adding the same mark to the same context
+// gives you back the same context as before. This shouldn't
+// change the semantics--everything here is immutable--but
+// it should cut down on memory use *a lot*; applying a mark
+// to a tree containing 50 identifiers would otherwise generate
pub struct SCTable {
table : ~[SyntaxContext_],
mark_memo : HashMap<(SyntaxContext,Mrk),SyntaxContext>,
// in the "from" slot. In essence, they're all
// pointers to a single "rename" event node.
Rename (Ident,Name,SyntaxContext),
+ // actually, IllegalCtxt may not be necessary.
IllegalCtxt
}
pub struct Lifetime {
id: NodeId,
span: Span,
+ // FIXME #7743 : change this to Name!
ident: Ident
}
UnsafeBlock,
}
-#[deriving(Eq, Encodable, Decodable,IterBytes)]
+#[deriving(Clone, Eq, Encodable, Decodable,IterBytes)]
pub struct Expr {
id: NodeId,
node: Expr_,
// a delimited sequence (the delimiters appear as the first
// and last elements of the vector)
tt_delim(@mut ~[token_tree]),
+
// These only make sense for right-hand-sides of MBE macros:
// a kleene-style repetition sequence with a span, a tt_forest,
- // an optional separator (?), and a boolean where true indicates
+ // an optional separator, and a boolean where true indicates
// zero or more (*), and false indicates one or more (+).
tt_seq(Span, @mut ~[token_tree], Option<::parse::token::Token>, bool),
pub type mac = Spanned<mac_>;
+// represents a macro invocation. The Path indicates which macro
+// is being invoked, and the vector of token-trees contains the source
+// of the macro invocation.
+// There's only one flavor, now, so this could presumably be simplified.
#[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]
pub enum mac_ {
- mac_invoc_tt(Path,~[token_tree]), // new macro-invocation
+ mac_invoc_tt(Path,~[token_tree],SyntaxContext), // new macro-invocation
}
pub type lit = Spanned<lit_>;
idents.map(|i| token::interner_get(i.name)).connect("::")
}
+// totally scary function: ignores all but the last element, should have
+// a different name
pub fn path_to_ident(path: &Path) -> Ident {
path.segments.last().identifier
}
// HYGIENE FUNCTIONS
-/// Construct an identifier with the given name and an empty context:
-pub fn new_ident(name: Name) -> Ident { Ident {name: name, ctxt: 0}}
-
/// Extend a syntax context with a given mark
pub fn new_mark(m:Mrk, tail:SyntaxContext) -> SyntaxContext {
new_mark_internal(m,tail,get_sctable())
}
}
+/// print out an SCTable for debugging
+pub fn display_sctable(table : &SCTable) {
+ error!("SC table:");
+ for (idx,val) in table.table.iter().enumerate() {
+ error!("%4u : %?",idx,val);
+ }
+}
+
+
/// Add a value to the end of a vec, return its index
fn idx_push<T>(vec: &mut ~[T], val: T) -> uint {
vec.push(val);
}
/// Resolve a syntax object to a name, per MTWT.
-pub fn resolve(id : Ident) -> Name {
- resolve_internal(id, get_sctable())
+pub fn mtwt_resolve(id : Ident) -> Name {
+ resolve_internal(id, get_sctable(), get_resolve_table())
+}
+
+// FIXME #4536: must be pub for testing
+pub type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
+
+// okay, I admit, putting this in TLS is not so nice:
+// fetch the SCTable from TLS, create one if it doesn't yet exist.
+pub fn get_resolve_table() -> @mut ResolveTable {
+ static resolve_table_key: local_data::Key<@@mut ResolveTable> = &local_data::Key;
+ match local_data::get(resolve_table_key, |k| k.map(|&k| *k)) {
+ None => {
+ let new_table = @@mut HashMap::new();
+ local_data::set(resolve_table_key,new_table);
+ *new_table
+ },
+ Some(intr) => *intr
+ }
}
// Resolve a syntax object to a name, per MTWT.
+// adding memoization to possibly resolve 500+ seconds in resolve for librustc (!)
// FIXME #4536 : currently pub to allow testing
-pub fn resolve_internal(id : Ident, table : &mut SCTable) -> Name {
- match table.table[id.ctxt] {
- EmptyCtxt => id.name,
- // ignore marks here:
- Mark(_,subctxt) => resolve_internal(Ident{name:id.name, ctxt: subctxt},table),
- // do the rename if necessary:
- Rename(Ident{name,ctxt},toname,subctxt) => {
- // this could be cached or computed eagerly:
- let resolvedfrom = resolve_internal(Ident{name:name,ctxt:ctxt},table);
- let resolvedthis = resolve_internal(Ident{name:id.name,ctxt:subctxt},table);
- if ((resolvedthis == resolvedfrom)
- && (marksof(ctxt,resolvedthis,table)
- == marksof(subctxt,resolvedthis,table))) {
- toname
- } else {
- resolvedthis
- }
+pub fn resolve_internal(id : Ident,
+ table : &mut SCTable,
+ resolve_table : &mut ResolveTable) -> Name {
+ let key = (id.name,id.ctxt);
+ match resolve_table.contains_key(&key) {
+ false => {
+ let resolved = {
+ match table.table[id.ctxt] {
+ EmptyCtxt => id.name,
+ // ignore marks here:
+ Mark(_,subctxt) =>
+ resolve_internal(Ident{name:id.name, ctxt: subctxt},table,resolve_table),
+ // do the rename if necessary:
+ Rename(Ident{name,ctxt},toname,subctxt) => {
+ let resolvedfrom =
+ resolve_internal(Ident{name:name,ctxt:ctxt},table,resolve_table);
+ let resolvedthis =
+ resolve_internal(Ident{name:id.name,ctxt:subctxt},table,resolve_table);
+ if ((resolvedthis == resolvedfrom)
+ && (marksof(ctxt,resolvedthis,table)
+ == marksof(subctxt,resolvedthis,table))) {
+ toname
+ } else {
+ resolvedthis
+ }
+ }
+ IllegalCtxt() => fail!(~"expected resolvable context, got IllegalCtxt")
+ }
+ };
+ resolve_table.insert(key,resolved);
+ resolved
+ }
+ true => {
+ // it's guaranteed to be there, because we just checked that it was
+ // there and we never remove anything from the table:
+ *(resolve_table.find(&key).unwrap())
}
- IllegalCtxt() => fail!(~"expected resolvable context, got IllegalCtxt")
}
}
/// Compute the marks associated with a syntax context.
+pub fn mtwt_marksof(ctxt: SyntaxContext, stopname: Name) -> ~[Mrk] {
+ marksof(ctxt, stopname, get_sctable())
+}
+
+// the internal function for computing marks
// it's not clear to me whether it's better to use a [] mutable
// vector or a cons-list for this.
pub fn marksof(ctxt: SyntaxContext, stopname: Name, table: &SCTable) -> ~[Mrk] {
}
}
+/// Return the outer mark for a context with a mark at the outside.
+/// FAILS when outside is not a mark.
+pub fn mtwt_outer_mark(ctxt: SyntaxContext) -> Mrk {
+ let sctable = get_sctable();
+ match sctable.table[ctxt] {
+ ast::Mark(mrk,_) => mrk,
+ _ => fail!("can't retrieve outer mark when outside is not a mark")
+ }
+}
+
/// Push a name... unless it matches the one on top, in which
/// case pop and discard (so two of the same marks cancel)
pub fn xorPush(marks: &mut ~[uint], mark: uint) {
*arr.last()
}
+// are two paths equal when compared unhygienically?
+// since I'm using this to replace ==, it seems appropriate
+// to compare the span, global, etc. fields as well.
+pub fn path_name_eq(a : &ast::Path, b : &ast::Path) -> bool {
+ (a.span == b.span)
+ && (a.global == b.global)
+ && (segments_name_eq(a.segments, b.segments))
+}
+
+// are two arrays of segments equal when compared unhygienically?
+pub fn segments_name_eq(a : &[ast::PathSegment], b : &[ast::PathSegment]) -> bool {
+ if (a.len() != b.len()) {
+ false
+ } else {
+ for (idx,seg) in a.iter().enumerate() {
+ if (seg.identifier.name != b[idx].identifier.name)
+ // FIXME #7743: ident -> name problems in lifetime comparison?
+ || (seg.lifetime != b[idx].lifetime)
+ // can types contain idents?
+ || (seg.types != b[idx].types) {
+ return false;
+ }
+ }
+ true
+ }
+}
#[cfg(test)]
mod test {
use ast::*;
use super::*;
use std::io;
+ use opt_vec;
+ use std::hash::HashMap;
+
+ fn ident_to_segment(id : &Ident) -> PathSegment {
+ PathSegment{identifier:id.clone(), lifetime: None, types: opt_vec::Empty}
+ }
+
+ #[test] fn idents_name_eq_test() {
+ assert!(segments_name_eq([Ident{name:3,ctxt:4},
+ Ident{name:78,ctxt:82}].map(ident_to_segment),
+ [Ident{name:3,ctxt:104},
+ Ident{name:78,ctxt:182}].map(ident_to_segment)));
+ assert!(!segments_name_eq([Ident{name:3,ctxt:4},
+ Ident{name:78,ctxt:82}].map(ident_to_segment),
+ [Ident{name:3,ctxt:104},
+ Ident{name:77,ctxt:182}].map(ident_to_segment)));
+ }
#[test] fn xorpush_test () {
let mut s = ~[];
#[test] fn resolve_tests () {
let a = 40;
let mut t = new_sctable_internal();
+ let mut rt = HashMap::new();
// - ctxt is MT
- assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t),a);
+ assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),a);
// - simple ignored marks
{ let sc = unfold_marks(~[1,2,3],EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t),a);}
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
// - orthogonal rename where names don't match
{ let sc = unfold_test_sc(~[R(id(50,EMPTY_CTXT),51),M(12)],EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t),a);}
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
// - rename where names do match, but marks don't
{ let sc1 = new_mark_internal(1,EMPTY_CTXT,&mut t);
let sc = unfold_test_sc(~[R(id(a,sc1),50),
M(1),
M(2)],
EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t), a);}
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), a);}
// - rename where names and marks match
{ let sc1 = unfold_test_sc(~[M(1),M(2)],EMPTY_CTXT,&mut t);
let sc = unfold_test_sc(~[R(id(a,sc1),50),M(1),M(2)],EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t), 50); }
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
// - rename where names and marks match by literal sharing
{ let sc1 = unfold_test_sc(~[M(1),M(2)],EMPTY_CTXT,&mut t);
let sc = unfold_test_sc(~[R(id(a,sc1),50)],sc1,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t), 50); }
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
// - two renames of the same var.. can only happen if you use
// local-expand to prevent the inner binding from being renamed
// during the rename-pass caused by the first:
{ let sc = unfold_test_sc(~[R(id(a,EMPTY_CTXT),50),
R(id(a,EMPTY_CTXT),51)],
EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t), 51); }
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 51); }
// the simplest double-rename:
{ let a_to_a50 = new_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t);
let a50_to_a51 = new_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
- assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t),51);
+ assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),51);
// mark on the outside doesn't stop rename:
let sc = new_mark_internal(9,a50_to_a51,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t),51);
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),51);
// but mark on the inside does:
let a50_to_a51_b = unfold_test_sc(~[R(id(a,a_to_a50),51),
M(9)],
a_to_a50,
&mut t);
- assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t),50);}
+ assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),50);}
}
+ #[test] fn mtwt_resolve_test(){
+ let a = 40;
+ assert_eq!(mtwt_resolve(id(a,EMPTY_CTXT)),a);
+ }
+
+
#[test] fn hashing_tests () {
let mut t = new_sctable_internal();
assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
// I'm assuming that the rename table will behave the same....
}
+ #[test] fn resolve_table_hashing_tests() {
+ let mut t = new_sctable_internal();
+ let mut rt = HashMap::new();
+ assert_eq!(rt.len(),0);
+ resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
+ assert_eq!(rt.len(),1);
+ resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
+ assert_eq!(rt.len(),2);
+ resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
+ assert_eq!(rt.len(),2);
+ }
+
}
// new-style macro! tt code:
//
-// SyntaxExpanderTT, SyntaxExpanderTTItem, MacResult,
-// NormalTT, IdentTT
+// MacResult, NormalTT, IdentTT
//
// also note that ast::mac used to have a bunch of extraneous cases and
// is now probably a redundant AST node, can be merged with
ext: SyntaxExtension
}
-pub type ItemDecorator = @fn(@ExtCtxt,
+// No context arg for an Item Decorator macro, simply because
+// adding it would require adding a ctxt field to all items.
+// we could do this if it turns out to be useful.
+
+pub type ItemDecoratorFun = @fn(@ExtCtxt,
Span,
@ast::MetaItem,
~[@ast::item])
-> ~[@ast::item];
-pub struct SyntaxExpanderTT {
- expander: SyntaxExpanderTTFun,
- span: Option<Span>
-}
-
pub type SyntaxExpanderTTFun = @fn(@ExtCtxt,
Span,
- &[ast::token_tree])
+ &[ast::token_tree],
+ ast::SyntaxContext)
-> MacResult;
-pub struct SyntaxExpanderTTItem {
- expander: SyntaxExpanderTTItemFun,
- span: Option<Span>
-}
-
pub type SyntaxExpanderTTItemFun = @fn(@ExtCtxt,
+ Span,
+ ast::Ident,
+ ~[ast::token_tree],
+ ast::SyntaxContext)
+ -> MacResult;
+
+// oog... in order to make the presentation of builtin_normal_tt_no_ctxt
+// and builtin_ident_tt_no_ctxt palatable, we need one-off types for
+// functions that don't consume a ctxt:
+
+pub type SyntaxExpanderTTFunNoCtxt = @fn(@ExtCtxt,
+ Span,
+ &[ast::token_tree])
+ -> MacResult;
+
+pub type SyntaxExpanderTTItemFunNoCtxt = @fn(@ExtCtxt,
Span,
ast::Ident,
~[ast::token_tree])
-> MacResult;
+
+
pub enum MacResult {
MRExpr(@ast::Expr),
MRItem(@ast::item),
pub enum SyntaxExtension {
// #[auto_encode] and such
- ItemDecorator(ItemDecorator),
+ ItemDecorator(ItemDecoratorFun),
// Token-tree expanders
- NormalTT(SyntaxExpanderTT),
+ NormalTT(SyntaxExpanderTTFun, Option<Span>),
// An IdentTT is a macro that has an
// identifier in between the name of the
// perhaps macro_rules! will lose its odd special identifier argument,
// and this can go away also
- IdentTT(SyntaxExpanderTTItem),
+ IdentTT(SyntaxExpanderTTItemFun, Option<Span>),
}
+
// The SyntaxEnv is the environment that's threaded through the expansion
// of macros. It contains bindings for macros, and also a special binding
// for " block" (not a legal identifier) that maps to a BlockInfo
// AST nodes into full ASTs
pub fn syntax_expander_table() -> SyntaxEnv {
// utility function to simplify creating NormalTT syntax extensions
- fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> @Transformer {
- @SE(NormalTT(SyntaxExpanderTT{expander: f, span: None}))
+ // that ignore their contexts
+ fn builtin_normal_tt_no_ctxt(f: SyntaxExpanderTTFunNoCtxt) -> @Transformer {
+ let wrapped_expander : SyntaxExpanderTTFun = |a,b,c,_d|{f(a,b,c)};
+ @SE(NormalTT(wrapped_expander, None))
}
// utility function to simplify creating IdentTT syntax extensions
- fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> @Transformer {
- @SE(IdentTT(SyntaxExpanderTTItem{expander: f, span: None}))
+ // that ignore their contexts
+ fn builtin_item_tt_no_ctxt(f: SyntaxExpanderTTItemFunNoCtxt) -> @Transformer {
+ let wrapped_expander : SyntaxExpanderTTItemFun = |a,b,c,d,_e|{f(a,b,c,d)};
+ @SE(IdentTT(wrapped_expander, None))
}
let mut syntax_expanders = HashMap::new();
// NB identifier starts with space, and can't conflict with legal idents
pending_renames : @mut ~[]
}));
syntax_expanders.insert(intern(&"macro_rules"),
- builtin_item_tt(
- ext::tt::macro_rules::add_new_extension));
+ @SE(IdentTT(ext::tt::macro_rules::add_new_extension, None)));
syntax_expanders.insert(intern(&"fmt"),
- builtin_normal_tt(ext::fmt::expand_syntax_ext));
+ builtin_normal_tt_no_ctxt(ext::fmt::expand_syntax_ext));
syntax_expanders.insert(intern(&"format"),
- builtin_normal_tt(ext::ifmt::expand_format));
+ builtin_normal_tt_no_ctxt(ext::ifmt::expand_format));
syntax_expanders.insert(intern(&"write"),
- builtin_normal_tt(ext::ifmt::expand_write));
+ builtin_normal_tt_no_ctxt(ext::ifmt::expand_write));
syntax_expanders.insert(intern(&"writeln"),
- builtin_normal_tt(ext::ifmt::expand_writeln));
+ builtin_normal_tt_no_ctxt(ext::ifmt::expand_writeln));
syntax_expanders.insert(
intern(&"auto_encode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
intern(&"auto_decode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
syntax_expanders.insert(intern(&"env"),
- builtin_normal_tt(ext::env::expand_env));
+ builtin_normal_tt_no_ctxt(ext::env::expand_env));
syntax_expanders.insert(intern(&"option_env"),
- builtin_normal_tt(ext::env::expand_option_env));
+ builtin_normal_tt_no_ctxt(ext::env::expand_option_env));
syntax_expanders.insert(intern("bytes"),
- builtin_normal_tt(ext::bytes::expand_syntax_ext));
+ builtin_normal_tt_no_ctxt(ext::bytes::expand_syntax_ext));
syntax_expanders.insert(intern("concat_idents"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::concat_idents::expand_syntax_ext));
syntax_expanders.insert(intern(&"log_syntax"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::log_syntax::expand_syntax_ext));
syntax_expanders.insert(intern(&"deriving"),
@SE(ItemDecorator(
// Quasi-quoting expanders
syntax_expanders.insert(intern(&"quote_tokens"),
- builtin_normal_tt(ext::quote::expand_quote_tokens));
+ builtin_normal_tt_no_ctxt(
+ ext::quote::expand_quote_tokens));
syntax_expanders.insert(intern(&"quote_expr"),
- builtin_normal_tt(ext::quote::expand_quote_expr));
+ builtin_normal_tt_no_ctxt(ext::quote::expand_quote_expr));
syntax_expanders.insert(intern(&"quote_ty"),
- builtin_normal_tt(ext::quote::expand_quote_ty));
+ builtin_normal_tt_no_ctxt(ext::quote::expand_quote_ty));
syntax_expanders.insert(intern(&"quote_item"),
- builtin_normal_tt(ext::quote::expand_quote_item));
+ builtin_normal_tt_no_ctxt(ext::quote::expand_quote_item));
syntax_expanders.insert(intern(&"quote_pat"),
- builtin_normal_tt(ext::quote::expand_quote_pat));
+ builtin_normal_tt_no_ctxt(ext::quote::expand_quote_pat));
syntax_expanders.insert(intern(&"quote_stmt"),
- builtin_normal_tt(ext::quote::expand_quote_stmt));
+ builtin_normal_tt_no_ctxt(ext::quote::expand_quote_stmt));
syntax_expanders.insert(intern(&"line"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::source_util::expand_line));
syntax_expanders.insert(intern(&"col"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::source_util::expand_col));
syntax_expanders.insert(intern(&"file"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::source_util::expand_file));
syntax_expanders.insert(intern(&"stringify"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::source_util::expand_stringify));
syntax_expanders.insert(intern(&"include"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::source_util::expand_include));
syntax_expanders.insert(intern(&"include_str"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::source_util::expand_include_str));
syntax_expanders.insert(intern(&"include_bin"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::source_util::expand_include_bin));
syntax_expanders.insert(intern(&"module_path"),
- builtin_normal_tt(
+ builtin_normal_tt_no_ctxt(
ext::source_util::expand_mod));
syntax_expanders.insert(intern(&"asm"),
- builtin_normal_tt(ext::asm::expand_asm));
+ builtin_normal_tt_no_ctxt(ext::asm::expand_asm));
syntax_expanders.insert(intern(&"cfg"),
- builtin_normal_tt(ext::cfg::expand_cfg));
+ builtin_normal_tt_no_ctxt(ext::cfg::expand_cfg));
syntax_expanders.insert(
intern(&"trace_macros"),
- builtin_normal_tt(ext::trace_macros::expand_trace_macros));
+ builtin_normal_tt_no_ctxt(ext::trace_macros::expand_trace_macros));
MapChain::new(~syntax_expanders)
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use ast::{Block, Crate, NodeId, Expr_, ExprMac, Ident, mac_invoc_tt};
-use ast::{item_mac, Stmt_, StmtMac, StmtExpr, StmtSemi};
-use ast::{ILLEGAL_CTXT};
+use ast::{Block, Crate, NodeId, DeclLocal, Expr_, ExprMac, SyntaxContext};
+use ast::{Local, Ident, mac_invoc_tt};
+use ast::{item_mac, Mrk, Stmt_, StmtDecl, StmtMac, StmtExpr, StmtSemi};
+use ast::{token_tree};
use ast;
-use ast_util::{new_rename, new_mark, resolve};
+use ast_util::{mtwt_outer_mark, new_rename, new_mark};
use attr;
use attr::AttrMetaMethods;
use codemap;
use parse;
use parse::{parse_item_from_source_str};
use parse::token;
-use parse::token::{ident_to_str, intern};
+use parse::token::{fresh_mark, fresh_name, ident_to_str, intern};
use visit;
use visit::Visitor;
// entry-point for all syntax extensions.
ExprMac(ref mac) => {
match (*mac).node {
+ // it would almost certainly be cleaner to pass the whole
+ // macro invocation in, rather than pulling it apart and
+ // marking the tts and the ctxt separately. This also goes
+ // for the other three macro invocation chunks of code
+ // in this file.
// Token-tree macros:
- mac_invoc_tt(ref pth, ref tts) => {
+ mac_invoc_tt(ref pth, ref tts, ctxt) => {
if (pth.segments.len() > 1u) {
cx.span_fatal(
pth.span,
pth.span,
fmt!("macro undefined: '%s'", extnamestr))
}
- Some(@SE(NormalTT(SyntaxExpanderTT{
- expander: exp,
- span: exp_sp
- }))) => {
+ Some(@SE(NormalTT(expandfun, exp_span))) => {
cx.bt_push(ExpnInfo {
call_site: s,
callee: NameAndSpan {
name: extnamestr,
- span: exp_sp,
+ span: exp_span,
},
});
-
- let expanded = match exp(cx, mac.span, *tts) {
- MRExpr(e) => e,
- MRAny(expr_maker,_,_) => expr_maker(),
- _ => {
- cx.span_fatal(
- pth.span,
- fmt!(
- "non-expr macro in expr pos: %s",
- extnamestr
+ let fm = fresh_mark();
+ // mark before:
+ let marked_before = mark_tts(*tts,fm);
+ let marked_ctxt = new_mark(fm, ctxt);
+ let expanded =
+ match expandfun(cx, mac.span, marked_before, marked_ctxt) {
+ MRExpr(e) => e,
+ MRAny(expr_maker,_,_) => expr_maker(),
+ _ => {
+ cx.span_fatal(
+ pth.span,
+ fmt!(
+ "non-expr macro in expr pos: %s",
+ extnamestr
+ )
)
- )
- }
- };
+ }
+ };
+ // mark after:
+ let marked_after = mark_expr(expanded,fm);
//keep going, outside-in
let fully_expanded =
- fld.fold_expr(expanded).node.clone();
+ fld.fold_expr(marked_after).node.clone();
cx.bt_pop();
(fully_expanded, s)
ast::_mod { items: new_items, ..module_ }
}
-
// eval $e with a new exts frame:
macro_rules! with_exts_frame (
($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
fld: @ast_fold,
orig: @fn(@ast::item, @ast_fold) -> Option<@ast::item>)
-> Option<@ast::item> {
- // need to do expansion first... it might turn out to be a module.
- let maybe_it = match it.node {
- ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
- _ => Some(it)
- };
- match maybe_it {
- Some(it) => {
- match it.node {
- ast::item_mod(_) | ast::item_foreign_mod(_) => {
- cx.mod_push(it.ident);
- let macro_escape = contains_macro_escape(it.attrs);
- let result = with_exts_frame!(extsbox,macro_escape,orig(it,fld));
- cx.mod_pop();
- result
- }
- _ => orig(it,fld)
- }
- }
- None => None
+ match it.node {
+ ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
+ ast::item_mod(_) | ast::item_foreign_mod(_) => {
+ cx.mod_push(it.ident);
+ let macro_escape = contains_macro_escape(it.attrs);
+ let result = with_exts_frame!(extsbox,macro_escape,orig(it,fld));
+ cx.mod_pop();
+ result
+ },
+ _ => orig(it,fld)
}
}
cx: @ExtCtxt, it: @ast::item,
fld: @ast_fold)
-> Option<@ast::item> {
- let (pth, tts) = match it.node {
- item_mac(codemap::Spanned { node: mac_invoc_tt(ref pth, ref tts), _}) => {
- (pth, (*tts).clone())
+ let (pth, tts, ctxt) = match it.node {
+ item_mac(codemap::Spanned { node: mac_invoc_tt(ref pth, ref tts, ctxt), _}) => {
+ (pth, (*tts).clone(), ctxt)
}
_ => cx.span_bug(it.span, "invalid item macro invocation")
};
let extname = &pth.segments[0].identifier;
let extnamestr = ident_to_str(extname);
+ let fm = fresh_mark();
let expanded = match (*extsbox).find(&extname.name) {
None => cx.span_fatal(pth.span,
fmt!("macro undefined: '%s!'", extnamestr)),
- Some(@SE(NormalTT(ref expand))) => {
- if it.ident != parse::token::special_idents::invalid {
+ Some(@SE(NormalTT(expander, span))) => {
+ if it.ident.name != parse::token::special_idents::invalid.name {
cx.span_fatal(pth.span,
fmt!("macro %s! expects no ident argument, \
given '%s'", extnamestr,
call_site: it.span,
callee: NameAndSpan {
name: extnamestr,
- span: expand.span
+ span: span
}
});
- ((*expand).expander)(cx, it.span, tts)
+ // mark before expansion:
+ let marked_before = mark_tts(tts,fm);
+ let marked_ctxt = new_mark(fm,ctxt);
+ expander(cx, it.span, marked_before, marked_ctxt)
}
- Some(@SE(IdentTT(ref expand))) => {
- if it.ident == parse::token::special_idents::invalid {
+ Some(@SE(IdentTT(expander, span))) => {
+ if it.ident.name == parse::token::special_idents::invalid.name {
cx.span_fatal(pth.span,
fmt!("macro %s! expects an ident argument",
extnamestr));
call_site: it.span,
callee: NameAndSpan {
name: extnamestr,
- span: expand.span
+ span: span
}
});
- ((*expand).expander)(cx, it.span, it.ident, tts)
+ // mark before expansion:
+ let marked_tts = mark_tts(tts,fm);
+ let marked_ctxt = new_mark(fm,ctxt);
+ expander(cx, it.span, it.ident, marked_tts, marked_ctxt)
}
_ => cx.span_fatal(
it.span, fmt!("%s! is not legal in item position", extnamestr))
};
let maybe_it = match expanded {
- MRItem(it) => fld.fold_item(it),
+ MRItem(it) => mark_item(it,fm).chain(|i| {fld.fold_item(i)}),
MRExpr(_) => cx.span_fatal(pth.span,
fmt!("expr macro in item position: %s", extnamestr)),
- MRAny(_, item_maker, _) => item_maker().chain(|i| {fld.fold_item(i)}),
+ MRAny(_, item_maker, _) => item_maker().chain(|i| {mark_item(i,fm)})
+ .chain(|i| {fld.fold_item(i)}),
MRDef(ref mdef) => {
+ // yikes... no idea how to apply the mark to this. I'm afraid
+ // we're going to have to wait-and-see on this one.
insert_macro(*extsbox,intern(mdef.name), @SE((*mdef).ext));
None
}
orig: @fn(&Stmt_, Span, @ast_fold)
-> (Option<Stmt_>, Span))
-> (Option<Stmt_>, Span) {
- let (mac, pth, tts, semi) = match *s {
+ // why the copying here and not in expand_expr?
+ // looks like classic changed-in-only-one-place
+ let (mac, pth, tts, semi, ctxt) = match *s {
StmtMac(ref mac, semi) => {
match mac.node {
- mac_invoc_tt(ref pth, ref tts) => {
- ((*mac).clone(), pth, (*tts).clone(), semi)
+ mac_invoc_tt(ref pth, ref tts, ctxt) => {
+ ((*mac).clone(), pth, (*tts).clone(), semi, ctxt)
}
}
}
- _ => return orig(s, sp, fld)
+ _ => return expand_non_macro_stmt(*extsbox,s,sp,fld,orig)
};
if (pth.segments.len() > 1u) {
cx.span_fatal(
None =>
cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", extnamestr)),
- Some(@SE(NormalTT(
- SyntaxExpanderTT{expander: exp, span: exp_sp}))) => {
+ Some(@SE(NormalTT(expandfun, exp_span))) => {
cx.bt_push(ExpnInfo {
call_site: sp,
- callee: NameAndSpan { name: extnamestr, span: exp_sp }
+ callee: NameAndSpan { name: extnamestr, span: exp_span }
});
- let expanded = match exp(cx, mac.span, tts) {
+ let fm = fresh_mark();
+ // mark before expansion:
+ let marked_tts = mark_tts(tts,fm);
+ let marked_ctxt = new_mark(fm,ctxt);
+ let expanded = match expandfun(cx, mac.span, marked_tts, marked_ctxt) {
MRExpr(e) =>
@codemap::Spanned { node: StmtExpr(e, cx.next_id()),
span: e.span},
pth.span,
fmt!("non-stmt macro in stmt pos: %s", extnamestr))
};
+ let marked_after = mark_stmt(expanded,fm);
//keep going, outside-in
- let fully_expanded = match fld.fold_stmt(expanded) {
+ let fully_expanded = match fld.fold_stmt(marked_after) {
Some(stmt) => {
let fully_expanded = &stmt.node;
cx.bt_pop();
}
+// expand a non-macro stmt. this is essentially the fallthrough for
+// expand_stmt, above.
+fn expand_non_macro_stmt (exts: SyntaxEnv,
+ s: &Stmt_,
+ sp: Span,
+ fld: @ast_fold,
+ orig: @fn(&Stmt_, Span, @ast_fold) -> (Option<Stmt_>, Span))
+ -> (Option<Stmt_>,Span) {
+ // is it a let?
+ match *s {
+ StmtDecl(@Spanned{node: DeclLocal(ref local), span: stmt_span}, node_id) => {
+ let block_info = get_block_info(exts);
+ let pending_renames = block_info.pending_renames;
+
+ // take it apart:
+ let @Local{is_mutbl:is_mutbl,
+ ty:_,
+ pat:pat,
+ init:init,
+ id:id,
+ span:span
+ } = *local;
+ // types can't be copied automatically because of the owned ptr in ty_tup...
+ let ty = local.ty.clone();
+ // expand the pat (it might contain exprs... #:(o)>
+ let expanded_pat = fld.fold_pat(pat);
+ // find the pat_idents in the pattern:
+ // oh dear heaven... this is going to include the enum names, as well....
+ // ... but that should be okay, as long as the new names are gensyms
+ // for the old ones.
+ let idents = @mut ~[];
+ let name_finder = new_name_finder(idents);
+ name_finder.visit_pat(expanded_pat,());
+ // generate fresh names, push them to a new pending list
+ let new_pending_renames = @mut ~[];
+ for ident in idents.iter() {
+ let new_name = fresh_name(ident);
+ new_pending_renames.push((*ident,new_name));
+ }
+ let rename_fld = renames_to_fold(new_pending_renames);
+ // rewrite the pattern using the new names (the old ones
+ // have already been applied):
+ let rewritten_pat = rename_fld.fold_pat(expanded_pat);
+ // add them to the existing pending renames:
+ for pr in new_pending_renames.iter() {pending_renames.push(*pr)}
+ // also, don't forget to expand the init:
+ let new_init_opt = init.map(|e| fld.fold_expr(*e));
+ let rewritten_local =
+ @Local{is_mutbl:is_mutbl,
+ ty:ty,
+ pat:rewritten_pat,
+ init:new_init_opt,
+ id:id,
+ span:span};
+ (Some(StmtDecl(@Spanned{node:DeclLocal(rewritten_local),
+ span: stmt_span},node_id)),
+ sp)
+ },
+ _ => {
+ orig(s, sp, fld)
+ }
+ }
+}
+
+// a visitor that extracts the pat_ident paths
+// from a given thingy and puts them in a mutable
+// array (passed in to the traversal)
#[deriving(Clone)]
struct NewNameFinderContext {
ident_accumulator: @mut ~[ast::Ident],
}
}
+// a visitor that extracts the paths
+// from a given thingy and puts them in a mutable
+// array (passed in to the traversal)
+#[deriving(Clone)]
+struct NewPathExprFinderContext {
+ path_accumulator: @mut ~[ast::Path],
+}
+
+// XXX : YIKES a lot of boilerplate again....
+impl Visitor<()> for NewPathExprFinderContext {
+
+ fn visit_expr(&mut self, expr: @ast::Expr, _: ()) {
+ match *expr {
+ ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
+ self.path_accumulator.push(p.clone());
+ // not calling visit_path, should be fine.
+ }
+ _ => visit::walk_expr(self,expr,())
+ }
+ }
+
+
+ // XXX: Methods below can become default methods.
+
+ fn visit_pat(&mut self, pattern: @ast::Pat, _: ()) {
+ visit::walk_pat(self,pattern,())
+ }
+
+ fn visit_mod(&mut self, module: &ast::_mod, _: Span, _: NodeId, _: ()) {
+ visit::walk_mod(self, module, ())
+ }
+
+ fn visit_view_item(&mut self, view_item: &ast::view_item, _: ()) {
+ visit::walk_view_item(self, view_item, ())
+ }
+
+ fn visit_item(&mut self, item: @ast::item, _: ()) {
+ visit::walk_item(self, item, ())
+ }
+
+ fn visit_foreign_item(&mut self,
+ foreign_item: @ast::foreign_item,
+ _: ()) {
+ visit::walk_foreign_item(self, foreign_item, ())
+ }
+
+ fn visit_local(&mut self, local: @ast::Local, _: ()) {
+ visit::walk_local(self, local, ())
+ }
+
+ fn visit_block(&mut self, block: &ast::Block, _: ()) {
+ visit::walk_block(self, block, ())
+ }
+
+ fn visit_stmt(&mut self, stmt: @ast::Stmt, _: ()) {
+ visit::walk_stmt(self, stmt, ())
+ }
+
+ fn visit_arm(&mut self, arm: &ast::Arm, _: ()) {
+ visit::walk_arm(self, arm, ())
+ }
+
+ fn visit_decl(&mut self, decl: @ast::Decl, _: ()) {
+ visit::walk_decl(self, decl, ())
+ }
+
+ fn visit_expr_post(&mut self, _: @ast::Expr, _: ()) {
+ // Empty!
+ }
+
+ fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
+ visit::walk_ty(self, typ, ())
+ }
+
+ fn visit_generics(&mut self, generics: &ast::Generics, _: ()) {
+ visit::walk_generics(self, generics, ())
+ }
+
+ fn visit_fn(&mut self,
+ function_kind: &visit::fn_kind,
+ function_declaration: &ast::fn_decl,
+ block: &ast::Block,
+ span: Span,
+ node_id: NodeId,
+ _: ()) {
+ visit::walk_fn(self,
+ function_kind,
+ function_declaration,
+ block,
+ span,
+ node_id,
+ ())
+ }
+
+ fn visit_ty_method(&mut self, ty_method: &ast::TypeMethod, _: ()) {
+ visit::walk_ty_method(self, ty_method, ())
+ }
+
+ fn visit_trait_method(&mut self,
+ trait_method: &ast::trait_method,
+ _: ()) {
+ visit::walk_trait_method(self, trait_method, ())
+ }
+
+ fn visit_struct_def(&mut self,
+ struct_def: @ast::struct_def,
+ ident: Ident,
+ generics: &ast::Generics,
+ node_id: NodeId,
+ _: ()) {
+ visit::walk_struct_def(self,
+ struct_def,
+ ident,
+ generics,
+ node_id,
+ ())
+ }
+
+ fn visit_struct_field(&mut self,
+ struct_field: @ast::struct_field,
+ _: ()) {
+ visit::walk_struct_field(self, struct_field, ())
+ }
+}
+
// return a visitor that extracts the pat_ident paths
-// from a given pattern and puts them in a mutable
+// from a given thingy and puts them in a mutable
// array (passed in to the traversal)
pub fn new_name_finder(idents: @mut ~[ast::Ident]) -> @mut Visitor<()> {
let context = @mut NewNameFinderContext {
context as @mut Visitor<()>
}
+// return a visitor that extracts the paths
+// from a given pattern and puts them in a mutable
+// array (passed in to the traversal)
+pub fn new_path_finder(paths: @mut ~[ast::Path]) -> @mut Visitor<()> {
+ let context = @mut NewPathExprFinderContext {
+ path_accumulator: paths,
+ };
+ context as @mut Visitor<()>
+}
+
+// expand a block. pushes a new exts_frame, then calls expand_block_elts
pub fn expand_block(extsbox: @mut SyntaxEnv,
_cx: @ExtCtxt,
blk: &Block,
fld: @ast_fold,
- orig: @fn(&Block, @ast_fold) -> Block)
+ _orig: @fn(&Block, @ast_fold) -> Block)
-> Block {
// see note below about treatment of exts table
- with_exts_frame!(extsbox,false,orig(blk,fld))
+ with_exts_frame!(extsbox,false,
+ expand_block_elts(*extsbox, blk, fld))
+}
+
+// expand the elements of a block.
+pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: @ast_fold) -> Block {
+ let block_info = get_block_info(exts);
+ let pending_renames = block_info.pending_renames;
+ let rename_fld = renames_to_fold(pending_renames);
+ let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
+ let mut new_stmts = ~[];
+ for x in b.stmts.iter() {
+ match fld.fold_stmt(mustbesome(rename_fld.fold_stmt(*x))) {
+ Some(s) => new_stmts.push(s),
+ None => ()
+ }
+ }
+ let new_expr = b.expr.map(|x| fld.fold_expr(rename_fld.fold_expr(*x)));
+ Block{
+ view_items: new_view_items,
+ stmts: new_stmts,
+ expr: new_expr,
+ id: fld.new_id(b.id),
+ rules: b.rules,
+ span: b.span,
+ }
}
+// rename_fold should never return "None".
+// (basically, just .get() with a better message...)
+fn mustbesome<T>(val : Option<T>) -> T {
+ match val {
+ Some(v) => v,
+ None => fail!("rename_fold returned None")
+ }
+}
// get the (innermost) BlockInfo from an exts stack
fn get_block_info(exts : SyntaxEnv) -> BlockInfo {
}
}
-
-// given a mutable list of renames, return a tree-folder that applies those
-// renames.
-fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold {
- let afp = default_ast_fold();
- let f_pre = @AstFoldFns {
- fold_ident: |id,_| {
- // the individual elements are memoized... it would
- // also be possible to memoize on the whole list at once.
- let new_ctxt = renames.iter().fold(id.ctxt,|ctxt,&(from,to)| {
- new_rename(from,to,ctxt)
- });
- ast::Ident{name:id.name,ctxt:new_ctxt}
- },
- .. *afp
- };
- make_fold(f_pre)
-}
-
-// perform a bunch of renames
-fn apply_pending_renames(folder : @ast_fold, stmt : ast::Stmt) -> @ast::Stmt {
- match folder.fold_stmt(&stmt) {
- Some(s) => s,
- None => fail!(fmt!("renaming of stmt produced None"))
- }
-}
-
-
-
pub fn new_span(cx: @ExtCtxt, sp: Span) -> Span {
/* this discards information in the case of macro-defining macros */
return Span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
}
// FIXME (#2247): this is a moderately bad kludge to inject some macros into
-// the default compilation environment. It would be much nicer to use
-// a mechanism like syntax_quote to ensure hygiene.
+// the default compilation environment in that it injects strings, rather than
+// syntax elements.
pub fn std_macros() -> @str {
return
return ret;
}
-// given a function from idents to idents, produce
-// an ast_fold that applies that function:
-pub fn fun_to_ident_folder(f: @fn(ast::Ident)->ast::Ident) -> @ast_fold{
- let afp = default_ast_fold();
- let f_pre = @AstFoldFns{
- fold_ident : |id, _| f(id),
- .. *afp
- };
- make_fold(f_pre)
+// HYGIENIC CONTEXT EXTENSION:
+// all of these functions are for walking over
+// ASTs and making some change to the context of every
+// element that has one. a CtxtFn is a trait-ified
+// version of a closure in (SyntaxContext -> SyntaxContext).
+// the ones defined here include:
+// Renamer - add a rename to a context
+// MultiRenamer - add a set of renames to a context
+// Marker - add a mark to a context
+// Repainter - replace a context (maybe Replacer would be a better name?)
+
+// a function in SyntaxContext -> SyntaxContext
+pub trait CtxtFn{
+ fn f(&self, ast::SyntaxContext) -> ast::SyntaxContext;
}
-// update the ctxts in a path to get a rename node
-pub fn new_ident_renamer(from: ast::Ident,
- to: ast::Name) ->
- @fn(ast::Ident)->ast::Ident {
- |id : ast::Ident|
- ast::Ident{
- name: id.name,
- ctxt: new_rename(from,to,id.ctxt)
+// a renamer adds a rename to the syntax context
+pub struct Renamer {
+ from : ast::Ident,
+ to : ast::Name
+}
+
+impl CtxtFn for Renamer {
+ fn f(&self, ctxt : ast::SyntaxContext) -> ast::SyntaxContext {
+ new_rename(self.from,self.to,ctxt)
+ }
+}
+
+// a renamer that performs a whole bunch of renames
+pub struct MultiRenamer {
+ renames : @mut ~[(ast::Ident,ast::Name)]
+}
+
+impl CtxtFn for MultiRenamer {
+ fn f(&self, starting_ctxt : ast::SyntaxContext) -> ast::SyntaxContext {
+ // the individual elements are memoized... it would
+ // also be possible to memoize on the whole list at once.
+ self.renames.iter().fold(starting_ctxt,|ctxt,&(from,to)| {
+ new_rename(from,to,ctxt)
+ })
+ }
+}
+
+// a marker adds the given mark to the syntax context
+pub struct Marker { mark : Mrk }
+
+impl CtxtFn for Marker {
+ fn f(&self, ctxt : ast::SyntaxContext) -> ast::SyntaxContext {
+ new_mark(self.mark,ctxt)
}
}
+// a repainter just replaces the given context with the one it's closed over
+pub struct Repainter { ctxt : SyntaxContext }
-// update the ctxts in a path to get a mark node
-pub fn new_ident_marker(mark: uint) ->
- @fn(ast::Ident)->ast::Ident {
- |id : ast::Ident|
- ast::Ident{
- name: id.name,
- ctxt: new_mark(mark,id.ctxt)
+impl CtxtFn for Repainter {
+ fn f(&self, _ctxt : ast::SyntaxContext) -> ast::SyntaxContext {
+ self.ctxt
}
}
-// perform resolution (in the MTWT sense) on all of the
-// idents in the tree. This is the final step in expansion.
-pub fn new_ident_resolver() ->
- @fn(ast::Ident)->ast::Ident {
- |id : ast::Ident|
- ast::Ident {
- name : resolve(id),
- ctxt : ILLEGAL_CTXT
+// given a function from ctxts to ctxts, produce
+// an ast_fold that applies that function to all ctxts:
+pub fn fun_to_ctxt_folder<T : 'static + CtxtFn>(cf: @T) -> @AstFoldFns {
+ let afp = default_ast_fold();
+ let fi : @fn(ast::Ident, @ast_fold) -> ast::Ident =
+ |ast::Ident{name, ctxt}, _| {
+ ast::Ident{name:name,ctxt:cf.f(ctxt)}
+ };
+ let fm : @fn(&ast::mac_, Span, @ast_fold) -> (ast::mac_,Span) =
+ |m, sp, fld| {
+ match *m {
+ mac_invoc_tt(ref path, ref tts, ctxt) =>
+ (mac_invoc_tt(fld.fold_path(path),
+ fold_tts(*tts,fld),
+ cf.f(ctxt)),
+ sp)
+ }
+
+ };
+ @AstFoldFns{
+ fold_ident : fi,
+ fold_mac : fm,
+ .. *afp
}
}
+
+// given a mutable list of renames, return a tree-folder that applies those
+// renames.
+// FIXME #4536: currently pub to allow testing
+pub fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @AstFoldFns {
+ fun_to_ctxt_folder(@MultiRenamer{renames : renames})
+}
+
+// just a convenience:
+pub fn new_mark_folder(m : Mrk) -> @AstFoldFns {
+ fun_to_ctxt_folder(@Marker{mark:m})
+}
+
+pub fn new_rename_folder(from : ast::Ident, to : ast::Name) -> @AstFoldFns {
+ fun_to_ctxt_folder(@Renamer{from:from,to:to})
+}
+
+// apply a given mark to the given token trees. Used prior to expansion of a macro.
+fn mark_tts(tts : &[token_tree], m : Mrk) -> ~[token_tree] {
+ fold_tts(tts,new_mark_folder(m) as @ast_fold)
+}
+
+// apply a given mark to the given expr. Used following the expansion of a macro.
+fn mark_expr(expr : @ast::Expr, m : Mrk) -> @ast::Expr {
+ new_mark_folder(m).fold_expr(expr)
+}
+
+// apply a given mark to the given stmt. Used following the expansion of a macro.
+fn mark_stmt(expr : &ast::Stmt, m : Mrk) -> @ast::Stmt {
+ new_mark_folder(m).fold_stmt(expr).unwrap()
+}
+
+// apply a given mark to the given item. Used following the expansion of a macro.
+fn mark_item(expr : @ast::item, m : Mrk) -> Option<@ast::item> {
+ new_mark_folder(m).fold_item(expr)
+}
+
+// replace all contexts in a given expr with the given mark. Used
+// for capturing macros
+pub fn replace_ctxts(expr : @ast::Expr, ctxt : SyntaxContext) -> @ast::Expr {
+ fun_to_ctxt_folder(@Repainter{ctxt:ctxt}).fold_expr(expr)
+}
+
+// take the mark from the given ctxt (that has a mark at the outside),
+// and apply it to everything in the token trees, thereby cancelling
+// that mark.
+pub fn mtwt_cancel_outer_mark(tts: &[ast::token_tree], ctxt: ast::SyntaxContext)
+ -> ~[ast::token_tree] {
+ let outer_mark = mtwt_outer_mark(ctxt);
+ mark_tts(tts,outer_mark)
+}
+
+
#[cfg(test)]
mod test {
use super::*;
use ast;
use ast::{Attribute_, AttrOuter, MetaWord, EMPTY_CTXT};
+ use ast_util::{get_sctable, mtwt_marksof, mtwt_resolve, new_rename};
+ use ast_util;
use codemap;
use codemap::Spanned;
+ use fold;
use parse;
- use parse::token::{intern, get_ident_interner};
+ use parse::token::{fresh_mark, gensym, intern, get_ident_interner, ident_to_str};
+ use parse::token;
use print::pprust;
- use util::parser_testing::{string_to_item, string_to_pat, strs_to_idents};
+ use std;
+ use util::parser_testing::{string_to_crate, string_to_crate_and_sess};
+ use util::parser_testing::{string_to_pat, string_to_tts, strs_to_idents};
+ use visit;
// make sure that fail! is present
#[test] fn fail_exists_test () {
}
}
+ #[test] fn cancel_outer_mark_test(){
+ let invalid_name = token::special_idents::invalid.name;
+ let ident_str = @"x";
+ let tts = string_to_tts(ident_str);
+ let fm = fresh_mark();
+ let marked_once = fold::fold_tts(tts,new_mark_folder(fm) as @fold::ast_fold);
+ assert_eq!(marked_once.len(),1);
+ let marked_once_ctxt =
+ match marked_once[0] {
+ ast::tt_tok(_,token::IDENT(id,_)) => id.ctxt,
+ _ => fail!(fmt!("unexpected shape for marked tts: %?",marked_once[0]))
+ };
+ assert_eq!(mtwt_marksof(marked_once_ctxt,invalid_name),~[fm]);
+ let remarked = mtwt_cancel_outer_mark(marked_once,marked_once_ctxt);
+ assert_eq!(remarked.len(),1);
+ match remarked[0] {
+ ast::tt_tok(_,token::IDENT(id,_)) =>
+ assert_eq!(mtwt_marksof(id.ctxt,invalid_name),~[]),
+ _ => fail!(fmt!("unexpected shape for marked tts: %?",remarked[0]))
+ }
+ }
+
#[test]
fn renaming () {
- let maybe_item_ast = string_to_item(@"fn a() -> int { let b = 13; b }");
- let item_ast = match maybe_item_ast {
- Some(x) => x,
- None => fail!("test case fail")
- };
+ let item_ast = string_to_crate(@"fn f() -> int { a }");
let a_name = intern("a");
- let a2_name = intern("a2");
- let renamer = new_ident_renamer(ast::Ident{name:a_name,ctxt:EMPTY_CTXT},
+ let a2_name = gensym("a2");
+ let renamer = new_rename_folder(ast::Ident{name:a_name,ctxt:EMPTY_CTXT},
a2_name);
- let renamed_ast = fun_to_ident_folder(renamer).fold_item(item_ast).unwrap();
- let resolver = new_ident_resolver();
- let resolved_ast = fun_to_ident_folder(resolver).fold_item(renamed_ast).unwrap();
- let resolved_as_str = pprust::item_to_str(resolved_ast,
- get_ident_interner());
- assert_eq!(resolved_as_str,~"fn a2() -> int { let b = 13; b }");
+ let renamed_ast = renamer.fold_crate(item_ast);
+ let varrefs = @mut ~[];
+ visit::walk_crate(&mut new_path_finder(varrefs), &renamed_ast, ());
+ match varrefs {
+ @[Path{segments:[ref seg],_}] => assert_eq!(mtwt_resolve(seg.identifier),a2_name),
+ _ => assert_eq!(0,1)
+ }
+
+ // try a double-rename, with pending_renames.
+ let a3_name = gensym("a3");
+ // a context that renames from ("a",empty) to "a2" :
+ let ctxt2 = new_rename(ast::Ident::new(a_name),a2_name,EMPTY_CTXT);
+ let pending_renames = @mut ~[(ast::Ident::new(a_name),a2_name),
+ (ast::Ident{name:a_name,ctxt:ctxt2},a3_name)];
+ let double_renamed = renames_to_fold(pending_renames).fold_crate(item_ast);
+ let varrefs = @mut ~[];
+ visit::walk_crate(&mut new_path_finder(varrefs), &double_renamed, ());
+ match varrefs {
+ @[Path{segments:[ref seg],_}] => assert_eq!(mtwt_resolve(seg.identifier),a3_name),
+ _ => assert_eq!(0,1)
+ }
+ }
+ fn fake_print_crate(crate: @ast::Crate) {
+ let s = pprust::rust_printer(std::io::stderr(),get_ident_interner());
+ pprust::print_crate_(s, crate);
+ }
+ fn expand_crate_str(crate_str: @str) -> @ast::Crate {
+ let (crate_ast,ps) = string_to_crate_and_sess(crate_str);
+ // the cfg argument actually does matter, here...
+ expand_crate(ps,~[],crate_ast)
}
- // sigh... it looks like I have two different renaming mechanisms, now...
+ //fn expand_and_resolve(crate_str: @str) -> ast::crate {
+ //let expanded_ast = expand_crate_str(crate_str);
+ // std::io::println(fmt!("expanded: %?\n",expanded_ast));
+ //mtwt_resolve_crate(expanded_ast)
+ //}
+ //fn expand_and_resolve_and_pretty_print (crate_str : @str) -> ~str {
+ //let resolved_ast = expand_and_resolve(crate_str);
+ //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
+ //}
+
+ #[test] fn macro_tokens_should_match(){
+ expand_crate_str(@"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
+ }
+
+ // renaming tests expand a crate and then check that the bindings match
+ // the right varrefs. The specification of the test case includes the
+ // text of the crate, and also an array of arrays. Each element in the
+ // outer array corresponds to a binding in the traversal of the AST
+ // induced by visit. Each of these arrays contains a list of indexes,
+ // interpreted as the varrefs in the varref traversal that this binding
+ // should match. So, for instance, in a program with two bindings and
+ // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
+ // binding should match the second two varrefs, and the second binding
+ // should match the first varref.
+ //
+ // The comparisons are done post-mtwt-resolve, so we're comparing renamed
+ // names; differences in marks don't matter any more.
+ //
+ // oog... I also want tests that check "binding-identifier-=?". That is,
+ // not just "do these have the same name", but "do they have the same
+ // name *and* the same marks"? Understanding this is really pretty painful.
+ // in principle, you might want to control this boolean on a per-varref basis,
+ // but that would make things even harder to understand, and might not be
+ // necessary for thorough testing.
+ type renaming_test = (&'static str, ~[~[uint]], bool);
+
+ #[test]
+ fn automatic_renaming () {
+ let tests : ~[renaming_test] =
+ ~[// b & c should get new names throughout, in the expr too:
+ ("fn a() -> int { let b = 13; let c = b; b+c }",
+ ~[~[0,1],~[2]], false),
+ // both x's should be renamed (how is this causing a bug?)
+ ("fn main () {let x : int = 13;x;}",
+ ~[~[0]], false),
+ // the use of b after the + should be renamed, the other one not:
+ ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
+ ~[~[1]], false),
+ // the b before the plus should not be renamed (requires marks)
+ ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
+ ~[~[1]], false),
+ // the marks going in and out of letty should cancel, allowing that $x to
+ // capture the one following the semicolon.
+ // this was an awesome test case, and caught a *lot* of bugs.
+ ("macro_rules! letty(($x:ident) => (let $x = 15;))
+ macro_rules! user(($x:ident) => ({letty!($x); $x}))
+ fn main() -> int {user!(z)}",
+ ~[~[0]], false),
+ // FIXME #8062: this test exposes a *potential* bug; our system does
+ // not behave exactly like MTWT, but I haven't thought of a way that
+ // this could cause a bug in Rust, yet.
+ // ("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
+ // ~[~[0]], true)
+ // FIXME #6994: the next string exposes the bug referred to in issue 6994, so I'm
+ // commenting it out.
+ // the z flows into and out of two macros (g & f) along one path, and one
+ // (just g) along the other, so the result of the whole thing should
+ // be "let z_123 = 3; z_123"
+ //"macro_rules! g (($x:ident) =>
+ // ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}))
+ // fn a(){g!(z)}"
+ // create a really evil test case where a $x appears inside a binding of $x
+ // but *shouldnt* bind because it was inserted by a different macro....
+ ];
+ for (idx,s) in tests.iter().enumerate() {
+ run_renaming_test(s,idx);
+ }
+ }
+
+ // run one of the renaming tests
+ fn run_renaming_test(t : &renaming_test, test_idx: uint) {
+ let invalid_name = token::special_idents::invalid.name;
+ let (teststr, bound_connections, bound_ident_check) = match *t {
+ (ref str,ref conns, bic) => (str.to_managed(), conns.clone(), bic)
+ };
+ let cr = expand_crate_str(teststr.to_managed());
+ // find the bindings:
+ let bindings = @mut ~[];
+ visit::walk_crate(&mut new_name_finder(bindings),cr,());
+ // find the varrefs:
+ let varrefs = @mut ~[];
+ visit::walk_crate(&mut new_path_finder(varrefs),cr,());
+ // must be one check clause for each binding:
+ assert_eq!(bindings.len(),bound_connections.len());
+ for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
+ let binding_name = mtwt_resolve(bindings[binding_idx]);
+ let binding_marks = mtwt_marksof(bindings[binding_idx].ctxt,invalid_name);
+ // shouldmatch can't name varrefs that don't exist:
+ assert!((shouldmatch.len() == 0) ||
+ (varrefs.len() > *shouldmatch.iter().max().unwrap()));
+ for (idx,varref) in varrefs.iter().enumerate() {
+ if shouldmatch.contains(&idx) {
+ // it should be a path of length 1, and it should
+ // be free-identifier=? or bound-identifier=? to the given binding
+ assert_eq!(varref.segments.len(),1);
+ let varref_name = mtwt_resolve(varref.segments[0].identifier);
+ let varref_marks = mtwt_marksof(varref.segments[0].identifier.ctxt,
+ invalid_name);
+ if (!(varref_name==binding_name)){
+ std::io::println("uh oh, should match but doesn't:");
+ std::io::println(fmt!("varref: %?",varref));
+ std::io::println(fmt!("binding: %?", bindings[binding_idx]));
+ ast_util::display_sctable(get_sctable());
+ }
+ assert_eq!(varref_name,binding_name);
+ if (bound_ident_check) {
+ // we're checking bound-identifier=?, and the marks
+ // should be the same, too:
+ assert_eq!(varref_marks,binding_marks.clone());
+ }
+ } else {
+ let fail = (varref.segments.len() == 1)
+ && (mtwt_resolve(varref.segments[0].identifier) == binding_name);
+ // temp debugging:
+ if (fail) {
+ println!("failure on test {}",test_idx);
+ println!("text of test case: \"{}\"", teststr);
+ println!("");
+ println!("uh oh, matches but shouldn't:");
+ std::io::println(fmt!("varref: %?",varref));
+ // good lord, you can't make a path with 0 segments, can you?
+ println!("varref's first segment's uint: {}, and string: \"{}\"",
+ varref.segments[0].identifier.name,
+ ident_to_str(&varref.segments[0].identifier));
+ std::io::println(fmt!("binding: %?", bindings[binding_idx]));
+ ast_util::display_sctable(get_sctable());
+ }
+ assert!(!fail);
+ }
+ }
+ }
+ }
+
+ #[test] fn fmt_in_macro_used_inside_module_macro() {
+ let crate_str = @"macro_rules! fmt_wrap(($b:expr)=>(fmt!(\"left: %?\", $b)))
+macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
+foo_module!()
+";
+ let cr = expand_crate_str(crate_str);
+ // find the xx binding
+ let bindings = @mut ~[];
+ visit::walk_crate(&mut new_name_finder(bindings), cr, ());
+ let cxbinds : ~[&ast::Ident] =
+ bindings.iter().filter(|b|{@"xx" == (ident_to_str(*b))}).collect();
+ let cxbind = match cxbinds {
+ [b] => b,
+ _ => fail!("expected just one binding for ext_cx")
+ };
+ let resolved_binding = mtwt_resolve(*cxbind);
+ // find all the xx varrefs:
+ let varrefs = @mut ~[];
+ visit::walk_crate(&mut new_path_finder(varrefs), cr, ());
+ // the xx binding should bind all of the xx varrefs:
+ for (idx,v) in varrefs.iter().filter(|p|{ p.segments.len() == 1
+ && (@"xx" == (ident_to_str(&p.segments[0].identifier)))
+ }).enumerate() {
+ if (mtwt_resolve(v.segments[0].identifier) != resolved_binding) {
+ std::io::println("uh oh, xx binding didn't match xx varref:");
+ std::io::println(fmt!("this is xx varref # %?",idx));
+ std::io::println(fmt!("binding: %?",cxbind));
+ std::io::println(fmt!("resolves to: %?",resolved_binding));
+ std::io::println(fmt!("varref: %?",v.segments[0].identifier));
+ std::io::println(fmt!("resolves to: %?",mtwt_resolve(v.segments[0].identifier)));
+ let table = get_sctable();
+ std::io::println("SC table:");
+ for (idx,val) in table.table.iter().enumerate() {
+ std::io::println(fmt!("%4u : %?",idx,val));
+ }
+ }
+ assert_eq!(mtwt_resolve(v.segments[0].identifier),resolved_binding);
+ };
+ }
#[test]
fn pat_idents(){
pat_idents.visit_pat(pat, ());
assert_eq!(idents, @mut strs_to_idents(~["a","c","b","d"]));
}
+
}
sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
let (cx_expr, expr) = expand_tts(cx, sp, tts);
- base::MRExpr(expand_wrapper(cx, sp, cx_expr, expr))
+ let expanded = expand_wrapper(cx, sp, cx_expr, expr);
+ base::MRExpr(expanded)
}
pub fn expand_quote_expr(cx: @ExtCtxt,
sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
- base::MRExpr(expand_parse_call(cx, sp, "parse_expr", ~[], tts))
+ let expanded = expand_parse_call(cx, sp, "parse_expr", ~[], tts);
+ base::MRExpr(expanded)
}
pub fn expand_quote_item(cx: @ExtCtxt,
sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
let e_attrs = cx.expr_vec_uniq(sp, ~[]);
- base::MRExpr(expand_parse_call(cx, sp, "parse_item",
- ~[e_attrs], tts))
+ let expanded = expand_parse_call(cx, sp, "parse_item",
+ ~[e_attrs], tts);
+ base::MRExpr(expanded)
}
pub fn expand_quote_pat(cx: @ExtCtxt,
sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
let e_refutable = cx.expr_lit(sp, ast::lit_bool(true));
- base::MRExpr(expand_parse_call(cx, sp, "parse_pat",
- ~[e_refutable], tts))
+ let expanded = expand_parse_call(cx, sp, "parse_pat",
+ ~[e_refutable], tts);
+ base::MRExpr(expanded)
}
pub fn expand_quote_ty(cx: @ExtCtxt,
sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
let e_param_colons = cx.expr_lit(sp, ast::lit_bool(false));
- base::MRExpr(expand_parse_call(cx, sp, "parse_ty",
- ~[e_param_colons], tts))
+ let expanded = expand_parse_call(cx, sp, "parse_ty",
+ ~[e_param_colons], tts);
+ base::MRExpr(expanded)
}
pub fn expand_quote_stmt(cx: @ExtCtxt,
sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
let e_attrs = cx.expr_vec_uniq(sp, ~[]);
- base::MRExpr(expand_parse_call(cx, sp, "parse_stmt",
- ~[e_attrs], tts))
+ let expanded = expand_parse_call(cx, sp, "parse_stmt",
+ ~[e_attrs], tts);
+ base::MRExpr(expanded)
}
fn ids_ext(strs: ~[~str]) -> ~[ast::Ident] {
}
}
+// perform a token equality check, ignoring syntax context (that is, an unhygienic comparison)
+pub fn token_name_eq(t1 : &Token, t2 : &Token) -> bool {
+ match (t1,t2) {
+ (&token::IDENT(id1,_),&token::IDENT(id2,_)) =>
+ id1.name == id2.name,
+ _ => *t1 == *t2
+ }
+}
+
pub fn parse(
sess: @mut ParseSess,
cfg: ast::CrateConfig,
// the *_t vars are workarounds for the lack of unary move
match ei.sep {
Some(ref t) if idx == len => { // we need a separator
- if tok == (*t) { //pass the separator
+ // i'm conflicted about whether this should be hygienic....
+ // though in this case, if the separators are never legal
+ // idents, it shouldn't matter.
+ if token_name_eq(&tok, t) { //pass the separator
let mut ei_t = ei.clone();
ei_t.idx += 1;
next_eis.push(ei_t);
match_nonterminal(_,_,_) => { bb_eis.push(ei) }
match_tok(ref t) => {
let mut ei_t = ei.clone();
- if (*t) == tok {
+ //if (token_name_eq(t,&tok)) {
+ if (token::mtwt_token_eq(t,&tok)) {
ei_t.idx += 1;
next_eis.push(ei_t);
}
}
/* error messages here could be improved with links to orig. rules */
- if tok == EOF {
+ if token_name_eq(&tok, &EOF) {
if eof_eis.len() == 1u {
let mut v = ~[];
for dv in eof_eis[0u].matches.mut_iter() {
use codemap::{Span, Spanned, dummy_sp};
use ext::base::{ExtCtxt, MacResult, MRAny, MRDef, MacroDef, NormalTT};
use ext::base;
+use ext::expand;
use ext::tt::macro_parser::{error};
use ext::tt::macro_parser::{named_match, matched_seq, matched_nonterminal};
use ext::tt::macro_parser::{parse, parse_or_else, success, failure};
use parse::token::{FAT_ARROW, SEMI, nt_matchers, nt_tt};
use print;
+// this procedure performs the expansion of the
+// macro_rules! macro. It parses the RHS and adds
+// an extension to the current context.
pub fn add_new_extension(cx: @ExtCtxt,
sp: Span,
name: Ident,
- arg: ~[ast::token_tree])
+ arg: ~[ast::token_tree],
+ stx_ctxt: ast::SyntaxContext)
-> base::MacResult {
+ let arg = expand::mtwt_cancel_outer_mark(arg,stx_ctxt);
+ // Wrap a matcher_ in a spanned to produce a matcher.
// these spans won't matter, anyways
fn ms(m: matcher_) -> matcher {
Spanned {
let lhs_nm = gensym_ident("lhs");
let rhs_nm = gensym_ident("rhs");
+ // The pattern that macro_rules matches.
// The grammar for macro_rules! is:
// $( $lhs:mtcs => $rhs:tt );+
// ...quasiquoting this would be nice.
cx.span_fatal(best_fail_spot, best_fail_msg);
}
- let exp: @fn(@ExtCtxt, Span, &[ast::token_tree]) -> MacResult =
- |cx, sp, arg| generic_extension(cx, sp, name, arg, *lhses, *rhses);
+ let exp: @fn(@ExtCtxt, Span, &[ast::token_tree], ctxt: ast::SyntaxContext) -> MacResult =
+ |cx, sp, arg, _ctxt| generic_extension(cx, sp, name, arg, *lhses, *rhses);
return MRDef(MacroDef{
name: ident_to_str(&name),
- ext: NormalTT(base::SyntaxExpanderTT{expander: exp, span: Some(sp)})
+ ext: NormalTT(exp, Some(sp))
});
}
use parse::token;
use opt_vec::OptVec;
+// this file defines an ast_fold trait for objects that can perform
+// a "fold" on Rust ASTs. It also contains a structure that implements
+// that trait, and a "default_fold" whose fields contain closures
+// that perform "default traversals", visiting all of the sub-elements
+// and re-assembling the result. The "fun_to_ident_folder" in the
+// test module provides a simple example of creating a very simple
+// fold that only looks at identifiers.
+
pub trait ast_fold {
fn fold_crate(@self, &Crate) -> Crate;
fn fold_view_item(@self, &view_item) -> view_item;
fn fold_ident(@self, Ident) -> Ident;
fn fold_path(@self, &Path) -> Path;
fn fold_local(@self, @Local) -> @Local;
+ fn fold_mac(@self, &mac) -> mac;
fn map_exprs(@self, @fn(@Expr) -> @Expr, &[@Expr]) -> ~[@Expr];
fn new_id(@self, NodeId) -> NodeId;
fn new_span(@self, Span) -> Span;
fold_ident: @fn(Ident, @ast_fold) -> Ident,
fold_path: @fn(&Path, @ast_fold) -> Path,
fold_local: @fn(@Local, @ast_fold) -> @Local,
+ fold_mac: @fn(&mac_, Span, @ast_fold) -> (mac_, Span),
map_exprs: @fn(@fn(@Expr) -> @Expr, &[@Expr]) -> ~[@Expr],
new_id: @fn(NodeId) -> NodeId,
new_span: @fn(Span) -> Span
}
}
-//used in noop_fold_expr, and possibly elsewhere in the future
-fn fold_mac_(m: &mac, fld: @ast_fold) -> mac {
- Spanned {
- node: match m.node {
- mac_invoc_tt(ref p,ref tts) =>
- mac_invoc_tt(fld.fold_path(p),
- fold_tts(*tts,fld))
- },
- span: fld.new_span(m.span)
- }
-}
-
-fn fold_tts(tts : &[token_tree], fld: @ast_fold) -> ~[token_tree] {
+// build a new vector of tts by appling the ast_fold's fold_ident to
+// all of the identifiers in the token trees.
+pub fn fold_tts(tts : &[token_tree], f : @ast_fold) -> ~[token_tree] {
do tts.map |tt| {
match *tt {
tt_tok(span, ref tok) =>
- tt_tok(span,maybe_fold_ident(tok,fld)),
+ tt_tok(span,maybe_fold_ident(tok,f)),
tt_delim(ref tts) =>
- tt_delim(@mut fold_tts(**tts, fld)),
+ tt_delim(@mut fold_tts(**tts, f)),
tt_seq(span, ref pattern, ref sep, is_optional) =>
tt_seq(span,
- @mut fold_tts(**pattern, fld),
- sep.map(|tok|maybe_fold_ident(tok,fld)),
+ @mut fold_tts(**pattern, f),
+ sep.map(|tok|maybe_fold_ident(tok,f)),
is_optional),
tt_nonterminal(sp,ref ident) =>
- tt_nonterminal(sp,fld.fold_ident(*ident))
+ tt_nonterminal(sp,f.fold_ident(*ident))
}
}
}
// apply ident folder if it's an ident, otherwise leave it alone
-fn maybe_fold_ident(t: &token::Token, fld: @ast_fold) -> token::Token {
+fn maybe_fold_ident(t : &token::Token, f: @ast_fold) -> token::Token {
match *t {
token::IDENT(id,followed_by_colons) =>
- token::IDENT(fld.fold_ident(id),followed_by_colons),
+ token::IDENT(f.fold_ident(id),followed_by_colons),
_ => (*t).clone()
}
}
}
fn noop_fold_view_item(vi: &view_item_, _fld: @ast_fold) -> view_item_ {
+ // FIXME #7654: doesn't iterate over idents in a view_item_use
return /* FIXME (#2543) */ (*vi).clone();
}
)
}
item_mac(ref m) => {
- // FIXME #2888: we might actually want to do something here.
- // ... okay, we're doing something. It would probably be nicer
- // to add something to the ast_fold trait, but I'll defer
- // that work.
- item_mac(fold_mac_(m,fld))
+ item_mac(fld.fold_mac(m))
}
}
}
}
fn noop_fold_stmt(s: &Stmt_, fld: @ast_fold) -> Option<Stmt_> {
- let fold_mac = |x| fold_mac_(x, fld);
match *s {
StmtDecl(d, nid) => {
match fld.fold_decl(d) {
StmtSemi(e, nid) => {
Some(StmtSemi(fld.fold_expr(e), fld.new_id(nid)))
}
- StmtMac(ref mac, semi) => Some(StmtMac(fold_mac(mac), semi))
+ StmtMac(ref mac, semi) => Some(StmtMac(fld.fold_mac(mac), semi))
}
}
}
}
+// lift a function in ast-thingy X fold -> ast-thingy to a function
+// in (ast-thingy X span X fold) -> (ast-thingy X span). Basically,
+// carries the span around.
+// It seems strange to me that the call to new_fold doesn't happen
+// here but instead in the impl down below.... probably just an
+// accident?
pub fn wrap<T>(f: @fn(&T, @ast_fold) -> T)
-> @fn(&T, Span, @ast_fold) -> (T, Span) {
let result: @fn(&T, Span, @ast_fold) -> (T, Span) = |x, s, fld| {
}
let fold_field = |x| fold_field_(x, fld);
- let fold_mac = |x| fold_mac_(x, fld);
-
match *e {
ExprVstore(e, v) => {
ExprVstore(fld.fold_expr(e), v)
ExprDoBody(f) => ExprDoBody(fld.fold_expr(f)),
ExprLit(_) => (*e).clone(),
ExprCast(expr, ref ty) => {
- ExprCast(fld.fold_expr(expr), (*ty).clone())
+ ExprCast(fld.fold_expr(expr), fld.fold_ty(ty))
}
ExprAddrOf(m, ohs) => ExprAddrOf(m, fld.fold_expr(ohs)),
ExprIf(cond, ref tr, fl) => {
.. (*a).clone()
})
}
- ExprMac(ref mac) => ExprMac(fold_mac(mac)),
+ ExprMac(ref mac) => ExprMac(fld.fold_mac(mac)),
ExprStruct(ref path, ref fields, maybe_expr) => {
ExprStruct(
fld.fold_path(path),
}
pub fn noop_fold_ty(t: &ty_, fld: @ast_fold) -> ty_ {
- let fold_mac = |x| fold_mac_(x, fld);
fn fold_mt(mt: &mt, fld: @ast_fold) -> mt {
mt {
ty: ~fld.fold_ty(mt.ty),
)
}
ty_typeof(e) => ty_typeof(fld.fold_expr(e)),
- ty_mac(ref mac) => ty_mac(fold_mac(mac))
+ ty_mac(ref mac) => ty_mac(fld.fold_mac(mac))
}
}
}
}
+// the default macro traversal. visit the path
+// using fold_path, and the tts using fold_tts,
+// and the span using new_span
+fn noop_fold_mac(m: &mac_, fld: @ast_fold) -> mac_ {
+ match *m {
+ mac_invoc_tt(ref p,ref tts,ctxt) =>
+ mac_invoc_tt(fld.fold_path(p),
+ fold_tts(*tts,fld),
+ ctxt)
+ }
+}
+
+
/* temporarily eta-expand because of a compiler bug with using `fn<T>` as a
value */
fn noop_map_exprs(f: @fn(@Expr) -> @Expr, es: &[@Expr]) -> ~[@Expr] {
fold_ident: noop_fold_ident,
fold_path: noop_fold_path,
fold_local: noop_fold_local,
+ fold_mac: wrap(noop_fold_mac),
map_exprs: noop_map_exprs,
new_id: noop_id,
new_span: noop_span,
fn fold_local(@self, x: @Local) -> @Local {
(self.fold_local)(x, self as @ast_fold)
}
+ fn fold_mac(@self, x: &mac) -> mac {
+ let (n, s) = (self.fold_mac)(&x.node, x.span, self as @ast_fold);
+ Spanned { node: n, span: (self.new_span)(s) }
+ }
fn map_exprs(@self,
f: @fn(@Expr) -> @Expr,
e: &[@Expr])
}
}
+// brson agrees with me that this function's existence is probably
+// not a good or useful thing.
pub fn make_fold(afp: ast_fold_fns) -> @ast_fold {
afp as @ast_fold
}
token::get_ident_interner()),
~"zz!zz((zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+)))");
}
+
+ // and in cast expressions... this appears to be an existing bug.
+ #[test] fn ident_transformation_in_types () {
+ let zz_fold = fun_to_ident_folder(to_zz());
+ let ast = string_to_crate(@"fn a() {let z = 13 as int;}");
+ assert_pred!(matches_codepattern,
+ "matches_codepattern",
+ pprust::to_str(&zz_fold.fold_crate(ast),fake_print_crate,
+ token::get_ident_interner()),
+ ~"fn zz(){let zz=13 as zz;}");
+ }
}
use ast::{BiBitAnd, BiBitOr, BiBitXor, Block};
use ast::{BlockCheckMode, UnBox};
use ast::{Crate, CrateConfig, Decl, DeclItem};
-use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, enum_def, explicit_self};
+use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, enum_def, explicit_self};
use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain};
use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock};
use ast::{ExprBreak, ExprCall, ExprCast, ExprDoBody};
|p| p.parse_token_tree());
let hi = self.span.hi;
- return self.mk_mac_expr(lo, hi, mac_invoc_tt(pth, tts));
+ return self.mk_mac_expr(lo, hi, mac_invoc_tt(pth, tts, EMPTY_CTXT));
} else if *self.token == token::LBRACE {
// This might be a struct literal.
if self.looking_at_record_literal() {
if id == token::special_idents::invalid {
return @spanned(lo, hi, StmtMac(
- spanned(lo, hi, mac_invoc_tt(pth, tts)), false));
+ spanned(lo, hi, mac_invoc_tt(pth, tts, EMPTY_CTXT)), false));
} else {
// if it has a special ident, it's definitely an item
return @spanned(lo, hi, StmtDecl(
@spanned(lo, hi, DeclItem(
self.mk_item(
lo, hi, id /*id is good here*/,
- item_mac(spanned(lo, hi, mac_invoc_tt(pth, tts))),
+ item_mac(spanned(lo, hi, mac_invoc_tt(pth, tts, EMPTY_CTXT))),
inherited, ~[/*no attrs*/]))),
self.get_id()));
}
}
fn is_self_ident(&self) -> bool {
- *self.token == token::IDENT(special_idents::self_, false)
+ match *self.token {
+ token::IDENT(id, false) => id.name == special_idents::self_.name,
+ _ => false
+ }
}
fn expect_self_ident(&self) {
_ => self.fatal("expected open delimiter")
};
// single-variant-enum... :
- let m = ast::mac_invoc_tt(pth, tts);
+ let m = ast::mac_invoc_tt(pth, tts, EMPTY_CTXT);
let m: ast::mac = codemap::Spanned { node: m,
span: mk_sp(self.span.lo,
self.span.hi) };
// except according to those terms.
use ast;
-use ast::Name;
+use ast::{Name, Mrk};
use ast_util;
use parse::token;
use util::interner::StrInterner;
use util::interner;
+use std::cast;
use std::char;
-use std::cmp::Equiv;
use std::local_data;
-use std::rand;
-use std::rand::RngUtil;
#[deriving(Clone, Encodable, Decodable, Eq, IterBytes)]
pub enum binop {
match *t { BINOP(OR) | OROR => true, _ => false }
}
-
pub mod special_idents {
use ast::Ident;
- pub static underscore : Ident = Ident { name: 0, ctxt: 0};
+ pub static underscore : Ident = Ident { name: 0, ctxt: 0}; // apparently unused?
pub static anon : Ident = Ident { name: 1, ctxt: 0};
pub static invalid : Ident = Ident { name: 2, ctxt: 0}; // ''
- pub static unary : Ident = Ident { name: 3, ctxt: 0};
- pub static not_fn : Ident = Ident { name: 4, ctxt: 0};
- pub static idx_fn : Ident = Ident { name: 5, ctxt: 0};
- pub static unary_minus_fn : Ident = Ident { name: 6, ctxt: 0};
+ pub static unary : Ident = Ident { name: 3, ctxt: 0}; // apparently unused?
+ pub static not_fn : Ident = Ident { name: 4, ctxt: 0}; // apparently unused?
+ pub static idx_fn : Ident = Ident { name: 5, ctxt: 0}; // apparently unused?
+ pub static unary_minus_fn : Ident = Ident { name: 6, ctxt: 0}; // apparently unused?
pub static clownshoes_extensions : Ident = Ident { name: 7, ctxt: 0};
pub static self_ : Ident = Ident { name: 8, ctxt: 0}; // 'self'
/* for matcher NTs */
+ // none of these appear to be used, but perhaps references to
+ // these are artificially fabricated by the macro system....
pub static item : Ident = Ident { name: 9, ctxt: 0};
pub static block : Ident = Ident { name: 10, ctxt: 0};
pub static stmt : Ident = Ident { name: 11, ctxt: 0};
pub static tt : Ident = Ident { name: 17, ctxt: 0};
pub static matchers : Ident = Ident { name: 18, ctxt: 0};
- pub static str : Ident = Ident { name: 19, ctxt: 0}; // for the type
+ pub static str : Ident = Ident { name: 19, ctxt: 0}; // for the type // apparently unused?
/* outside of libsyntax */
pub static arg : Ident = Ident { name: 20, ctxt: 0};
pub static statik : Ident = Ident { name: 27, ctxt: 0};
pub static clownshoes_foreign_mod: Ident = Ident { name: 28, ctxt: 0};
pub static unnamed_field: Ident = Ident { name: 29, ctxt: 0};
- pub static c_abi: Ident = Ident { name: 30, ctxt: 0};
+ pub static c_abi: Ident = Ident { name: 30, ctxt: 0}; // apparently unused?
pub static type_self: Ident = Ident { name: 31, ctxt: 0}; // `Self`
}
+// here are the ones that actually occur in the source. Maybe the rest
+// should be removed?
+/*
+special_idents::anon
+special_idents::arg
+special_idents::blk
+special_idents::clownshoe_abi
+special_idents::clownshoe_stack_shim
+special_idents::clownshoes_extensions
+special_idents::clownshoes_foreign_mod
+special_idents::descrim
+special_idents::invalid
+special_idents::main
+special_idents::matchers
+special_idents::opaque
+special_idents::self_
+special_idents::statik
+special_idents::tt
+special_idents::type_self
+special_idents::unnamed_field
+*/
+
/**
* Maps a token to a record specifying the corresponding binary
* operator
}
}
-pub struct ident_interner {
- priv interner: StrInterner,
-}
-
-impl ident_interner {
- pub fn intern(&self, val: &str) -> Name {
- self.interner.intern(val)
- }
- pub fn gensym(&self, val: &str) -> Name {
- self.interner.gensym(val)
- }
- pub fn get(&self, idx: Name) -> @str {
- self.interner.get(idx)
- }
- // is this really something that should be exposed?
- pub fn len(&self) -> uint {
- self.interner.len()
- }
- pub fn find_equiv<Q:Hash + IterBytes + Equiv<@str>>(&self, val: &Q)
- -> Option<Name> {
- self.interner.find_equiv(val)
- }
-}
-
+// looks like we can get rid of this completely...
+pub type ident_interner = StrInterner;
// return a fresh interner, preloaded with special identifiers.
fn mk_fresh_ident_interner() -> @ident_interner {
"typeof", // 67
];
- @ident_interner {
- interner: interner::StrInterner::prefill(init_vec)
- }
+ @interner::StrInterner::prefill(init_vec)
}
// if an interner exists in TLS, return it. Otherwise, prepare a
/* for when we don't care about the contents; doesn't interact with TLD or
serialization */
pub fn mk_fake_ident_interner() -> @ident_interner {
- @ident_interner { interner: interner::StrInterner::new() }
+ @interner::StrInterner::new()
}
// maps a string to its interned representation
ast::Ident::new(gensym(str))
}
+// create a fresh name that maps to the same string as the old one.
+// note that this guarantees that str_ptr_eq(ident_to_str(src),interner_get(fresh_name(src)));
+// that is, that the new name and the old one are connected to ptr_eq strings.
+pub fn fresh_name(src : &ast::Ident) -> Name {
+ let interner = get_ident_interner();
+ interner.gensym_copy(src.name)
+ // following: debug version. Could work in final except that it's incompatible with
+ // good error messages and uses of struct names in ambiguous could-be-binding
+ // locations. Also definitely destroys the guarantee given above about ptr_eq.
+ /*let num = rand::rng().gen_uint_range(0,0xffff);
+ gensym(fmt!("%s_%u",ident_to_str(src),num))*/
+}
+
+// it looks like there oughta be a str_ptr_eq fn, but no one bothered to implement it?
+
+// determine whether two @str values are pointer-equal
+pub fn str_ptr_eq(a : @str, b : @str) -> bool {
+ unsafe {
+ let p : uint = cast::transmute(a);
+ let q : uint = cast::transmute(b);
+ let result = p == q;
+ // got to transmute them back, to make sure the ref count is correct:
+ let _junk1 : @str = cast::transmute(p);
+ let _junk2 : @str = cast::transmute(q);
+ result
+ }
+}
+
+// return true when two identifiers refer (through the intern table) to the same ptr_eq
+// string. This is used to compare identifiers in places where hygienic comparison is
+// not wanted (i.e. not lexical vars).
+pub fn ident_spelling_eq(a : &ast::Ident, b : &ast::Ident) -> bool {
+ str_ptr_eq(interner_get(a.name),interner_get(b.name))
+}
-// create a fresh name. In principle, this is just a
-// gensym, but for debugging purposes, you'd like the
-// resulting name to have a suggestive stringify, without
-// paying the cost of guaranteeing that the name is
-// truly unique. I'm going to try to strike a balance
-// by using a gensym with a name that has a random number
-// at the end. So, the gensym guarantees the uniqueness,
-// and the int helps to avoid confusion.
-pub fn fresh_name(src_name : &str) -> Name {
- let num = rand::rng().gen_uint_range(0,0xffff);
- gensym(fmt!("%s_%u",src_name,num))
+// create a fresh mark.
+pub fn fresh_mark() -> Mrk {
+ gensym("mark")
}
/**
}
}
+pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool {
+ match (t1,t2) {
+ (&IDENT(id1,_),&IDENT(id2,_)) =>
+ ast_util::mtwt_resolve(id1) == ast_util::mtwt_resolve(id2),
+ _ => *t1 == *t2
+ }
+}
+
+
#[cfg(test)]
mod test {
use super::*;
- #[test] fn t1() {
- let a = fresh_name("ghi");
- printfln!("interned name: %u,\ntextual name: %s\n",
- a, interner_get(a));
+ use ast;
+ use ast_util;
+
+ fn mark_ident(id : ast::Ident, m : ast::Mrk) -> ast::Ident {
+ ast::Ident{name:id.name,ctxt:ast_util::new_mark(m,id.ctxt)}
}
+
+ #[test] fn mtwt_token_eq_test() {
+ assert!(mtwt_token_eq(>,>));
+ let a = str_to_ident("bac");
+ let a1 = mark_ident(a,92);
+ assert!(mtwt_token_eq(&IDENT(a,true),&IDENT(a1,false)));
+ }
+
+
+ #[test] fn str_ptr_eq_tests(){
+ let a = @"abc";
+ let b = @"abc";
+ let c = a;
+ assert!(str_ptr_eq(a,c));
+ assert!(!str_ptr_eq(a,b));
+ }
+
+ #[test] fn fresh_name_pointer_sharing() {
+ let ghi = str_to_ident("ghi");
+ assert_eq!(ident_to_str(&ghi),@"ghi");
+ assert!(str_ptr_eq(ident_to_str(&ghi),ident_to_str(&ghi)))
+ let fresh = ast::Ident::new(fresh_name(&ghi));
+ assert_eq!(ident_to_str(&fresh),@"ghi");
+ assert!(str_ptr_eq(ident_to_str(&ghi),ident_to_str(&fresh)));
+ }
+
}
}
bclose(s, item.span);
}
- ast::item_mac(codemap::Spanned { node: ast::mac_invoc_tt(ref pth, ref tts),
+ // I think it's reasonable to hide the context here:
+ ast::item_mac(codemap::Spanned { node: ast::mac_invoc_tt(ref pth, ref tts, _),
_}) => {
print_visibility(s, item.vis);
print_path(s, pth, false);
pub fn print_mac(s: @ps, m: &ast::mac) {
match m.node {
- ast::mac_invoc_tt(ref pth, ref tts) => {
+ // I think it's reasonable to hide the ctxt here:
+ ast::mac_invoc_tt(ref pth, ref tts, _) => {
print_path(s, pth, false);
word(s.s, "!");
popen(s);
match input.pat.node {
ast::PatIdent(_, ref path, _) if
path.segments.len() == 1 &&
- path.segments[0].identifier ==
- parse::token::special_idents::invalid => {
+ path.segments[0].identifier.name ==
+ parse::token::special_idents::invalid.name => {
// Do nothing.
}
_ => {
new_idx
}
+ // I want these gensyms to share name pointers
+ // with existing entries. This would be automatic,
+ // except that the existing gensym creates its
+ // own managed ptr using to_managed. I think that
+ // adding this utility function is the most
+ // lightweight way to get what I want, though not
+ // necessarily the cleanest.
+
+ // create a gensym with the same name as an existing
+ // entry.
+ pub fn gensym_copy(&self, idx : uint) -> uint {
+ let new_idx = self.len();
+ // leave out of map to avoid colliding
+ self.vect.push(self.vect[idx]);
+ new_idx
+ }
+
// this isn't "pure" in the traditional sense, because it can go from
// failing to returning a value as items are interned. But for typestate,
// where we first check a pred and then rely on it, ceasing to fail is ok.
}
#[test]
- fn i2 () {
+ fn interner_tests () {
let i : Interner<@str> = Interner::new();
// first one is zero:
- assert_eq!(i.intern (@"dog"), 0);
+ assert_eq!(i.intern(@"dog"), 0);
// re-use gets the same entry:
- assert_eq!(i.intern (@"dog"), 0);
+ assert_eq!(i.intern(@"dog"), 0);
// different string gets a different #:
- assert_eq!(i.intern (@"cat"), 1);
- assert_eq!(i.intern (@"cat"), 1);
+ assert_eq!(i.intern(@"cat"), 1);
+ assert_eq!(i.intern(@"cat"), 1);
// dog is still at zero
- assert_eq!(i.intern (@"dog"), 0);
+ assert_eq!(i.intern(@"dog"), 0);
// gensym gets 3
- assert_eq!(i.gensym (@"zebra" ), 2);
+ assert_eq!(i.gensym(@"zebra" ), 2);
// gensym of same string gets new number :
assert_eq!(i.gensym (@"zebra" ), 3);
// gensym of *existing* string gets new number:
- assert_eq!(i.gensym (@"dog"), 4);
+ assert_eq!(i.gensym(@"dog"), 4);
assert_eq!(i.get(0), @"dog");
assert_eq!(i.get(1), @"cat");
assert_eq!(i.get(2), @"zebra");
assert_eq!(i.get(2), @"Carol");
assert_eq!(i.intern(@"Bob"), 1);
}
+
+ #[test]
+ fn string_interner_tests() {
+ let i : StrInterner = StrInterner::new();
+ // first one is zero:
+ assert_eq!(i.intern("dog"), 0);
+ // re-use gets the same entry:
+ assert_eq!(i.intern ("dog"), 0);
+ // different string gets a different #:
+ assert_eq!(i.intern("cat"), 1);
+ assert_eq!(i.intern("cat"), 1);
+ // dog is still at zero
+ assert_eq!(i.intern("dog"), 0);
+ // gensym gets 3
+ assert_eq!(i.gensym("zebra"), 2);
+ // gensym of same string gets new number :
+ assert_eq!(i.gensym("zebra"), 3);
+ // gensym of *existing* string gets new number:
+ assert_eq!(i.gensym("dog"), 4);
+ // gensym tests again with gensym_copy:
+ assert_eq!(i.gensym_copy(2), 5);
+ assert_eq!(i.get(5), @"zebra");
+ assert_eq!(i.gensym_copy(2), 6);
+ assert_eq!(i.get(6), @"zebra");
+ assert_eq!(i.get(0), @"dog");
+ assert_eq!(i.get(1), @"cat");
+ assert_eq!(i.get(2), @"zebra");
+ assert_eq!(i.get(3), @"zebra");
+ assert_eq!(i.get(4), @"dog");
+ }
}
(filemap_to_tts(ps,string_to_filemap(ps,source_str,@"bogofile")),ps)
}
+// map a string to tts, using a made-up filename:
+pub fn string_to_tts(source_str : @str) -> ~[ast::token_tree] {
+ let (tts,_) = string_to_tts_and_sess(source_str);
+ tts
+}
+
pub fn string_to_parser_and_sess(source_str: @str) -> (Parser,@mut ParseSess) {
let ps = new_parse_sess(None);
(new_parser_from_source_str(ps,~[],@"bogofile",source_str),ps)
x
}
+// parse a string, return a crate.
pub fn string_to_crate (source_str : @str) -> @ast::Crate {
do with_error_checking_parse(source_str) |p| {
p.parse_crate_mod()
}
}
+// parse a string, return a crate and the ParseSess
+pub fn string_to_crate_and_sess (source_str : @str) -> (@ast::Crate,@mut ParseSess) {
+ let (p,ps) = string_to_parser_and_sess(source_str);
+ (p.parse_crate_mod(),ps)
+}
+
// parse a string, return an expr
pub fn string_to_expr (source_str : @str) -> @ast::Expr {
do with_error_checking_parse(source_str) |p| {
}
}
-// parse a string, return an item and the ParseSess
-pub fn string_to_item_and_sess (source_str : @str) -> (Option<@ast::item>,@mut ParseSess) {
- let (p,ps) = string_to_parser_and_sess(source_str);
- let io = p.parse_item(~[]);
- p.abort_if_errors();
- (io,ps)
-}
-
// parse a string, return a stmt
pub fn string_to_stmt(source_str : @str) -> @ast::Stmt {
do with_error_checking_parse(source_str) |p| {
use std::vec;
macro_rules! bench (
- ($id:ident) => (maybe_run_test(argv, stringify!($id).to_owned(), $id))
+ ($argv:expr, $id:ident) => (maybe_run_test($argv, stringify!($id).to_owned(), $id))
)
fn main() {
let argv = os::args();
let _tests = argv.slice(1, argv.len());
- bench!(shift_push);
- bench!(read_line);
- bench!(vec_plus);
- bench!(vec_append);
- bench!(vec_push_all);
- bench!(is_utf8_ascii);
- bench!(is_utf8_multibyte);
+ bench!(argv, shift_push);
+ bench!(argv, read_line);
+ bench!(argv, vec_plus);
+ bench!(argv, vec_append);
+ bench!(argv, vec_push_all);
+ bench!(argv, is_utf8_ascii);
+ bench!(argv, is_utf8_multibyte);
}
fn maybe_run_test(argv: &[~str], name: ~str, test: &fn()) {
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod x {
+ pub fn g() -> uint {14}
+}
+
+fn main(){
+ // should *not* shadow the module x:
+ let x = 9;
+ // use it to avoid warnings:
+ x+3;
+ assert_eq!(x::g(),14);
+}
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// shouldn't affect evaluation of $ex:
+macro_rules! bad_macro (($ex:expr) => ({let _x = 9; $ex}))
+fn main() {
+ let _x = 8;
+ assert_eq!(bad_macro!(_x),8)
+}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+// xfail-test
+// this now fails (correctly, I claim) because hygiene prevents
+// the assembled identifier from being a reference to the binding.
pub fn main() {
let asdf_fdsa = ~"<.<";