about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorJeffrey Seyfried <jeffrey.seyfried@gmail.com>2016-06-22 08:03:42 +0000
committerJeffrey Seyfried <jeffrey.seyfried@gmail.com>2016-07-17 17:12:32 +0000
commit76ed4456224f6bf7dc35dc72cc1c2cc93186285a (patch)
treee91209b14a183011a23baf699e1686fc9c233c27 /src/libsyntax
parent145f0ec88caaae531d7ba0232c4ef39704a23af2 (diff)
downloadrust-76ed4456224f6bf7dc35dc72cc1c2cc93186285a.tar.gz
rust-76ed4456224f6bf7dc35dc72cc1c2cc93186285a.zip
Clean up and encapsulate `syntax::ext::mtwt`
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs22
-rw-r--r--src/libsyntax/ext/expand.rs44
-rw-r--r--src/libsyntax/ext/mtwt.rs155
-rw-r--r--src/libsyntax/parse/token.rs5
4 files changed, 94 insertions, 132 deletions
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<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
@@ -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 {
@@ -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})
 }
 
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<Vec<SyntaxContext_>>,
-    marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
-}
+/// 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(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
-pub enum SyntaxContext_ {
-    EmptyCtxt,
-    Mark (Mrk,SyntaxContext),
+#[derive(Copy, Clone)]
+pub struct SyntaxContextData {
+    pub outer_mark: Mark,
+    pub prev_ctxt: SyntaxContext,
 }
 
-/// 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))
-}
+/// 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 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<T, F>(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<SyntaxContextData>,
+    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),
+        }
     }
-}
 
-/// 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();
-    });
+    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()))
+    }
 }
 
-/// 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();
-    });
+pub fn reset_hygiene_data() {
+    HygieneData::with(|data| *data = HygieneData::new())
 }
 
-/// Add a value to the end of a vec, return its index
-fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
-    vec.push(val);
-    (vec.len() - 1) as u32
-}
+impl SyntaxContext {
+    pub const fn empty() -> Self {
+        SyntaxContext(0)
+    }
+
+    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
-}