]> git.proxmox.com Git - rustc.git/blame - src/libsyntax/ext/mtwt.rs
Imported Upstream version 1.11.0+dfsg1
[rustc.git] / src / libsyntax / ext / mtwt.rs
CommitLineData
1a4d82fc
JJ
1// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! Machinery for hygienic macros, as described in the MTWT[1] paper.
12//!
13//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
14//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
15//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
16//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
17
18pub use self::SyntaxContext_::*;
19
20use ast::{Ident, Mrk, Name, SyntaxContext};
21
22use std::cell::RefCell;
23use std::collections::HashMap;
24
25/// The SCTable contains a table of SyntaxContext_'s. It
26/// represents a flattened tree structure, to avoid having
27/// managed pointers everywhere (that caused an ICE).
3157f602 28/// the `marks` and `renames` fields are side-tables
1a4d82fc 29/// that ensure that adding the same mark to the same context
3157f602
XL
30/// gives you back the same context as before. This should cut
31/// down on memory use *a lot*; applying a mark to a tree containing
32/// 50 identifiers would otherwise generate 50 new contexts.
1a4d82fc
JJ
33pub struct SCTable {
34 table: RefCell<Vec<SyntaxContext_>>,
3157f602
XL
35 marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
36 renames: RefCell<HashMap<Name,SyntaxContext>>,
1a4d82fc
JJ
37}
38
c34b1796 39#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
1a4d82fc
JJ
40pub enum SyntaxContext_ {
41 EmptyCtxt,
42 Mark (Mrk,SyntaxContext),
3157f602 43 Rename (Name),
1a4d82fc
JJ
44 /// actually, IllegalCtxt may not be necessary.
45 IllegalCtxt
46}
47
48/// A list of ident->name renamings
49pub type RenameList = Vec<(Ident, Name)>;
50
51/// Extend a syntax context with a given mark
52pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
53 with_sctable(|table| apply_mark_internal(m, ctxt, table))
54}
55
56/// Extend a syntax context with a given mark and sctable (explicit memoization)
57fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
3157f602
XL
58 let ctxts = &mut *table.table.borrow_mut();
59 match ctxts[ctxt.0 as usize] {
60 // Applying the same mark twice is a no-op.
61 Mark(outer_mark, prev_ctxt) if outer_mark == m => return prev_ctxt,
62 _ => *table.marks.borrow_mut().entry((ctxt, m)).or_insert_with(|| {
63 SyntaxContext(idx_push(ctxts, Mark(m, ctxt)))
64 }),
65 }
1a4d82fc
JJ
66}
67
68/// Extend a syntax context with a given rename
3157f602
XL
69pub fn apply_rename(from: Ident, to: Name, ident: Ident) -> Ident {
70 with_sctable(|table| apply_rename_internal(from, to, ident, table))
1a4d82fc
JJ
71}
72
73/// Extend a syntax context with a given rename and sctable (explicit memoization)
3157f602
XL
74fn apply_rename_internal(from: Ident, to: Name, ident: Ident, table: &SCTable) -> Ident {
75 if (ident.name, ident.ctxt) != (from.name, from.ctxt) {
76 return ident;
77 }
78 let ctxt = *table.renames.borrow_mut().entry(to).or_insert_with(|| {
79 SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(to)))
80 });
81 Ident { ctxt: ctxt, ..ident }
1a4d82fc
JJ
82}
83
84/// Apply a list of renamings to a context
85// if these rename lists get long, it would make sense
86// to consider memoizing this fold. This may come up
87// when we add hygiene to item names.
3157f602
XL
88pub fn apply_renames(renames: &RenameList, ident: Ident) -> Ident {
89 renames.iter().fold(ident, |ident, &(from, to)| {
90 apply_rename(from, to, ident)
1a4d82fc
JJ
91 })
92}
93
94/// Fetch the SCTable from TLS, create one if it doesn't yet exist.
95pub fn with_sctable<T, F>(op: F) -> T where
96 F: FnOnce(&SCTable) -> T,
97{
98 thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal());
99 SCTABLE_KEY.with(move |slot| op(slot))
100}
101
102// Make a fresh syntax context table with EmptyCtxt in slot zero
103// and IllegalCtxt in slot one.
104fn new_sctable_internal() -> SCTable {
105 SCTable {
106 table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
3157f602
XL
107 marks: RefCell::new(HashMap::new()),
108 renames: RefCell::new(HashMap::new()),
1a4d82fc
JJ
109 }
110}
111
112/// Print out an SCTable for debugging
113pub fn display_sctable(table: &SCTable) {
114 error!("SC table:");
115 for (idx,val) in table.table.borrow().iter().enumerate() {
116 error!("{:4} : {:?}",idx,val);
117 }
118}
119
120/// Clear the tables from TLD to reclaim memory.
121pub fn clear_tables() {
122 with_sctable(|table| {
123 *table.table.borrow_mut() = Vec::new();
3157f602
XL
124 *table.marks.borrow_mut() = HashMap::new();
125 *table.renames.borrow_mut() = HashMap::new();
1a4d82fc 126 });
1a4d82fc
JJ
127}
128
129/// Reset the tables to their initial state
130pub fn reset_tables() {
131 with_sctable(|table| {
132 *table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt);
3157f602
XL
133 *table.marks.borrow_mut() = HashMap::new();
134 *table.renames.borrow_mut() = HashMap::new();
1a4d82fc 135 });
1a4d82fc
JJ
136}
137
138/// Add a value to the end of a vec, return its index
139fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
140 vec.push(val);
141 (vec.len() - 1) as u32
142}
143
144/// Resolve a syntax object to a name, per MTWT.
145pub fn resolve(id: Ident) -> Name {
146 with_sctable(|sctable| {
3157f602 147 resolve_internal(id, sctable)
1a4d82fc
JJ
148 })
149}
150
1a4d82fc
JJ
151/// Resolve a syntax object to a name, per MTWT.
152/// adding memoization to resolve 500+ seconds in resolve for librustc (!)
3157f602
XL
153fn resolve_internal(id: Ident, table: &SCTable) -> Name {
154 match table.table.borrow()[id.ctxt.0 as usize] {
155 EmptyCtxt => id.name,
156 // ignore marks here:
157 Mark(_, subctxt) => resolve_internal(Ident::new(id.name, subctxt), table),
158 Rename(name) => name,
159 IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
1a4d82fc
JJ
160 }
161}
162
163/// Return the outer mark for a context with a mark at the outside.
164/// FAILS when outside is not a mark.
165pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
166 with_sctable(|sctable| {
b039eaaf 167 match (*sctable.table.borrow())[ctxt.0 as usize] {
1a4d82fc
JJ
168 Mark(mrk, _) => mrk,
169 _ => panic!("can't retrieve outer mark when outside is not a mark")
170 }
171 })
172}
173
1a4d82fc
JJ
174#[cfg(test)]
175mod tests {
1a4d82fc 176 use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
3157f602
XL
177 use super::{resolve, apply_mark_internal, new_sctable_internal};
178 use super::{SCTable, Mark};
1a4d82fc
JJ
179
180 fn id(n: u32, s: SyntaxContext) -> Ident {
b039eaaf 181 Ident::new(Name(n), s)
1a4d82fc
JJ
182 }
183
1a4d82fc
JJ
184 // extend a syntax context with a sequence of marks given
185 // in a vector. v[0] will be the outermost mark.
186 fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
187 -> SyntaxContext {
188 mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
189 {apply_mark_internal(*mrk,tail,table)})
190 }
191
192 #[test] fn unfold_marks_test() {
193 let mut t = new_sctable_internal();
194
b039eaaf 195 assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),SyntaxContext(3));
1a4d82fc
JJ
196 {
197 let table = t.table.borrow();
b039eaaf
SL
198 assert!((*table)[2] == Mark(7,EMPTY_CTXT));
199 assert!((*table)[3] == Mark(3,SyntaxContext(2)));
1a4d82fc
JJ
200 }
201 }
202
1a4d82fc
JJ
203 #[test]
204 fn mtwt_resolve_test(){
205 let a = 40;
206 assert_eq!(resolve(id(a,EMPTY_CTXT)),Name(a));
207 }
208
1a4d82fc
JJ
209 #[test]
210 fn hashing_tests () {
211 let mut t = new_sctable_internal();
b039eaaf
SL
212 assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(2));
213 assert_eq!(apply_mark_internal(13,EMPTY_CTXT,&mut t),SyntaxContext(3));
1a4d82fc 214 // using the same one again should result in the same index:
b039eaaf 215 assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(2));
1a4d82fc
JJ
216 // I'm assuming that the rename table will behave the same....
217 }
1a4d82fc 218}