(index<- ) ./libsyntax/ext/mtwt.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
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
18 use ast::{Ident, Mrk, Name, SyntaxContext};
19
20 use std::cell::RefCell;
21 use std::rc::Rc;
22
23 use 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).
28 // the mark_memo and rename_memo fields are side-tables
29 // that ensure that adding the same mark to the same context
30 // gives you back the same context as before. This shouldn't
31 // change the semantics--everything here is immutable--but
32 // it should cut down on memory use *a lot*; applying a mark
33 // to a tree containing 50 identifiers would otherwise generate
34 pub struct SCTable {
35 table: RefCell<Vec<SyntaxContext_>>,
36 mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
37 rename_memo: RefCell<HashMap<(SyntaxContext,Ident,Name),SyntaxContext>>,
38 }
39
40 #[deriving(Eq, Encodable, Decodable, Hash)]
41 pub enum SyntaxContext_ {
42 EmptyCtxt,
43 Mark (Mrk,SyntaxContext),
44 // flattening the name and syntaxcontext into the rename...
45 // HIDDEN INVARIANTS:
46 // 1) the first name in a Rename node
47 // can only be a programmer-supplied name.
48 // 2) Every Rename node with a given Name in the
49 // "to" slot must have the same name and context
50 // in the "from" slot. In essence, they're all
51 // pointers to a single "rename" event node.
52 Rename (Ident,Name,SyntaxContext),
53 // actually, IllegalCtxt may not be necessary.
54 IllegalCtxt
55 }
56
57 /// Extend a syntax context with a given mark
58 pub fn new_mark(m: Mrk, tail: SyntaxContext) -> SyntaxContext {
59 with_sctable(|table| new_mark_internal(m, tail, table))
60 }
61
62 // Extend a syntax context with a given mark and table
63 fn new_mark_internal(m: Mrk, tail: SyntaxContext, table: &SCTable) -> SyntaxContext {
64 let key = (tail, m);
65 let new_ctxt = |_: &(SyntaxContext, Mrk)|
66 idx_push(&mut *table.table.borrow_mut(), Mark(m, tail));
67
68 *table.mark_memo.borrow_mut().find_or_insert_with(key, new_ctxt)
69 }
70
71 /// Extend a syntax context with a given rename
72 pub fn new_rename(id: Ident, to:Name,
73 tail: SyntaxContext) -> SyntaxContext {
74 with_sctable(|table| new_rename_internal(id, to, tail, table))
75 }
76
77 // Extend a syntax context with a given rename and sctable
78 fn new_rename_internal(id: Ident,
79 to: Name,
80 tail: SyntaxContext,
81 table: &SCTable) -> SyntaxContext {
82 let key = (tail,id,to);
83 let new_ctxt = |_: &(SyntaxContext, Ident, Mrk)|
84 idx_push(&mut *table.table.borrow_mut(), Rename(id, to, tail));
85
86 *table.rename_memo.borrow_mut().find_or_insert_with(key, new_ctxt)
87 }
88
89 /// Fetch the SCTable from TLS, create one if it doesn't yet exist.
90 pub fn with_sctable<T>(op: |&SCTable| -> T) -> T {
91 local_data_key!(sctable_key: Rc<SCTable>)
92
93 match sctable_key.get() {
94 Some(ts) => op(&**ts),
95 None => {
96 let ts = Rc::new(new_sctable_internal());
97 sctable_key.replace(Some(ts.clone()));
98 op(&*ts)
99 }
100 }
101 }
102
103 // Make a fresh syntax context table with EmptyCtxt in slot zero
104 // and IllegalCtxt in slot one.
105 fn new_sctable_internal() -> SCTable {
106 SCTable {
107 table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
108 mark_memo: RefCell::new(HashMap::new()),
109 rename_memo: RefCell::new(HashMap::new()),
110 }
111 }
112
113 /// Print out an SCTable for debugging
114 pub fn display_sctable(table: &SCTable) {
115 error!("SC table:");
116 for (idx,val) in table.table.borrow().iter().enumerate() {
117 error!("{:4u} : {:?}",idx,val);
118 }
119 }
120
121 /// Clear the tables from TLD to reclaim memory.
122 pub fn clear_tables() {
123 with_sctable(|table| {
124 *table.table.borrow_mut() = Vec::new();
125 *table.mark_memo.borrow_mut() = HashMap::new();
126 *table.rename_memo.borrow_mut() = HashMap::new();
127 });
128 with_resolve_table_mut(|table| *table = HashMap::new());
129 }
130
131 // Add a value to the end of a vec, return its index
132 fn idx_push<T>(vec: &mut Vec<T> , val: T) -> u32 {
133 vec.push(val);
134 (vec.len() - 1) as u32
135 }
136
137 /// Resolve a syntax object to a name, per MTWT.
138 pub fn resolve(id: Ident) -> Name {
139 with_sctable(|sctable| {
140 with_resolve_table_mut(|resolve_table| {
141 resolve_internal(id, sctable, resolve_table)
142 })
143 })
144 }
145
146 type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
147
148 // okay, I admit, putting this in TLS is not so nice:
149 // fetch the SCTable from TLS, create one if it doesn't yet exist.
150 fn with_resolve_table_mut<T>(op: |&mut ResolveTable| -> T) -> T {
151 local_data_key!(resolve_table_key: Rc<RefCell<ResolveTable>>)
152
153 match resolve_table_key.get() {
154 Some(ts) => op(&mut *ts.borrow_mut()),
155 None => {
156 let ts = Rc::new(RefCell::new(HashMap::new()));
157 resolve_table_key.replace(Some(ts.clone()));
158 op(&mut *ts.borrow_mut())
159 }
160 }
161 }
162
163 // Resolve a syntax object to a name, per MTWT.
164 // adding memorization to possibly resolve 500+ seconds in resolve for librustc (!)
165 fn resolve_internal(id: Ident,
166 table: &SCTable,
167 resolve_table: &mut ResolveTable) -> Name {
168 let key = (id.name, id.ctxt);
169
170 match resolve_table.find(&key) {
171 Some(&name) => return name,
172 None => {}
173 }
174
175 let resolved = {
176 let result = *table.table.borrow().get(id.ctxt as uint);
177 match result {
178 EmptyCtxt => id.name,
179 // ignore marks here:
180 Mark(_,subctxt) =>
181 resolve_internal(Ident{name:id.name, ctxt: subctxt},
182 table, resolve_table),
183 // do the rename if necessary:
184 Rename(Ident{name, ctxt}, toname, subctxt) => {
185 let resolvedfrom =
186 resolve_internal(Ident{name:name, ctxt:ctxt},
187 table, resolve_table);
188 let resolvedthis =
189 resolve_internal(Ident{name:id.name, ctxt:subctxt},
190 table, resolve_table);
191 if (resolvedthis == resolvedfrom)
192 && (marksof_internal(ctxt, resolvedthis, table)
193 == marksof_internal(subctxt, resolvedthis, table)) {
194 toname
195 } else {
196 resolvedthis
197 }
198 }
199 IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
200 }
201 };
202 resolve_table.insert(key, resolved);
203 resolved
204 }
205
206 /// Compute the marks associated with a syntax context.
207 pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
208 with_sctable(|table| marksof_internal(ctxt, stopname, table))
209 }
210
211 // the internal function for computing marks
212 // it's not clear to me whether it's better to use a [] mutable
213 // vector or a cons-list for this.
214 fn marksof_internal(ctxt: SyntaxContext,
215 stopname: Name,
216 table: &SCTable) -> Vec<Mrk> {
217 let mut result = Vec::new();
218 let mut loopvar = ctxt;
219 loop {
220 let table_entry = *table.table.borrow().get(loopvar as uint);
221 match table_entry {
222 EmptyCtxt => {
223 return result;
224 },
225 Mark(mark, tl) => {
226 xorPush(&mut result, mark);
227 loopvar = tl;
228 },
229 Rename(_,name,tl) => {
230 // see MTWT for details on the purpose of the stopname.
231 // short version: it prevents duplication of effort.
232 if name == stopname {
233 return result;
234 } else {
235 loopvar = tl;
236 }
237 }
238 IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
239 }
240 }
241 }
242
243 /// Return the outer mark for a context with a mark at the outside.
244 /// FAILS when outside is not a mark.
245 pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
246 with_sctable(|sctable| {
247 match *sctable.table.borrow().get(ctxt as uint) {
248 Mark(mrk, _) => mrk,
249 _ => fail!("can't retrieve outer mark when outside is not a mark")
250 }
251 })
252 }
253
254 // Push a name... unless it matches the one on top, in which
255 // case pop and discard (so two of the same marks cancel)
256 fn xorPush(marks: &mut Vec<Mrk>, mark: Mrk) {
257 if (marks.len() > 0) && (*marks.last().unwrap() == mark) {
258 marks.pop().unwrap();
259 } else {
260 marks.push(mark);
261 }
262 }
263
264 #[cfg(test)]
265 mod tests {
266 use ast::*;
267 use super::{resolve, xorPush, new_mark_internal, new_sctable_internal};
268 use super::{new_rename_internal, marksof_internal, resolve_internal};
269 use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
270 use collections::HashMap;
271
272 #[test] fn xorpush_test () {
273 let mut s = Vec::new();
274 xorPush(&mut s, 14);
275 assert_eq!(s.clone(), vec!(14));
276 xorPush(&mut s, 14);
277 assert_eq!(s.clone(), Vec::new());
278 xorPush(&mut s, 14);
279 assert_eq!(s.clone(), vec!(14));
280 xorPush(&mut s, 15);
281 assert_eq!(s.clone(), vec!(14, 15));
282 xorPush(&mut s, 16);
283 assert_eq!(s.clone(), vec!(14, 15, 16));
284 xorPush(&mut s, 16);
285 assert_eq!(s.clone(), vec!(14, 15));
286 xorPush(&mut s, 15);
287 assert_eq!(s.clone(), vec!(14));
288 }
289
290 fn id(n: Name, s: SyntaxContext) -> Ident {
291 Ident {name: n, ctxt: s}
292 }
293
294 // because of the SCTable, I now need a tidy way of
295 // creating syntax objects. Sigh.
296 #[deriving(Clone, Eq, Show)]
297 enum TestSC {
298 M(Mrk),
299 R(Ident,Name)
300 }
301
302 // unfold a vector of TestSC values into a SCTable,
303 // returning the resulting index
304 fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
305 -> SyntaxContext {
306 tscs.iter().rev().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
307 {match *tsc {
308 M(mrk) => new_mark_internal(mrk,tail,table),
309 R(ident,name) => new_rename_internal(ident,name,tail,table)}})
310 }
311
312 // gather a SyntaxContext back into a vector of TestSCs
313 fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
314 let mut result = Vec::new();
315 loop {
316 let table = table.table.borrow();
317 match *table.get(sc as uint) {
318 EmptyCtxt => {return result;},
319 Mark(mrk,tail) => {
320 result.push(M(mrk));
321 sc = tail;
322 continue;
323 },
324 Rename(id,name,tail) => {
325 result.push(R(id,name));
326 sc = tail;
327 continue;
328 }
329 IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
330 }
331 }
332 }
333
334 #[test] fn test_unfold_refold(){
335 let mut t = new_sctable_internal();
336
337 let test_sc = vec!(M(3),R(id(101,0),14),M(9));
338 assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),4);
339 {
340 let table = t.table.borrow();
341 assert!(*table.get(2) == Mark(9,0));
342 assert!(*table.get(3) == Rename(id(101,0),14,2));
343 assert!(*table.get(4) == Mark(3,3));
344 }
345 assert_eq!(refold_test_sc(4,&t),test_sc);
346 }
347
348 // extend a syntax context with a sequence of marks given
349 // in a vector. v[0] will be the outermost mark.
350 fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
351 -> SyntaxContext {
352 mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
353 {new_mark_internal(*mrk,tail,table)})
354 }
355
356 #[test] fn unfold_marks_test() {
357 let mut t = new_sctable_internal();
358
359 assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),3);
360 {
361 let table = t.table.borrow();
362 assert!(*table.get(2) == Mark(7,0));
363 assert!(*table.get(3) == Mark(3,2));
364 }
365 }
366
367 #[test] fn test_marksof () {
368 let stopname = 242;
369 let name1 = 243;
370 let mut t = new_sctable_internal();
371 assert_eq!(marksof_internal (EMPTY_CTXT,stopname,&t),Vec::new());
372 // FIXME #5074: ANF'd to dodge nested calls
373 { let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
374 assert_eq! (marksof_internal (ans,stopname,&t),vec!(4,98));}
375 // does xoring work?
376 { let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
377 assert_eq! (marksof_internal (ans,stopname,&t), vec!(16));}
378 // does nested xoring work?
379 { let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
380 assert_eq! (marksof_internal (ans, stopname,&t), vec!(16));}
381 // rename where stop doesn't match:
382 { let chain = vec!(M(9),
383 R(id(name1,
384 new_mark_internal (4, EMPTY_CTXT,&mut t)),
385 100101102),
386 M(14));
387 let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
388 assert_eq! (marksof_internal (ans, stopname, &t), vec!(9,14));}
389 // rename where stop does match
390 { let name1sc = new_mark_internal(4, EMPTY_CTXT, &mut t);
391 let chain = vec!(M(9),
392 R(id(name1, name1sc),
393 stopname),
394 M(14));
395 let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
396 assert_eq! (marksof_internal (ans, stopname, &t), vec!(9)); }
397 }
398
399
400 #[test] fn resolve_tests () {
401 let a = 40;
402 let mut t = new_sctable_internal();
403 let mut rt = HashMap::new();
404 // - ctxt is MT
405 assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),a);
406 // - simple ignored marks
407 { let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
408 assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
409 // - orthogonal rename where names don't match
410 { let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),51),M(12)),EMPTY_CTXT,&mut t);
411 assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
412 // - rename where names do match, but marks don't
413 { let sc1 = new_mark_internal(1,EMPTY_CTXT,&mut t);
414 let sc = unfold_test_sc(vec!(R(id(a,sc1),50),
415 M(1),
416 M(2)),
417 EMPTY_CTXT,&mut t);
418 assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), a);}
419 // - rename where names and marks match
420 { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
421 let sc = unfold_test_sc(vec!(R(id(a,sc1),50),M(1),M(2)),EMPTY_CTXT,&mut t);
422 assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
423 // - rename where names and marks match by literal sharing
424 { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
425 let sc = unfold_test_sc(vec!(R(id(a,sc1),50)),sc1,&mut t);
426 assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
427 // - two renames of the same var.. can only happen if you use
428 // local-expand to prevent the inner binding from being renamed
429 // during the rename-pass caused by the first:
430 println!("about to run bad test");
431 { let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),50),
432 R(id(a,EMPTY_CTXT),51)),
433 EMPTY_CTXT,&mut t);
434 assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 51); }
435 // the simplest double-rename:
436 { let a_to_a50 = new_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t);
437 let a50_to_a51 = new_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
438 assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),51);
439 // mark on the outside doesn't stop rename:
440 let sc = new_mark_internal(9,a50_to_a51,&mut t);
441 assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),51);
442 // but mark on the inside does:
443 let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),51),
444 M(9)),
445 a_to_a50,
446 &mut t);
447 assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),50);}
448 }
449
450 #[test] fn mtwt_resolve_test(){
451 let a = 40;
452 assert_eq!(resolve(id(a,EMPTY_CTXT)),a);
453 }
454
455
456 #[test] fn hashing_tests () {
457 let mut t = new_sctable_internal();
458 assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
459 assert_eq!(new_mark_internal(13,EMPTY_CTXT,&mut t),3);
460 // using the same one again should result in the same index:
461 assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
462 // I'm assuming that the rename table will behave the same....
463 }
464
465 #[test] fn resolve_table_hashing_tests() {
466 let mut t = new_sctable_internal();
467 let mut rt = HashMap::new();
468 assert_eq!(rt.len(),0);
469 resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
470 assert_eq!(rt.len(),1);
471 resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
472 assert_eq!(rt.len(),2);
473 resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
474 assert_eq!(rt.len(),2);
475 }
476 }
libsyntax/ext/mtwt.rs:89:68-89:68 -fn- definition:
/// Fetch the SCTable from TLS, create one if it doesn't yet exist.
pub fn with_sctable<T>(op: |&SCTable| -> T) -> T {
local_data_key!(sctable_key: Rc<SCTable>)
references:- 6138: pub fn resolve(id: Ident) -> Name {
139: with_sctable(|sctable| {
140: with_resolve_table_mut(|resolve_table| {
--
245: pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
246: with_sctable(|sctable| {
247: match *sctable.table.borrow().get(ctxt as uint) {
libsyntax/ext/mtwt.rs:33:64-33:64 -struct- definition:
// to a tree containing 50 identifiers would otherwise generate
pub struct SCTable {
table: RefCell<Vec<SyntaxContext_>>,
references:- 9113: /// Print out an SCTable for debugging
114: pub fn display_sctable(table: &SCTable) {
115: error!("SC table:");
--
215: stopname: Name,
216: table: &SCTable) -> Vec<Mrk> {
217: let mut result = Vec::new();
libsyntax/ext/mtwt.rs:131:53-131:53 -fn- definition:
// 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);
references:- 283: let new_ctxt = |_: &(SyntaxContext, Ident, Mrk)|
84: idx_push(&mut *table.table.borrow_mut(), Rename(id, to, tail));
libsyntax/ext/mtwt.rs:164:84-164:84 -fn- definition:
// adding memorization to possibly resolve 500+ seconds in resolve for librustc (!)
fn resolve_internal(id: Ident,
table: &SCTable,
references:- 4188: let resolvedthis =
189: resolve_internal(Ident{name:id.name, ctxt:subctxt},
190: table, resolve_table);
libsyntax/ext/mtwt.rs:57:46-57:46 -fn- definition:
/// Extend a syntax context with a given mark
pub fn new_mark(m: Mrk, tail: SyntaxContext) -> SyntaxContext {
with_sctable(|table| new_mark_internal(m, tail, table))
references:- 2libsyntax/ext/expand.rs:
907: fold_tts(tts.as_slice(), self),
908: mtwt::new_mark(self.mark, ctxt))
909: }
libsyntax/ext/mtwt.rs:145:1-145:1 -NK_AS_STR_TODO- definition:
type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
// okay, I admit, putting this in TLS is not so nice:
// fetch the SCTable from TLS, create one if it doesn't yet exist.
references:- 3150: fn with_resolve_table_mut<T>(op: |&mut ResolveTable| -> T) -> T {
151: local_data_key!(resolve_table_key: Rc<RefCell<ResolveTable>>)
--
166: table: &SCTable,
167: resolve_table: &mut ResolveTable) -> Name {
168: let key = (id.name, id.ctxt);
libsyntax/ext/mtwt.rs:137:49-137:49 -fn- definition:
/// Resolve a syntax object to a name, per MTWT.
pub fn resolve(id: Ident) -> Name {
with_sctable(|sctable| {
references:- 2libsyntax/parse/token.rs:
723: (&IDENT(id1,_),&IDENT(id2,_)) | (&LIFETIME(id1),&LIFETIME(id2)) =>
724: mtwt::resolve(id1) == mtwt::resolve(id2),
725: _ => *t1 == *t2
libsyntax/ext/mtwt.rs:40:44-40:44 -enum- definition:
pub enum SyntaxContext_ {
EmptyCtxt,
Mark (Mrk,SyntaxContext),
references:- 841: pub enum SyntaxContext_ {
libsyntax/ext/mtwt.rs:213:35-213:35 -fn- definition:
// vector or a cons-list for this.
fn marksof_internal(ctxt: SyntaxContext,
stopname: Name,
references:- 3191: if (resolvedthis == resolvedfrom)
192: && (marksof_internal(ctxt, resolvedthis, table)
193: == marksof_internal(subctxt, resolvedthis, table)) {
--
207: pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
208: with_sctable(|table| marksof_internal(ctxt, stopname, table))
209: }
libsyntax/ext/mtwt.rs:149:67-149:67 -fn- definition:
// fetch the SCTable from TLS, create one if it doesn't yet exist.
fn with_resolve_table_mut<T>(op: |&mut ResolveTable| -> T) -> T {
local_data_key!(resolve_table_key: Rc<RefCell<ResolveTable>>)
references:- 2139: with_sctable(|sctable| {
140: with_resolve_table_mut(|resolve_table| {
141: resolve_internal(id, sctable, resolve_table)