From 76ed4456224f6bf7dc35dc72cc1c2cc93186285a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 22 Jun 2016 08:03:42 +0000 Subject: [PATCH] Clean up and encapsulate `syntax::ext::mtwt` --- src/librustc_driver/driver.rs | 7 +- src/librustc_driver/pretty.rs | 2 +- src/librustc_resolve/assign_ids.rs | 6 +- src/librustc_resolve/lib.rs | 20 ++-- src/libsyntax/ast.rs | 22 +--- src/libsyntax/ext/expand.rs | 42 +++----- src/libsyntax/ext/mtwt.rs | 157 ++++++++++++++--------------- src/libsyntax/parse/token.rs | 5 - 8 files changed, 110 insertions(+), 151 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 3b1124a911e..6b141ca15c0 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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::mtwt::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); @@ -763,7 +762,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, // Discard MTWT tables that aren't required past lowering to HIR. if !keep_mtwt_tables(sess) { - syntax::ext::mtwt::clear_tables(); + syntax::ext::mtwt::reset_hygiene_data(); } Ok(ExpansionResult { diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index baac455a25f..14476cc997f 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -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)?; diff --git a/src/librustc_resolve/assign_ids.rs b/src/librustc_resolve/assign_ids.rs index d4465822229..77facbfb617 100644 --- a/src/librustc_resolve/assign_ids.rs +++ b/src/librustc_resolve/assign_ids.rs @@ -11,7 +11,7 @@ use Resolver; use rustc::session::Session; use syntax::ast; -use syntax::ext::mtwt; +use syntax::ext::mtwt::Mark; use syntax::fold::{self, Folder}; use syntax::ptr::P; use syntax::util::move_map::MoveMap; @@ -31,7 +31,7 @@ impl<'a> Resolver<'a> { struct NodeIdAssigner<'a> { sess: &'a Session, - macros_at_scope: &'a mut HashMap>, + macros_at_scope: &'a mut HashMap>, } impl<'a> Folder for NodeIdAssigner<'a> { @@ -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; } } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 2535c264ef8..36cd2ef6002 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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::mtwt::Mark; use syntax::ast::{self, FloatTy}; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy}; use syntax::parse::token::{self, keywords}; @@ -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)] @@ -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>, + macros_at_scope: HashMap>, graph_root: Module<'a>, @@ -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; } } } @@ -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; } } _ => { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 6b662c6779a..1f716923a16 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -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::mtwt::SyntaxContext; use parse::token::{self, keywords, InternedString}; use print::pprust; use ptr::P; @@ -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" @@ -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) } } @@ -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, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3e9837a6995..767d1ddb8e2 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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::mtwt::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; @@ -130,9 +129,9 @@ fn expand_mac_invoc(mac: ast::Mac, ident: Option, attrs: Vec(path: &ast::Path, ident: Option, tts: Vec, mark: Mrk, + fn mac_result<'a>(path: &ast::Path, ident: Option, tts: Vec, mark: Mark, attrs: Vec, call_site: Span, fld: &'a mut MacroExpander) -> Option> { // Detect use of feature-gated or invalid attributes on macro invoations @@ -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 } +struct Marker { mark: Mark, expn_id: Option } 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_ident(&mut self, mut ident: Ident) -> Ident { + ident.ctxt = ident.ctxt.apply_mark(self.mark); + ident } - 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_mac(&mut self, mac: ast::Mac) -> ast::Mac { + noop_fold_mac(mac, self) } fn new_span(&mut self, mut span: Span) -> Span { @@ -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 { +fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec { noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None}) } diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/mtwt.rs index d2f6df9d5db..c4af4a1f85b 100644 --- a/src/libsyntax/ext/mtwt.rs +++ b/src/libsyntax/ext/mtwt.rs @@ -15,107 +15,104 @@ //! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216. //! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093 -pub use self::SyntaxContext_::*; - -use ast::{Ident, Mrk, SyntaxContext}; - use std::cell::RefCell; use std::collections::HashMap; +use std::fmt; -/// 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 `marks` ensures that adding the same mark to the -/// same context gives you back the same context as before. -pub struct SCTable { - table: RefCell>, - marks: RefCell>, +/// 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, } -#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)] -pub enum SyntaxContext_ { - EmptyCtxt, - Mark (Mrk,SyntaxContext), -} +/// A mark represents a unique id associated with a macro expansion. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct Mark(u32); -/// Extend a syntax context with a given mark -pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext { - with_sctable(|table| apply_mark_internal(m, ctxt, table)) -} - -/// Extend a syntax context with a given mark and sctable (explicit memoization) -fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext { - let ctxts = &mut *table.table.borrow_mut(); - match ctxts[ctxt.0 as usize] { - // Applying the same mark twice is a no-op. - Mark(outer_mark, prev_ctxt) if outer_mark == m => return prev_ctxt, - _ => *table.marks.borrow_mut().entry((ctxt, m)).or_insert_with(|| { - SyntaxContext(idx_push(ctxts, Mark(m, ctxt))) - }), +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) + }) } } -/// Fetch the SCTable from TLS, create one if it doesn't yet exist. -pub fn with_sctable(op: F) -> T where - F: FnOnce(&SCTable) -> T, -{ - thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal()); - SCTABLE_KEY.with(move |slot| op(slot)) +struct HygieneData { + syntax_contexts: Vec, + markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, + next_mark: Mark, } -// Make a fresh syntax context table with EmptyCtxt in slot zero. -fn new_sctable_internal() -> SCTable { - SCTable { - table: RefCell::new(vec![EmptyCtxt]), - marks: RefCell::new(HashMap::new()), +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: F) -> T { + thread_local! { + static HYGIENE_DATA: RefCell = RefCell::new(HygieneData::new()); + } + HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut())) } } -/// Clear the tables from TLD to reclaim memory. -pub fn clear_tables() { - with_sctable(|table| { - *table.table.borrow_mut() = Vec::new(); - *table.marks.borrow_mut() = HashMap::new(); - }); +pub fn reset_hygiene_data() { + HygieneData::with(|data| *data = HygieneData::new()) } -/// Reset the tables to their initial state -pub fn reset_tables() { - with_sctable(|table| { - *table.table.borrow_mut() = vec![EmptyCtxt]; - *table.marks.borrow_mut() = HashMap::new(); - }); -} +impl SyntaxContext { + pub const fn empty() -> Self { + SyntaxContext(0) + } -/// Add a value to the end of a vec, return its index -fn idx_push(vec: &mut Vec, val: T) -> u32 { - vec.push(val); - (vec.len() - 1) as u32 -} + pub fn data(self) -> SyntaxContextData { + HygieneData::with(|data| data.syntax_contexts[self.0 as usize]) + } -/// Return the outer mark for a context with a mark at the outside. -/// FAILS when outside is not a mark. -pub fn outer_mark(ctxt: SyntaxContext) -> Mrk { - with_sctable(|sctable| { - match (*sctable.table.borrow())[ctxt.0 as usize] { - Mark(mrk, _) => mrk, - _ => panic!("can't retrieve outer mark when outside is not a mark") + /// 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) + } } -/// 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(ident: Ident) -> Option<(Ident /* source ident */, Mrk /* source macro */)> { - with_sctable(|sctable| { - let ctxts = sctable.table.borrow(); - if let Mark(_expansion_mark, macro_ctxt) = ctxts[ident.ctxt.0 as usize] { - if let Mark(definition_mark, orig_ctxt) = ctxts[macro_ctxt.0 as usize] { - return Some((Ident::new(ident.name, orig_ctxt), definition_mark)); - } - } - None - }) +impl fmt::Debug for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#{}", self.0) + } } #[cfg(test)] diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index fe9d3ef7c23..f0a6f8edeec 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -633,8 +633,3 @@ pub fn fresh_name(src: ast::Ident) -> ast::Name { /*let num = rand::thread_rng().gen_uint_range(0,0xffff); gensym(format!("{}_{}",ident_to_string(src),num))*/ } - -// create a fresh mark. -pub fn fresh_mark() -> ast::Mrk { - gensym("mark").0 -}