Skip to content

Commit fc4f304

Browse files
committed
hygiene infrastructure.
- added a hash table to memoize rename and mark operations. - added rename, mark, and resolve fold fns
1 parent b621820 commit fc4f304

File tree

5 files changed

+194
-110
lines changed

5 files changed

+194
-110
lines changed

src/libsyntax/ast.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use core::option::{None, Option, Some};
1919
use core::to_bytes;
2020
use core::to_bytes::IterBytes;
2121
use core::to_str::ToStr;
22+
use core::hashmap::HashMap;
2223
use std::serialize::{Encodable, Decodable, Encoder, Decoder};
2324

2425

@@ -38,14 +39,20 @@ pub struct ident { repr: Name, ctxt: SyntaxContext }
3839
// that's causing unreleased memory to cause core dumps
3940
// and also perhaps to save some work in destructor checks.
4041
// the special uint '0' will be used to indicate an empty
41-
// syntax context
42+
// syntax context.
4243

4344
// this uint is a reference to a table stored in thread-local
4445
// storage.
4546
pub type SyntaxContext = uint;
4647

47-
pub type SCTable = ~[SyntaxContext_];
48+
pub struct SCTable {
49+
table : ~[SyntaxContext_],
50+
mark_memo : HashMap<(SyntaxContext,Mrk),SyntaxContext>,
51+
rename_memo : HashMap<(SyntaxContext,ident,Name),SyntaxContext>
52+
}
53+
// NB: these must be placed in any SCTable...
4854
pub static empty_ctxt : uint = 0;
55+
pub static illegal_ctxt : uint = 1;
4956

5057
#[deriving(Eq, Encodable, Decodable)]
5158
pub enum SyntaxContext_ {
@@ -59,7 +66,8 @@ pub enum SyntaxContext_ {
5966
// "to" slot must have the same name and context
6067
// in the "from" slot. In essence, they're all
6168
// pointers to a single "rename" event node.
62-
Rename (ident,Name,SyntaxContext)
69+
Rename (ident,Name,SyntaxContext),
70+
IllegalCtxt()
6371
}
6472

6573
// a name represents an identifier

src/libsyntax/ast_util.rs

Lines changed: 93 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use codemap::{span, spanned};
1515
use parse::token;
1616
use visit;
1717
use opt_vec;
18+
use core::hashmap::HashMap;
1819

1920
use core::to_bytes;
2021

@@ -577,22 +578,61 @@ pub enum Privacy {
577578
// HYGIENE FUNCTIONS
578579

579580
/// Construct an identifier with the given repr and an empty context:
580-
pub fn mk_ident(repr: uint) -> ident { ident {repr: repr, ctxt: 0}}
581+
pub fn new_ident(repr: uint) -> ident { ident {repr: repr, ctxt: 0}}
581582

582583
/// Extend a syntax context with a given mark
583-
pub fn mk_mark (m:Mrk,ctxt:SyntaxContext,table:&mut SCTable)
584+
pub fn new_mark (m:Mrk, tail:SyntaxContext,table:&mut SCTable)
584585
-> SyntaxContext {
585-
idx_push(table,Mark(m,ctxt))
586+
let key = (tail,m);
587+
// FIXME #5074 : can't use more natural style because we're missing
588+
// flow-sensitivity. Results in two lookups on a hash table hit.
589+
// also applies to new_rename, below.
590+
// let try_lookup = table.mark_memo.find(&key);
591+
match table.mark_memo.contains_key(&key) {
592+
false => {
593+
let new_idx = idx_push(&mut table.table,Mark(m,tail));
594+
table.mark_memo.insert(key,new_idx);
595+
new_idx
596+
}
597+
true => {
598+
match table.mark_memo.find(&key) {
599+
None => fail!(~"internal error: key disappeared 2013042901"),
600+
Some(idxptr) => {*idxptr}
601+
}
602+
}
603+
}
586604
}
587605
588606
/// Extend a syntax context with a given rename
589-
pub fn mk_rename (id:ident, to:Name, tail:SyntaxContext, table: &mut SCTable)
607+
pub fn new_rename (id:ident, to:Name, tail:SyntaxContext, table: &mut SCTable)
590608
-> SyntaxContext {
591-
idx_push(table,Rename(id,to,tail))
609+
let key = (tail,id,to);
610+
// FIXME #5074
611+
//let try_lookup = table.rename_memo.find(&key);
612+
match table.rename_memo.contains_key(&key) {
613+
false => {
614+
let new_idx = idx_push(&mut table.table,Rename(id,to,tail));
615+
table.rename_memo.insert(key,new_idx);
616+
new_idx
617+
}
618+
true => {
619+
match table.rename_memo.find(&key) {
620+
None => fail!(~"internal error: key disappeared 2013042902"),
621+
Some(idxptr) => {*idxptr}
622+
}
623+
}
624+
}
592625
}
593626
594627
/// Make a fresh syntax context table with EmptyCtxt in slot zero
595-
pub fn mk_sctable() -> SCTable { ~[EmptyCtxt] }
628+
/// and IllegalCtxt in slot one.
629+
pub fn new_sctable() -> SCTable {
630+
SCTable {
631+
table: ~[EmptyCtxt,IllegalCtxt],
632+
mark_memo: HashMap::new(),
633+
rename_memo: HashMap::new()
634+
}
635+
}
596636
597637
/// Add a value to the end of a vec, return its index
598638
fn idx_push<T>(vec: &mut ~[T], val: T) -> uint {
@@ -601,8 +641,8 @@ fn idx_push<T>(vec: &mut ~[T], val: T) -> uint {
601641
}
602642
603643
/// Resolve a syntax object to a name, per MTWT.
604-
pub fn resolve (id : ident, table : &SCTable) -> Name {
605-
match table[id.ctxt] {
644+
pub fn resolve (id : ident, table : &mut SCTable) -> Name {
645+
match table.table[id.ctxt] {
606646
EmptyCtxt => id.repr,
607647
// ignore marks here:
608648
Mark(_,subctxt) => resolve (ident{repr:id.repr, ctxt: subctxt},table),
@@ -619,6 +659,7 @@ pub fn resolve (id : ident, table : &SCTable) -> Name {
619659
resolvedthis
620660
}
621661
}
662+
IllegalCtxt() => fail!(~"expected resolvable context, got IllegalCtxt")
622663
}
623664
}
624665
@@ -629,7 +670,7 @@ pub fn marksof(ctxt: SyntaxContext, stopname: Name, table: &SCTable) -> ~[Mrk] {
629670
let mut result = ~[];
630671
let mut loopvar = ctxt;
631672
loop {
632-
match table[loopvar] {
673+
match table.table[loopvar] {
633674
EmptyCtxt => {return result;},
634675
Mark(mark,tl) => {
635676
xorPush(&mut result,mark);
@@ -644,6 +685,7 @@ pub fn marksof(ctxt: SyntaxContext, stopname: Name, table: &SCTable) -> ~[Mrk] {
644685
loopvar = tl;
645686
}
646687
}
688+
IllegalCtxt => fail!(~"expected resolvable context, got IllegalCtxt")
647689
}
648690
}
649691
}
@@ -713,15 +755,15 @@ mod test {
713755
-> SyntaxContext {
714756
tscs.foldr(tail, |tsc : &TestSC,tail : SyntaxContext|
715757
{match *tsc {
716-
M(mrk) => mk_mark(mrk,tail,table),
717-
R(ident,name) => mk_rename(ident,name,tail,table)}})
758+
M(mrk) => new_mark(mrk,tail,table),
759+
R(ident,name) => new_rename(ident,name,tail,table)}})
718760
}
719761
720762
// gather a SyntaxContext back into a vector of TestSCs
721763
fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> ~[TestSC] {
722764
let mut result = ~[];
723765
loop {
724-
match table[sc] {
766+
match table.table[sc] {
725767
EmptyCtxt => {return result;},
726768
Mark(mrk,tail) => {
727769
result.push(M(mrk));
@@ -733,40 +775,41 @@ mod test {
733775
sc = tail;
734776
loop;
735777
}
778+
IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
736779
}
737780
}
738781
}
739782
740783
#[test] fn test_unfold_refold(){
741-
let mut t = mk_sctable();
784+
let mut t = new_sctable();
742785
743786
let test_sc = ~[M(3),R(id(101,0),14),M(9)];
744-
assert_eq!(unfold_test_sc(copy test_sc,empty_ctxt,&mut t),3);
745-
assert_eq!(t[1],Mark(9,0));
746-
assert_eq!(t[2],Rename(id(101,0),14,1));
747-
assert_eq!(t[3],Mark(3,2));
748-
assert_eq!(refold_test_sc(3,&t),test_sc);
787+
assert_eq!(unfold_test_sc(copy test_sc,empty_ctxt,&mut t),4);
788+
assert_eq!(t.table[2],Mark(9,0));
789+
assert_eq!(t.table[3],Rename(id(101,0),14,2));
790+
assert_eq!(t.table[4],Mark(3,3));
791+
assert_eq!(refold_test_sc(4,&t),test_sc);
749792
}
750793
751794
// extend a syntax context with a sequence of marks given
752795
// in a vector. v[0] will be the outermost mark.
753796
fn unfold_marks(mrks:~[Mrk],tail:SyntaxContext,table: &mut SCTable) -> SyntaxContext {
754797
mrks.foldr(tail, |mrk:&Mrk,tail:SyntaxContext|
755-
{mk_mark(*mrk,tail,table)})
798+
{new_mark(*mrk,tail,table)})
756799
}
757800
758801
#[test] fn unfold_marks_test() {
759-
let mut t = ~[EmptyCtxt];
802+
let mut t = new_sctable();
760803
761-
assert_eq!(unfold_marks(~[3,7],empty_ctxt,&mut t),2);
762-
assert_eq!(t[1],Mark(7,0));
763-
assert_eq!(t[2],Mark(3,1));
804+
assert_eq!(unfold_marks(~[3,7],empty_ctxt,&mut t),3);
805+
assert_eq!(t.table[2],Mark(7,0));
806+
assert_eq!(t.table[3],Mark(3,2));
764807
}
765808
766809
#[test] fn test_marksof () {
767810
let stopname = 242;
768811
let name1 = 243;
769-
let mut t = mk_sctable();
812+
let mut t = new_sctable();
770813
assert_eq!(marksof (empty_ctxt,stopname,&t),~[]);
771814
// FIXME #5074: ANF'd to dodge nested calls
772815
{ let ans = unfold_marks(~[4,98],empty_ctxt,&mut t);
@@ -780,13 +823,13 @@ mod test {
780823
// rename where stop doesn't match:
781824
{ let chain = ~[M(9),
782825
R(id(name1,
783-
mk_mark (4, empty_ctxt,&mut t)),
826+
new_mark (4, empty_ctxt,&mut t)),
784827
100101102),
785828
M(14)];
786829
let ans = unfold_test_sc(chain,empty_ctxt,&mut t);
787830
assert_eq! (marksof (ans, stopname, &t), ~[9,14]);}
788831
// rename where stop does match
789-
{ let name1sc = mk_mark(4, empty_ctxt, &mut t);
832+
{ let name1sc = new_mark(4, empty_ctxt, &mut t);
790833
let chain = ~[M(9),
791834
R(id(name1, name1sc),
792835
stopname),
@@ -798,51 +841,60 @@ mod test {
798841
799842
#[test] fn resolve_tests () {
800843
let a = 40;
801-
let mut t = mk_sctable();
844+
let mut t = new_sctable();
802845
// - ctxt is MT
803-
assert_eq!(resolve(id(a,empty_ctxt),&t),a);
846+
assert_eq!(resolve(id(a,empty_ctxt),&mut t),a);
804847
// - simple ignored marks
805848
{ let sc = unfold_marks(~[1,2,3],empty_ctxt,&mut t);
806-
assert_eq!(resolve(id(a,sc),&t),a);}
849+
assert_eq!(resolve(id(a,sc),&mut t),a);}
807850
// - orthogonal rename where names don't match
808851
{ let sc = unfold_test_sc(~[R(id(50,empty_ctxt),51),M(12)],empty_ctxt,&mut t);
809-
assert_eq!(resolve(id(a,sc),&t),a);}
852+
assert_eq!(resolve(id(a,sc),&mut t),a);}
810853
// - rename where names do match, but marks don't
811-
{ let sc1 = mk_mark(1,empty_ctxt,&mut t);
854+
{ let sc1 = new_mark(1,empty_ctxt,&mut t);
812855
let sc = unfold_test_sc(~[R(id(a,sc1),50),
813856
M(1),
814857
M(2)],
815858
empty_ctxt,&mut t);
816-
assert_eq!(resolve(id(a,sc),&t), a);}
859+
assert_eq!(resolve(id(a,sc),&mut t), a);}
817860
// - rename where names and marks match
818861
{ let sc1 = unfold_test_sc(~[M(1),M(2)],empty_ctxt,&mut t);
819862
let sc = unfold_test_sc(~[R(id(a,sc1),50),M(1),M(2)],empty_ctxt,&mut t);
820-
assert_eq!(resolve(id(a,sc),&t), 50); }
863+
assert_eq!(resolve(id(a,sc),&mut t), 50); }
821864
// - rename where names and marks match by literal sharing
822865
{ let sc1 = unfold_test_sc(~[M(1),M(2)],empty_ctxt,&mut t);
823866
let sc = unfold_test_sc(~[R(id(a,sc1),50)],sc1,&mut t);
824-
assert_eq!(resolve(id(a,sc),&t), 50); }
867+
assert_eq!(resolve(id(a,sc),&mut t), 50); }
825868
// - two renames of the same var.. can only happen if you use
826869
// local-expand to prevent the inner binding from being renamed
827870
// during the rename-pass caused by the first:
828871
io::println("about to run bad test");
829872
{ let sc = unfold_test_sc(~[R(id(a,empty_ctxt),50),
830873
R(id(a,empty_ctxt),51)],
831874
empty_ctxt,&mut t);
832-
assert_eq!(resolve(id(a,sc),&t), 51); }
875+
assert_eq!(resolve(id(a,sc),&mut t), 51); }
833876
// the simplest double-rename:
834-
{ let a_to_a50 = mk_rename(id(a,empty_ctxt),50,empty_ctxt,&mut t);
835-
let a50_to_a51 = mk_rename(id(a,a_to_a50),51,a_to_a50,&mut t);
836-
assert_eq!(resolve(id(a,a50_to_a51),&t),51);
877+
{ let a_to_a50 = new_rename(id(a,empty_ctxt),50,empty_ctxt,&mut t);
878+
let a50_to_a51 = new_rename(id(a,a_to_a50),51,a_to_a50,&mut t);
879+
assert_eq!(resolve(id(a,a50_to_a51),&mut t),51);
837880
// mark on the outside doesn't stop rename:
838-
let sc = mk_mark(9,a50_to_a51,&mut t);
839-
assert_eq!(resolve(id(a,sc),&t),51);
881+
let sc = new_mark(9,a50_to_a51,&mut t);
882+
assert_eq!(resolve(id(a,sc),&mut t),51);
840883
// but mark on the inside does:
841884
let a50_to_a51_b = unfold_test_sc(~[R(id(a,a_to_a50),51),
842885
M(9)],
843886
a_to_a50,
844887
&mut t);
845-
assert_eq!(resolve(id(a,a50_to_a51_b),&t),50);}
888+
assert_eq!(resolve(id(a,a50_to_a51_b),&mut t),50);}
889+
}
890+
891+
#[test] fn hashing_tests () {
892+
let mut t = new_sctable();
893+
assert_eq!(new_mark(12,empty_ctxt,&mut t),2);
894+
assert_eq!(new_mark(13,empty_ctxt,&mut t),3);
895+
// using the same one again should result in the same index:
896+
assert_eq!(new_mark(12,empty_ctxt,&mut t),2);
897+
// I'm assuming that the rename table will behave the same....
846898
}
847899

848900
}

0 commit comments

Comments
 (0)