Skip to content

Commit

Permalink
Auto merge of #34860 - jseyfried:encapsulate_hygiene, r=nrc
Browse files Browse the repository at this point in the history
Clean up and encapsulate `syntax::ext::mtwt`, rename `mtwt` to `hygiene`

r? @nrc
  • Loading branch information
bors authored Jul 18, 2016
2 parents f441bca + 275d321 commit 6cc49e5
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 233 deletions.
4 changes: 2 additions & 2 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,8 +735,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"for every macro invocation, print its name and arguments"),
enable_nonzeroing_move_hints: bool = (false, parse_bool,
"force nonzeroing move optimization on"),
keep_mtwt_tables: bool = (false, parse_bool,
"don't clear the resolution tables after analysis"),
keep_hygiene_data: bool = (false, parse_bool,
"don't clear the hygiene data after analysis"),
keep_ast: bool = (false, parse_bool,
"keep the AST after lowering it to HIR"),
show_span: Option<String> = (None, parse_opt_string,
Expand Down
15 changes: 7 additions & 8 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ pub fn compile_input(sess: &Session,
Ok(())
}

fn keep_mtwt_tables(sess: &Session) -> bool {
sess.opts.debugging_opts.keep_mtwt_tables
fn keep_hygiene_data(sess: &Session) -> bool {
sess.opts.debugging_opts.keep_hygiene_data
}

fn keep_ast(sess: &Session) -> bool {
Expand Down Expand Up @@ -479,9 +479,8 @@ pub fn phase_1_parse_input<'a>(sess: &'a Session,
input: &Input)
-> PResult<'a, ast::Crate> {
// These may be left in an incoherent state after a previous compile.
// `clear_tables` and `clear_ident_interner` can be used to free
// memory, but they do not restore the initial state.
syntax::ext::mtwt::reset_tables();
syntax::ext::hygiene::reset_hygiene_data();
// `clear_ident_interner` can be used to free memory, but it does not restore the initial state.
token::reset_ident_interner();
let continue_after_error = sess.opts.continue_parse_after_error;
sess.diagnostic().set_continue_after_error(continue_after_error);
Expand Down Expand Up @@ -761,9 +760,9 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
hir_map::Forest::new(lower_crate(sess, &krate, &mut resolver), &sess.dep_graph)
});

// Discard MTWT tables that aren't required past lowering to HIR.
if !keep_mtwt_tables(sess) {
syntax::ext::mtwt::clear_tables();
// Discard hygiene data, which isn't required past lowering to HIR.
if !keep_hygiene_data(sess) {
syntax::ext::hygiene::reset_hygiene_data();
}

Ok(ExpansionResult {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_driver/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> {
pp::space(&mut s.s)?;
// FIXME #16420: this doesn't display the connections
// between syntax contexts
s.synth_comment(format!("{}#{}", nm, ctxt.0))
s.synth_comment(format!("{}{:?}", nm, ctxt))
}
pprust::NodeName(&ast::Name(nm)) => {
pp::space(&mut s.s)?;
Expand Down
6 changes: 3 additions & 3 deletions src/librustc_resolve/assign_ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use Resolver;
use rustc::session::Session;
use syntax::ast;
use syntax::ext::mtwt;
use syntax::ext::hygiene::Mark;
use syntax::fold::{self, Folder};
use syntax::ptr::P;
use syntax::util::move_map::MoveMap;
Expand All @@ -31,7 +31,7 @@ impl<'a> Resolver<'a> {

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

impl<'a> Folder for NodeIdAssigner<'a> {
Expand All @@ -49,7 +49,7 @@ impl<'a> Folder for NodeIdAssigner<'a> {
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));
macros.push(item.ident.ctxt.data().outer_mark);
return None;
}
}
Expand Down
20 changes: 9 additions & 11 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use rustc::ty::subst::{ParamSpace, FnSpace, TypeSpace};
use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet};

use syntax::ext::mtwt;
use syntax::ext::hygiene::Mark;
use syntax::ast::{self, FloatTy};
use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy};
use syntax::parse::token::{self, keywords};
Expand Down Expand Up @@ -654,7 +654,7 @@ enum RibKind<'a> {
ModuleRibKind(Module<'a>),

// We passed through a `macro_rules!` statement with the given expansion
MacroDefinition(ast::Mrk),
MacroDefinition(Mark),
}

#[derive(Copy, Clone)]
Expand Down Expand Up @@ -933,7 +933,7 @@ pub struct Resolver<'a> {

// Maps the node id of a statement to the expansions of the `macro_rules!`s
// immediately above the statement (if appropriate).
macros_at_scope: HashMap<NodeId, Vec<ast::Mrk>>,
macros_at_scope: HashMap<NodeId, Vec<Mark>>,

graph_root: Module<'a>,

Expand Down Expand Up @@ -1434,10 +1434,9 @@ impl<'a> Resolver<'a> {
if let MacroDefinition(mac) = self.get_ribs(ns)[i].kind {
// If an invocation of this macro created `ident`, give up on `ident`
// and switch to `ident`'s source from the macro definition.
if let Some((source_ident, source_macro)) = mtwt::source(ident) {
if mac == source_macro {
ident = source_ident;
}
let (source_ctxt, source_macro) = ident.ctxt.source();
if source_macro == mac {
ident.ctxt = source_ctxt;
}
}
}
Expand Down Expand Up @@ -1585,10 +1584,9 @@ impl<'a> Resolver<'a> {
MacroDefinition(mac) => {
// If an invocation of this macro created `ident`, give up on `ident`
// and switch to `ident`'s source from the macro definition.
if let Some((source_ident, source_macro)) = mtwt::source(ident) {
if mac == source_macro {
ident = source_ident;
}
let (source_ctxt, source_macro) = ident.ctxt.source();
if source_macro == mac {
ident.ctxt = source_ctxt;
}
}
_ => {
Expand Down
22 changes: 3 additions & 19 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub use util::ThinVec;
use syntax_pos::{mk_sp, Span, DUMMY_SP, ExpnId};
use codemap::{respan, Spanned};
use abi::Abi;
use ext::hygiene::SyntaxContext;
use parse::token::{self, keywords, InternedString};
use print::pprust;
use ptr::P;
Expand All @@ -33,15 +34,6 @@ use serialize::{Encodable, Decodable, Encoder, Decoder};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Name(pub u32);

/// A SyntaxContext represents a chain of macro-expandings
/// and renamings. Each macro expansion corresponds to
/// a fresh u32. This u32 is a reference to a table stored
/// in thread-local storage.
/// The special value EMPTY_CTXT is used to indicate an empty
/// syntax context.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct SyntaxContext(pub u32);

/// An identifier contains a Name (index into the interner
/// table) and a SyntaxContext to track renaming and
/// macro expansion per Flatt et al., "Macros That Work Together"
Expand Down Expand Up @@ -81,20 +73,15 @@ impl Decodable for Name {
}
}

pub const EMPTY_CTXT : SyntaxContext = SyntaxContext(0);

impl Ident {
pub fn new(name: Name, ctxt: SyntaxContext) -> Ident {
Ident {name: name, ctxt: ctxt}
}
pub const fn with_empty_ctxt(name: Name) -> Ident {
Ident {name: name, ctxt: EMPTY_CTXT}
Ident { name: name, ctxt: SyntaxContext::empty() }
}
}

impl fmt::Debug for Ident {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}#{}", self.name, self.ctxt.0)
write!(f, "{}{:?}", self.name, self.ctxt)
}
}

Expand All @@ -116,9 +103,6 @@ impl Decodable for Ident {
}
}

/// A mark represents a unique id associated with a macro expansion
pub type Mrk = u32;

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)]
pub struct Lifetime {
pub id: NodeId,
Expand Down
44 changes: 15 additions & 29 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@
// except according to those terms.

use ast::{Block, Crate, Ident, Mac_, Name, PatKind};
use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
use ast::{MacStmtStyle, Stmt, StmtKind, ItemKind};
use ast;
use attr::HasAttrs;
use ext::mtwt;
use attr;
use ext::hygiene::Mark;
use attr::{self, HasAttrs};
use attr::AttrMetaMethods;
use codemap::{dummy_spanned, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
use codemap::{dummy_spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
use syntax_pos::{self, Span, ExpnId};
use config::StripUnconfigured;
use ext::base::*;
use feature_gate::{self, Features};
use fold;
use fold::*;
use parse::token::{fresh_mark, intern, keywords};
use parse::token::{intern, keywords};
use ptr::P;
use tokenstream::TokenTree;
use util::small_vector::SmallVector;
Expand Down Expand Up @@ -130,9 +129,9 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
// 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.
let Mac_ { path, tts, .. } = mac.node;
let mark = fresh_mark();
let mark = Mark::fresh();

fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mark,
attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
-> Option<Box<MacResult + 'a>> {
// Detect use of feature-gated or invalid attributes on macro invoations
Expand Down Expand Up @@ -708,30 +707,17 @@ pub fn expand_crate(mut cx: ExtCtxt,
return (ret, cx.syntax_env.names);
}

// 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:
// Marker - add a mark to a context

// A Marker adds the given mark to the syntax context and
// sets spans' `expn_id` to the given expn_id (unless it is `None`).
struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
struct Marker { mark: Mark, expn_id: Option<ExpnId> }

impl Folder for Marker {
fn fold_ident(&mut self, id: Ident) -> Ident {
ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt))
}
fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
Spanned {
node: Mac_ {
path: self.fold_path(node.path),
tts: self.fold_tts(&node.tts),
},
span: self.new_span(span),
}
fn fold_ident(&mut self, mut ident: Ident) -> Ident {
ident.ctxt = ident.ctxt.apply_mark(self.mark);
ident
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
noop_fold_mac(mac, self)
}

fn new_span(&mut self, mut span: Span) -> Span {
Expand All @@ -743,7 +729,7 @@ impl Folder for Marker {
}

// apply a given mark to the given token trees. Used prior to expansion of a macro.
fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec<TokenTree> {
noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
}

Expand Down
116 changes: 116 additions & 0 deletions src/libsyntax/ext/hygiene.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2012-2014 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.

//! Machinery for hygienic macros, inspired by the MTWT[1] paper.
//!
//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;

/// A SyntaxContext represents a chain of macro expansions (represented by marks).
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Default)]
pub struct SyntaxContext(u32);

#[derive(Copy, Clone)]
pub struct SyntaxContextData {
pub outer_mark: Mark,
pub prev_ctxt: SyntaxContext,
}

/// A mark represents a unique id associated with a macro expansion.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct Mark(u32);

impl Mark {
pub fn fresh() -> Self {
HygieneData::with(|data| {
let next_mark = Mark(data.next_mark.0 + 1);
::std::mem::replace(&mut data.next_mark, next_mark)
})
}
}

struct HygieneData {
syntax_contexts: Vec<SyntaxContextData>,
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
next_mark: Mark,
}

impl HygieneData {
fn new() -> Self {
HygieneData {
syntax_contexts: vec![SyntaxContextData {
outer_mark: Mark(0), // the null mark
prev_ctxt: SyntaxContext(0), // the empty context
}],
markings: HashMap::new(),
next_mark: Mark(1),
}
}

fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
thread_local! {
static HYGIENE_DATA: RefCell<HygieneData> = RefCell::new(HygieneData::new());
}
HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut()))
}
}

pub fn reset_hygiene_data() {
HygieneData::with(|data| *data = HygieneData::new())
}

impl SyntaxContext {
pub const fn empty() -> Self {
SyntaxContext(0)
}

pub fn data(self) -> SyntaxContextData {
HygieneData::with(|data| data.syntax_contexts[self.0 as usize])
}

/// Extend a syntax context with a given mark
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
// Applying the same mark twice is a no-op
let ctxt_data = self.data();
if mark == ctxt_data.outer_mark {
return ctxt_data.prev_ctxt;
}

HygieneData::with(|data| {
let syntax_contexts = &mut data.syntax_contexts;
*data.markings.entry((self, mark)).or_insert_with(|| {
syntax_contexts.push(SyntaxContextData {
outer_mark: mark,
prev_ctxt: self,
});
SyntaxContext(syntax_contexts.len() as u32 - 1)
})
})
}

/// If `ident` is macro expanded, return the source ident from the macro definition
/// and the mark of the expansion that created the macro definition.
pub fn source(self) -> (Self /* source context */, Mark /* source macro */) {
let macro_def_ctxt = self.data().prev_ctxt.data();
(macro_def_ctxt.prev_ctxt, macro_def_ctxt.outer_mark)
}
}

impl fmt::Debug for SyntaxContext {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{}", self.0)
}
}
Loading

0 comments on commit 6cc49e5

Please sign in to comment.