Skip to content

Commit

Permalink
Auto merge of #34570 - jseyfried:no_rename, r=nrc
Browse files Browse the repository at this point in the history
Simplify the macro hygiene algorithm

This PR removes renaming from the hygiene algorithm and treats differently marked identifiers as unequal.

This change makes the scope of identifiers in `macro_rules!` items empty. That is, identifiers in `macro_rules!` definitions do not inherit any semantics from the `macro_rules!`'s scope.

Since `macro_rules!` macros are items, the scope of their identifiers "should" be the same as that of other items; in particular, the scope should contain only items. Since all items are unhygienic today, this would mean the scope should be empty.

However, the scope of an identifier in a `macro_rules!` statement today is the scope that the identifier would have if it replaced the `macro_rules!` (excluding anything unhygienic, i.e. locals only).

To continue to support this, this PR tracks the scope of each `macro_rules!` and uses it in `resolve` to ensure that an identifier expanded from a `macro_rules!` gets a chance to resolve to the locals in the `macro_rules!`'s scope.

This PR is a pure refactoring. After this PR,
 - `syntax::ext::expand` is much simpler.
 - We can expand macros in any order without causing problems for hygiene (needed for macro modularization).
 - We can deprecate or remove today's `macro_rules!` scope easily.
 - Expansion performance improves by 25%, post-expansion memory usage decreases by ~5%.
 - Expanding a block is no longer quadratic in the number of `let` statements (fixes #10607).

r? @nrc
  • Loading branch information
bors authored Jul 15, 2016
2 parents 3cc3ad1 + c1a6ff2 commit 4db1874
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 806 deletions.
70 changes: 13 additions & 57 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ use std::io::{self, Write};
use std::path::{Path, PathBuf};
use syntax::{ast, diagnostics, visit};
use syntax::attr::{self, AttrMetaMethods};
use syntax::fold::Folder;
use syntax::parse::{self, PResult, token};
use syntax::util::node_count::NodeCounter;
use syntax;
Expand Down Expand Up @@ -695,6 +694,19 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
sess.diagnostic())
});

let resolver_arenas = Resolver::arenas();
let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas);

let krate = time(sess.time_passes(), "assigning node ids", || resolver.assign_node_ids(krate));

if sess.opts.debugging_opts.input_stats {
println!("Post-expansion node count: {}", count_nodes(&krate));
}

if sess.opts.debugging_opts.ast_json {
println!("{}", json::as_json(&krate));
}

time(time_passes,
"checking for inline asm in case the target doesn't support it",
|| no_asm::check_crate(sess, &krate));
Expand All @@ -710,15 +722,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
})
})?;

if sess.opts.debugging_opts.input_stats {
println!("Post-expansion node count: {}", count_nodes(&krate));
}

krate = assign_node_ids(sess, krate);

let resolver_arenas = Resolver::arenas();
let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas);

// Collect defintions for def ids.
time(sess.time_passes(), "collecting defs", || resolver.definitions.collect(&krate));

Expand Down Expand Up @@ -783,53 +786,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
})
}

pub fn assign_node_ids(sess: &Session, krate: ast::Crate) -> ast::Crate {
use syntax::ptr::P;
use syntax::util::move_map::MoveMap;

struct NodeIdAssigner<'a> {
sess: &'a Session,
}

impl<'a> Folder for NodeIdAssigner<'a> {
fn new_id(&mut self, old_id: ast::NodeId) -> ast::NodeId {
assert_eq!(old_id, ast::DUMMY_NODE_ID);
self.sess.next_node_id()
}

fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
block.map(|mut block| {
block.id = self.new_id(block.id);

let stmt = block.stmts.pop();
block.stmts = block.stmts.move_flat_map(|s| self.fold_stmt(s).into_iter());
if let Some(ast::Stmt { node: ast::StmtKind::Expr(expr), span, .. }) = stmt {
let expr = self.fold_expr(expr);
block.stmts.push(ast::Stmt {
id: expr.id,
node: ast::StmtKind::Expr(expr),
span: span,
});
} else if let Some(stmt) = stmt {
block.stmts.extend(self.fold_stmt(stmt));
}

block
})
}
}

let krate = time(sess.time_passes(),
"assigning node ids",
|| NodeIdAssigner { sess: sess }.fold_crate(krate));

if sess.opts.debugging_opts.ast_json {
println!("{}", json::as_json(&krate));
}

krate
}

/// Run the resolution, typechecking, region checking and other
/// miscellaneous analysis passes on the crate. Return various
/// structures carrying the results of the analysis.
Expand Down
92 changes: 92 additions & 0 deletions src/librustc_resolve/assign_ids.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2016 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.

use Resolver;
use rustc::session::Session;
use syntax::ast;
use syntax::ext::mtwt;
use syntax::fold::{self, Folder};
use syntax::ptr::P;
use syntax::util::move_map::MoveMap;
use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::mem;

impl<'a> Resolver<'a> {
pub fn assign_node_ids(&mut self, krate: ast::Crate) -> ast::Crate {
NodeIdAssigner {
sess: self.session,
macros_at_scope: &mut self.macros_at_scope,
}.fold_crate(krate)
}
}

struct NodeIdAssigner<'a> {
sess: &'a Session,
macros_at_scope: &'a mut HashMap<ast::NodeId, Vec<ast::Mrk>>,
}

impl<'a> Folder for NodeIdAssigner<'a> {
fn new_id(&mut self, old_id: ast::NodeId) -> ast::NodeId {
assert_eq!(old_id, ast::DUMMY_NODE_ID);
self.sess.next_node_id()
}

fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
block.map(|mut block| {
block.id = self.new_id(block.id);

let stmt = block.stmts.pop();
let mut macros = Vec::new();
block.stmts = block.stmts.move_flat_map(|stmt| {
if let ast::StmtKind::Item(ref item) = stmt.node {
if let ast::ItemKind::Mac(..) = item.node {
macros.push(mtwt::outer_mark(item.ident.ctxt));
return None;
}
}

let stmt = self.fold_stmt(stmt).pop().unwrap();
if !macros.is_empty() {
self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new()));
}
Some(stmt)
});

stmt.and_then(|mut stmt| {
// Avoid wasting a node id on a trailing expression statement,
// which shares a HIR node with the expression itself.
if let ast::StmtKind::Expr(expr) = stmt.node {
let expr = self.fold_expr(expr);
stmt.id = expr.id;
stmt.node = ast::StmtKind::Expr(expr);
Some(stmt)
} else {
self.fold_stmt(stmt).pop()
}
}).map(|stmt| {
if !macros.is_empty() {
self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new()));
}
block.stmts.push(stmt);
});

block
})
}

fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
match item.node {
ast::ItemKind::Mac(..) => SmallVector::zero(),
_ => fold::noop_fold_item(item, self),
}
}
}
Loading

0 comments on commit 4db1874

Please sign in to comment.