Skip to content

Commit d7fa72a

Browse files
committed
Fix hygiene regression
1 parent b1ae194 commit d7fa72a

File tree

2 files changed

+44
-151
lines changed

2 files changed

+44
-151
lines changed

src/libsyntax/ext/expand.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ pub struct IdentRenamer<'a> {
681681

682682
impl<'a> Folder for IdentRenamer<'a> {
683683
fn fold_ident(&mut self, id: Ident) -> Ident {
684-
Ident::new(id.name, mtwt::apply_renames(self.renames, id.ctxt))
684+
mtwt::apply_renames(self.renames, id)
685685
}
686686
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
687687
fold::noop_fold_mac(mac, self)
@@ -705,8 +705,7 @@ impl<'a> Folder for PatIdentRenamer<'a> {
705705

706706
pat.map(|ast::Pat {id, node, span}| match node {
707707
PatKind::Ident(binding_mode, Spanned{span: sp, node: ident}, sub) => {
708-
let new_ident = Ident::new(ident.name,
709-
mtwt::apply_renames(self.renames, ident.ctxt));
708+
let new_ident = mtwt::apply_renames(self.renames, ident);
710709
let new_node =
711710
PatKind::Ident(binding_mode,
712711
Spanned{span: sp, node: new_ident},

src/libsyntax/ext/mtwt.rs

Lines changed: 42 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -25,34 +25,22 @@ use std::collections::HashMap;
2525
/// The SCTable contains a table of SyntaxContext_'s. It
2626
/// represents a flattened tree structure, to avoid having
2727
/// managed pointers everywhere (that caused an ICE).
28-
/// the mark_memo and rename_memo fields are side-tables
28+
/// the `marks` and `renames` fields are side-tables
2929
/// 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-
/// 50 new contexts
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.
3533
pub struct SCTable {
3634
table: RefCell<Vec<SyntaxContext_>>,
37-
mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
38-
// The pair (Name,SyntaxContext) is actually one Ident, but it needs to be hashed and
39-
// compared as pair (name, ctxt) and not as an Ident
40-
rename_memo: RefCell<HashMap<(SyntaxContext,(Name,SyntaxContext),Name),SyntaxContext>>,
35+
marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
36+
renames: RefCell<HashMap<Name,SyntaxContext>>,
4137
}
4238

4339
#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
4440
pub enum SyntaxContext_ {
4541
EmptyCtxt,
4642
Mark (Mrk,SyntaxContext),
47-
/// flattening the name and syntaxcontext into the rename...
48-
/// HIDDEN INVARIANTS:
49-
/// 1) the first name in a Rename node
50-
/// can only be a programmer-supplied name.
51-
/// 2) Every Rename node with a given Name in the
52-
/// "to" slot must have the same name and context
53-
/// in the "from" slot. In essence, they're all
54-
/// pointers to a single "rename" event node.
55-
Rename (Ident,Name,SyntaxContext),
43+
Rename (Name),
5644
/// actually, IllegalCtxt may not be necessary.
5745
IllegalCtxt
5846
}
@@ -67,37 +55,39 @@ pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
6755

6856
/// Extend a syntax context with a given mark and sctable (explicit memoization)
6957
fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
70-
let key = (ctxt, m);
71-
*table.mark_memo.borrow_mut().entry(key).or_insert_with(|| {
72-
SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Mark(m, ctxt)))
73-
})
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+
}
7466
}
7567

7668
/// Extend a syntax context with a given rename
77-
pub fn apply_rename(id: Ident, to:Name,
78-
ctxt: SyntaxContext) -> SyntaxContext {
79-
with_sctable(|table| apply_rename_internal(id, to, ctxt, table))
69+
pub fn apply_rename(from: Ident, to: Name, ident: Ident) -> Ident {
70+
with_sctable(|table| apply_rename_internal(from, to, ident, table))
8071
}
8172

8273
/// Extend a syntax context with a given rename and sctable (explicit memoization)
83-
fn apply_rename_internal(id: Ident,
84-
to: Name,
85-
ctxt: SyntaxContext,
86-
table: &SCTable) -> SyntaxContext {
87-
let key = (ctxt, (id.name, id.ctxt), to);
88-
89-
*table.rename_memo.borrow_mut().entry(key).or_insert_with(|| {
90-
SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt)))
91-
})
74+
fn 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 }
9282
}
9383

9484
/// Apply a list of renamings to a context
9585
// if these rename lists get long, it would make sense
9686
// to consider memoizing this fold. This may come up
9787
// when we add hygiene to item names.
98-
pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext {
99-
renames.iter().fold(ctxt, |ctxt, &(from, to)| {
100-
apply_rename(from, to, ctxt)
88+
pub fn apply_renames(renames: &RenameList, ident: Ident) -> Ident {
89+
renames.iter().fold(ident, |ident, &(from, to)| {
90+
apply_rename(from, to, ident)
10191
})
10292
}
10393

@@ -114,8 +104,8 @@ pub fn with_sctable<T, F>(op: F) -> T where
114104
fn new_sctable_internal() -> SCTable {
115105
SCTable {
116106
table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
117-
mark_memo: RefCell::new(HashMap::new()),
118-
rename_memo: RefCell::new(HashMap::new()),
107+
marks: RefCell::new(HashMap::new()),
108+
renames: RefCell::new(HashMap::new()),
119109
}
120110
}
121111

@@ -131,20 +121,18 @@ pub fn display_sctable(table: &SCTable) {
131121
pub fn clear_tables() {
132122
with_sctable(|table| {
133123
*table.table.borrow_mut() = Vec::new();
134-
*table.mark_memo.borrow_mut() = HashMap::new();
135-
*table.rename_memo.borrow_mut() = HashMap::new();
124+
*table.marks.borrow_mut() = HashMap::new();
125+
*table.renames.borrow_mut() = HashMap::new();
136126
});
137-
with_resolve_table_mut(|table| *table = HashMap::new());
138127
}
139128

140129
/// Reset the tables to their initial state
141130
pub fn reset_tables() {
142131
with_sctable(|table| {
143132
*table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt);
144-
*table.mark_memo.borrow_mut() = HashMap::new();
145-
*table.rename_memo.borrow_mut() = HashMap::new();
133+
*table.marks.borrow_mut() = HashMap::new();
134+
*table.renames.borrow_mut() = HashMap::new();
146135
});
147-
with_resolve_table_mut(|table| *table = HashMap::new());
148136
}
149137

150138
/// Add a value to the end of a vec, return its index
@@ -156,103 +144,19 @@ fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
156144
/// Resolve a syntax object to a name, per MTWT.
157145
pub fn resolve(id: Ident) -> Name {
158146
with_sctable(|sctable| {
159-
with_resolve_table_mut(|resolve_table| {
160-
resolve_internal(id, sctable, resolve_table)
161-
})
147+
resolve_internal(id, sctable)
162148
})
163149
}
164150

165-
type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
166-
167-
// okay, I admit, putting this in TLS is not so nice:
168-
// fetch the SCTable from TLS, create one if it doesn't yet exist.
169-
fn with_resolve_table_mut<T, F>(op: F) -> T where
170-
F: FnOnce(&mut ResolveTable) -> T,
171-
{
172-
thread_local!(static RESOLVE_TABLE_KEY: RefCell<ResolveTable> = {
173-
RefCell::new(HashMap::new())
174-
});
175-
176-
RESOLVE_TABLE_KEY.with(move |slot| op(&mut *slot.borrow_mut()))
177-
}
178-
179151
/// Resolve a syntax object to a name, per MTWT.
180152
/// adding memoization to resolve 500+ seconds in resolve for librustc (!)
181-
fn resolve_internal(id: Ident,
182-
table: &SCTable,
183-
resolve_table: &mut ResolveTable) -> Name {
184-
let key = (id.name, id.ctxt);
185-
186-
match resolve_table.get(&key) {
187-
Some(&name) => return name,
188-
None => {}
189-
}
190-
191-
let resolved = {
192-
let result = (*table.table.borrow())[id.ctxt.0 as usize];
193-
match result {
194-
EmptyCtxt => id.name,
195-
// ignore marks here:
196-
Mark(_,subctxt) =>
197-
resolve_internal(Ident::new(id.name, subctxt),
198-
table, resolve_table),
199-
// do the rename if necessary:
200-
Rename(Ident{name, ctxt}, toname, subctxt) => {
201-
let resolvedfrom =
202-
resolve_internal(Ident::new(name, ctxt),
203-
table, resolve_table);
204-
let resolvedthis =
205-
resolve_internal(Ident::new(id.name, subctxt),
206-
table, resolve_table);
207-
if (resolvedthis == resolvedfrom)
208-
&& (marksof_internal(ctxt, resolvedthis, table)
209-
== marksof_internal(subctxt, resolvedthis, table)) {
210-
toname
211-
} else {
212-
resolvedthis
213-
}
214-
}
215-
IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
216-
}
217-
};
218-
resolve_table.insert(key, resolved);
219-
resolved
220-
}
221-
222-
/// Compute the marks associated with a syntax context.
223-
pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
224-
with_sctable(|table| marksof_internal(ctxt, stopname, table))
225-
}
226-
227-
// the internal function for computing marks
228-
// it's not clear to me whether it's better to use a [] mutable
229-
// vector or a cons-list for this.
230-
fn marksof_internal(ctxt: SyntaxContext,
231-
stopname: Name,
232-
table: &SCTable) -> Vec<Mrk> {
233-
let mut result = Vec::new();
234-
let mut loopvar = ctxt;
235-
loop {
236-
let table_entry = (*table.table.borrow())[loopvar.0 as usize];
237-
match table_entry {
238-
EmptyCtxt => {
239-
return result;
240-
},
241-
Mark(mark, tl) => {
242-
xor_push(&mut result, mark);
243-
loopvar = tl;
244-
},
245-
Rename(_,name,tl) => {
246-
// see MTWT for details on the purpose of the stopname.
247-
// short version: it prevents duplication of effort.
248-
if name == stopname {
249-
return result;
250-
} else {
251-
loopvar = tl;
252-
}
253-
}
254-
IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
255-
}
153+
fn 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")
256160
}
257161
}
258162

@@ -267,16 +171,6 @@ pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
267171
})
268172
}
269173

270-
/// Push a name... unless it matches the one on top, in which
271-
/// case pop and discard (so two of the same marks cancel)
272-
fn xor_push(marks: &mut Vec<Mrk>, mark: Mrk) {
273-
if (!marks.is_empty()) && (*marks.last().unwrap() == mark) {
274-
marks.pop().unwrap();
275-
} else {
276-
marks.push(mark);
277-
}
278-
}
279-
280174
#[cfg(test)]
281175
mod tests {
282176
use self::TestSC::*;

0 commit comments

Comments
 (0)