2014-02-24 14:47:19 -06:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! Machinery for hygienic macros, as described in the MTWT[1] paper.
|
|
|
|
//!
|
|
|
|
//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
|
|
|
|
//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
|
|
|
|
//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
|
|
|
|
//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
|
|
|
|
|
2014-11-06 02:05:53 -06:00
|
|
|
pub use self::SyntaxContext_::*;
|
|
|
|
|
2014-02-24 14:47:19 -06:00
|
|
|
use ast::{Ident, Mrk, Name, SyntaxContext};
|
|
|
|
|
|
|
|
use std::cell::RefCell;
|
2014-05-29 21:03:06 -05:00
|
|
|
use std::collections::HashMap;
|
2014-10-30 20:25:08 -05:00
|
|
|
use std::collections::hash_map::{Occupied, Vacant};
|
2014-02-24 14:47:19 -06:00
|
|
|
|
2014-06-09 15:12:30 -05:00
|
|
|
/// 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 mark_memo and rename_memo fields are side-tables
|
|
|
|
/// that ensure that adding the same mark to the same context
|
|
|
|
/// gives you back the same context as before. This shouldn't
|
|
|
|
/// change the semantics--everything here is immutable--but
|
|
|
|
/// it should cut down on memory use *a lot*; applying a mark
|
|
|
|
/// to a tree containing 50 identifiers would otherwise generate
|
|
|
|
/// 50 new contexts
|
2014-02-24 14:47:19 -06:00
|
|
|
pub struct SCTable {
|
|
|
|
table: RefCell<Vec<SyntaxContext_>>,
|
|
|
|
mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
|
|
|
|
rename_memo: RefCell<HashMap<(SyntaxContext,Ident,Name),SyntaxContext>>,
|
|
|
|
}
|
|
|
|
|
2014-10-14 20:59:41 -05:00
|
|
|
#[deriving(PartialEq, Encodable, Decodable, Hash, Show)]
|
2014-02-24 14:47:19 -06:00
|
|
|
pub enum SyntaxContext_ {
|
|
|
|
EmptyCtxt,
|
|
|
|
Mark (Mrk,SyntaxContext),
|
2014-06-09 15:12:30 -05:00
|
|
|
/// flattening the name and syntaxcontext into the rename...
|
|
|
|
/// HIDDEN INVARIANTS:
|
|
|
|
/// 1) the first name in a Rename node
|
|
|
|
/// can only be a programmer-supplied name.
|
|
|
|
/// 2) Every Rename node with a given Name in the
|
|
|
|
/// "to" slot must have the same name and context
|
|
|
|
/// in the "from" slot. In essence, they're all
|
|
|
|
/// pointers to a single "rename" event node.
|
2014-02-24 14:47:19 -06:00
|
|
|
Rename (Ident,Name,SyntaxContext),
|
2014-06-09 15:12:30 -05:00
|
|
|
/// actually, IllegalCtxt may not be necessary.
|
2014-02-24 14:47:19 -06:00
|
|
|
IllegalCtxt
|
|
|
|
}
|
|
|
|
|
librustc: Make `Copy` opt-in.
This change makes the compiler no longer infer whether types (structures
and enumerations) implement the `Copy` trait (and thus are implicitly
copyable). Rather, you must implement `Copy` yourself via `impl Copy for
MyType {}`.
A new warning has been added, `missing_copy_implementations`, to warn
you if a non-generic public type has been added that could have
implemented `Copy` but didn't.
For convenience, you may *temporarily* opt out of this behavior by using
`#![feature(opt_out_copy)]`. Note though that this feature gate will never be
accepted and will be removed by the time that 1.0 is released, so you should
transition your code away from using it.
This breaks code like:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
Change this code to:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
impl Copy for Point2D {}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
This is the backwards-incompatible part of #13231.
Part of RFC #3.
[breaking-change]
2014-12-05 19:01:33 -06:00
|
|
|
impl Copy for SyntaxContext_ {}
|
|
|
|
|
2014-07-03 00:38:30 -05:00
|
|
|
/// A list of ident->name renamings
|
|
|
|
pub type RenameList = Vec<(Ident, Name)>;
|
|
|
|
|
2014-02-24 14:47:19 -06:00
|
|
|
/// Extend a syntax context with a given mark
|
2014-07-03 16:38:59 -05:00
|
|
|
pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
|
|
|
|
with_sctable(|table| apply_mark_internal(m, ctxt, table))
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
2014-06-09 15:12:30 -05:00
|
|
|
/// Extend a syntax context with a given mark and sctable (explicit memoization)
|
2014-07-03 16:38:59 -05:00
|
|
|
fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
|
2014-07-03 00:38:30 -05:00
|
|
|
let key = (ctxt, m);
|
2014-09-18 16:05:52 -05:00
|
|
|
* match table.mark_memo.borrow_mut().entry(key) {
|
|
|
|
Vacant(entry) => entry.set(idx_push(&mut *table.table.borrow_mut(), Mark(m, ctxt))),
|
|
|
|
Occupied(entry) => entry.into_mut(),
|
|
|
|
}
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Extend a syntax context with a given rename
|
2014-07-03 16:38:59 -05:00
|
|
|
pub fn apply_rename(id: Ident, to:Name,
|
2014-07-03 00:38:30 -05:00
|
|
|
ctxt: SyntaxContext) -> SyntaxContext {
|
2014-07-03 16:38:59 -05:00
|
|
|
with_sctable(|table| apply_rename_internal(id, to, ctxt, table))
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
2014-06-09 15:12:30 -05:00
|
|
|
/// Extend a syntax context with a given rename and sctable (explicit memoization)
|
2014-07-03 16:38:59 -05:00
|
|
|
fn apply_rename_internal(id: Ident,
|
2014-02-24 14:47:19 -06:00
|
|
|
to: Name,
|
2014-07-03 00:38:30 -05:00
|
|
|
ctxt: SyntaxContext,
|
2014-02-24 14:47:19 -06:00
|
|
|
table: &SCTable) -> SyntaxContext {
|
2014-07-06 03:17:59 -05:00
|
|
|
let key = (ctxt, id, to);
|
2014-02-24 14:47:19 -06:00
|
|
|
|
2014-09-18 16:05:52 -05:00
|
|
|
* match table.rename_memo.borrow_mut().entry(key) {
|
|
|
|
Vacant(entry) => entry.set(idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt))),
|
|
|
|
Occupied(entry) => entry.into_mut(),
|
|
|
|
}
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
2014-07-03 00:38:30 -05:00
|
|
|
/// Apply a list of renamings to a context
|
|
|
|
// if these rename lists get long, it would make sense
|
|
|
|
// to consider memoizing this fold. This may come up
|
|
|
|
// when we add hygiene to item names.
|
2014-07-03 16:38:59 -05:00
|
|
|
pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext {
|
2014-07-03 00:38:30 -05:00
|
|
|
renames.iter().fold(ctxt, |ctxt, &(from, to)| {
|
2014-07-03 16:38:59 -05:00
|
|
|
apply_rename(from, to, ctxt)
|
|
|
|
})
|
2014-07-03 00:38:30 -05:00
|
|
|
}
|
|
|
|
|
2014-02-24 14:47:19 -06:00
|
|
|
/// Fetch the SCTable from TLS, create one if it doesn't yet exist.
|
2014-12-08 12:28:32 -06:00
|
|
|
pub fn with_sctable<T, F>(op: F) -> T where
|
|
|
|
F: FnOnce(&SCTable) -> T,
|
|
|
|
{
|
2014-11-14 11:18:10 -06:00
|
|
|
thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal());
|
2014-12-08 12:28:32 -06:00
|
|
|
SCTABLE_KEY.with(move |slot| op(slot))
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make a fresh syntax context table with EmptyCtxt in slot zero
|
|
|
|
// and IllegalCtxt in slot one.
|
|
|
|
fn new_sctable_internal() -> SCTable {
|
|
|
|
SCTable {
|
|
|
|
table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
|
|
|
|
mark_memo: RefCell::new(HashMap::new()),
|
|
|
|
rename_memo: RefCell::new(HashMap::new()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Print out an SCTable for debugging
|
|
|
|
pub fn display_sctable(table: &SCTable) {
|
|
|
|
error!("SC table:");
|
2014-03-20 17:05:37 -05:00
|
|
|
for (idx,val) in table.table.borrow().iter().enumerate() {
|
2014-11-17 13:29:38 -06:00
|
|
|
error!("{:4} : {}",idx,val);
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:18:58 -06:00
|
|
|
/// Clear the tables from TLD to reclaim memory.
|
|
|
|
pub fn clear_tables() {
|
|
|
|
with_sctable(|table| {
|
2014-03-20 17:05:37 -05:00
|
|
|
*table.table.borrow_mut() = Vec::new();
|
|
|
|
*table.mark_memo.borrow_mut() = HashMap::new();
|
|
|
|
*table.rename_memo.borrow_mut() = HashMap::new();
|
2014-03-08 16:18:58 -06:00
|
|
|
});
|
|
|
|
with_resolve_table_mut(|table| *table = HashMap::new());
|
|
|
|
}
|
2014-02-24 14:47:19 -06:00
|
|
|
|
2014-11-28 22:56:09 -06:00
|
|
|
/// Reset the tables to their initial state
|
|
|
|
pub fn reset_tables() {
|
|
|
|
with_sctable(|table| {
|
|
|
|
*table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt);
|
|
|
|
*table.mark_memo.borrow_mut() = HashMap::new();
|
|
|
|
*table.rename_memo.borrow_mut() = HashMap::new();
|
|
|
|
});
|
|
|
|
with_resolve_table_mut(|table| *table = HashMap::new());
|
|
|
|
}
|
|
|
|
|
2014-06-09 15:12:30 -05:00
|
|
|
/// Add a value to the end of a vec, return its index
|
2014-07-06 03:17:59 -05:00
|
|
|
fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
|
2014-02-24 14:47:19 -06:00
|
|
|
vec.push(val);
|
|
|
|
(vec.len() - 1) as u32
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resolve a syntax object to a name, per MTWT.
|
|
|
|
pub fn resolve(id: Ident) -> Name {
|
|
|
|
with_sctable(|sctable| {
|
|
|
|
with_resolve_table_mut(|resolve_table| {
|
|
|
|
resolve_internal(id, sctable, resolve_table)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2014-12-08 12:28:32 -06:00
|
|
|
fn with_resolve_table_mut<T, F>(op: F) -> T where
|
|
|
|
F: FnOnce(&mut ResolveTable) -> T,
|
|
|
|
{
|
2014-11-14 16:20:57 -06:00
|
|
|
thread_local!(static RESOLVE_TABLE_KEY: RefCell<ResolveTable> = {
|
|
|
|
RefCell::new(HashMap::new())
|
2014-11-14 11:18:10 -06:00
|
|
|
});
|
2014-11-14 16:20:57 -06:00
|
|
|
|
2014-12-08 12:28:32 -06:00
|
|
|
RESOLVE_TABLE_KEY.with(move |slot| op(&mut *slot.borrow_mut()))
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
2014-06-09 15:12:30 -05:00
|
|
|
/// Resolve a syntax object to a name, per MTWT.
|
|
|
|
/// adding memoization to resolve 500+ seconds in resolve for librustc (!)
|
2014-02-24 14:47:19 -06:00
|
|
|
fn resolve_internal(id: Ident,
|
|
|
|
table: &SCTable,
|
|
|
|
resolve_table: &mut ResolveTable) -> Name {
|
|
|
|
let key = (id.name, id.ctxt);
|
|
|
|
|
2014-11-06 11:25:16 -06:00
|
|
|
match resolve_table.get(&key) {
|
2014-02-24 14:47:19 -06:00
|
|
|
Some(&name) => return name,
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
let resolved = {
|
2014-10-15 01:05:01 -05:00
|
|
|
let result = (*table.table.borrow())[id.ctxt as uint];
|
2014-02-24 14:47:19 -06:00
|
|
|
match result {
|
|
|
|
EmptyCtxt => id.name,
|
|
|
|
// ignore marks here:
|
|
|
|
Mark(_,subctxt) =>
|
|
|
|
resolve_internal(Ident{name:id.name, ctxt: subctxt},
|
|
|
|
table, resolve_table),
|
|
|
|
// do the rename if necessary:
|
|
|
|
Rename(Ident{name, ctxt}, toname, subctxt) => {
|
|
|
|
let resolvedfrom =
|
|
|
|
resolve_internal(Ident{name:name, ctxt:ctxt},
|
|
|
|
table, resolve_table);
|
|
|
|
let resolvedthis =
|
|
|
|
resolve_internal(Ident{name:id.name, ctxt:subctxt},
|
|
|
|
table, resolve_table);
|
|
|
|
if (resolvedthis == resolvedfrom)
|
|
|
|
&& (marksof_internal(ctxt, resolvedthis, table)
|
|
|
|
== marksof_internal(subctxt, resolvedthis, table)) {
|
|
|
|
toname
|
|
|
|
} else {
|
|
|
|
resolvedthis
|
|
|
|
}
|
|
|
|
}
|
2014-10-09 14:17:22 -05:00
|
|
|
IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
resolve_table.insert(key, resolved);
|
|
|
|
resolved
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute the marks associated with a syntax context.
|
|
|
|
pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
|
|
|
|
with_sctable(|table| marksof_internal(ctxt, stopname, table))
|
|
|
|
}
|
|
|
|
|
|
|
|
// the internal function for computing marks
|
|
|
|
// it's not clear to me whether it's better to use a [] mutable
|
|
|
|
// vector or a cons-list for this.
|
|
|
|
fn marksof_internal(ctxt: SyntaxContext,
|
|
|
|
stopname: Name,
|
|
|
|
table: &SCTable) -> Vec<Mrk> {
|
|
|
|
let mut result = Vec::new();
|
|
|
|
let mut loopvar = ctxt;
|
|
|
|
loop {
|
2014-10-15 01:05:01 -05:00
|
|
|
let table_entry = (*table.table.borrow())[loopvar as uint];
|
2014-02-24 14:47:19 -06:00
|
|
|
match table_entry {
|
|
|
|
EmptyCtxt => {
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
Mark(mark, tl) => {
|
2014-05-25 18:27:36 -05:00
|
|
|
xor_push(&mut result, mark);
|
2014-02-24 14:47:19 -06:00
|
|
|
loopvar = tl;
|
|
|
|
},
|
|
|
|
Rename(_,name,tl) => {
|
|
|
|
// see MTWT for details on the purpose of the stopname.
|
|
|
|
// short version: it prevents duplication of effort.
|
|
|
|
if name == stopname {
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
loopvar = tl;
|
|
|
|
}
|
|
|
|
}
|
2014-10-09 14:17:22 -05:00
|
|
|
IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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| {
|
2014-10-15 01:05:01 -05:00
|
|
|
match (*sctable.table.borrow())[ctxt as uint] {
|
2014-02-24 14:47:19 -06:00
|
|
|
Mark(mrk, _) => mrk,
|
2014-10-09 14:17:22 -05:00
|
|
|
_ => panic!("can't retrieve outer mark when outside is not a mark")
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-06-09 15:12:30 -05:00
|
|
|
/// Push a name... unless it matches the one on top, in which
|
|
|
|
/// case pop and discard (so two of the same marks cancel)
|
2014-05-25 18:27:36 -05:00
|
|
|
fn xor_push(marks: &mut Vec<Mrk>, mark: Mrk) {
|
2014-02-24 14:47:19 -06:00
|
|
|
if (marks.len() > 0) && (*marks.last().unwrap() == mark) {
|
|
|
|
marks.pop().unwrap();
|
|
|
|
} else {
|
|
|
|
marks.push(mark);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2014-11-06 02:05:53 -06:00
|
|
|
use self::TestSC::*;
|
2014-07-03 00:38:30 -05:00
|
|
|
use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
|
2014-07-03 16:38:59 -05:00
|
|
|
use super::{resolve, xor_push, apply_mark_internal, new_sctable_internal};
|
|
|
|
use super::{apply_rename_internal, apply_renames, marksof_internal, resolve_internal};
|
2014-02-24 14:47:19 -06:00
|
|
|
use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
|
2014-05-29 21:03:06 -05:00
|
|
|
use std::collections::HashMap;
|
2014-02-24 14:47:19 -06:00
|
|
|
|
2014-05-25 18:27:36 -05:00
|
|
|
#[test]
|
|
|
|
fn xorpush_test () {
|
2014-02-24 14:47:19 -06:00
|
|
|
let mut s = Vec::new();
|
2014-05-25 18:27:36 -05:00
|
|
|
xor_push(&mut s, 14);
|
2014-02-24 14:47:19 -06:00
|
|
|
assert_eq!(s.clone(), vec!(14));
|
2014-05-25 18:27:36 -05:00
|
|
|
xor_push(&mut s, 14);
|
2014-02-24 14:47:19 -06:00
|
|
|
assert_eq!(s.clone(), Vec::new());
|
2014-05-25 18:27:36 -05:00
|
|
|
xor_push(&mut s, 14);
|
2014-02-24 14:47:19 -06:00
|
|
|
assert_eq!(s.clone(), vec!(14));
|
2014-05-25 18:27:36 -05:00
|
|
|
xor_push(&mut s, 15);
|
2014-02-24 14:47:19 -06:00
|
|
|
assert_eq!(s.clone(), vec!(14, 15));
|
2014-05-25 18:27:36 -05:00
|
|
|
xor_push(&mut s, 16);
|
2014-02-24 14:47:19 -06:00
|
|
|
assert_eq!(s.clone(), vec!(14, 15, 16));
|
2014-05-25 18:27:36 -05:00
|
|
|
xor_push(&mut s, 16);
|
2014-02-24 14:47:19 -06:00
|
|
|
assert_eq!(s.clone(), vec!(14, 15));
|
2014-05-25 18:27:36 -05:00
|
|
|
xor_push(&mut s, 15);
|
2014-02-24 14:47:19 -06:00
|
|
|
assert_eq!(s.clone(), vec!(14));
|
|
|
|
}
|
|
|
|
|
2014-07-09 00:28:52 -05:00
|
|
|
fn id(n: u32, s: SyntaxContext) -> Ident {
|
|
|
|
Ident {name: Name(n), ctxt: s}
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// because of the SCTable, I now need a tidy way of
|
|
|
|
// creating syntax objects. Sigh.
|
2014-05-29 19:45:07 -05:00
|
|
|
#[deriving(Clone, PartialEq, Show)]
|
2014-02-24 14:47:19 -06:00
|
|
|
enum TestSC {
|
|
|
|
M(Mrk),
|
|
|
|
R(Ident,Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// unfold a vector of TestSC values into a SCTable,
|
|
|
|
// returning the resulting index
|
|
|
|
fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
|
|
|
|
-> SyntaxContext {
|
2014-03-16 18:04:29 -05:00
|
|
|
tscs.iter().rev().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
|
2014-02-24 14:47:19 -06:00
|
|
|
{match *tsc {
|
2014-07-03 16:38:59 -05:00
|
|
|
M(mrk) => apply_mark_internal(mrk,tail,table),
|
|
|
|
R(ident,name) => apply_rename_internal(ident,name,tail,table)}})
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// gather a SyntaxContext back into a vector of TestSCs
|
|
|
|
fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
|
|
|
|
let mut result = Vec::new();
|
|
|
|
loop {
|
|
|
|
let table = table.table.borrow();
|
2014-10-15 01:05:01 -05:00
|
|
|
match (*table)[sc as uint] {
|
2014-02-24 14:47:19 -06:00
|
|
|
EmptyCtxt => {return result;},
|
|
|
|
Mark(mrk,tail) => {
|
|
|
|
result.push(M(mrk));
|
|
|
|
sc = tail;
|
|
|
|
continue;
|
|
|
|
},
|
|
|
|
Rename(id,name,tail) => {
|
|
|
|
result.push(R(id,name));
|
|
|
|
sc = tail;
|
|
|
|
continue;
|
|
|
|
}
|
2014-10-09 14:17:22 -05:00
|
|
|
IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-25 18:27:36 -05:00
|
|
|
#[test]
|
|
|
|
fn test_unfold_refold(){
|
2014-02-24 14:47:19 -06:00
|
|
|
let mut t = new_sctable_internal();
|
|
|
|
|
2014-07-09 00:28:52 -05:00
|
|
|
let test_sc = vec!(M(3),R(id(101,0),Name(14)),M(9));
|
2014-02-24 14:47:19 -06:00
|
|
|
assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),4);
|
|
|
|
{
|
|
|
|
let table = t.table.borrow();
|
2014-10-15 01:05:01 -05:00
|
|
|
assert!((*table)[2] == Mark(9,0));
|
|
|
|
assert!((*table)[3] == Rename(id(101,0),Name(14),2));
|
|
|
|
assert!((*table)[4] == Mark(3,3));
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
assert_eq!(refold_test_sc(4,&t),test_sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
// extend a syntax context with a sequence of marks given
|
|
|
|
// in a vector. v[0] will be the outermost mark.
|
|
|
|
fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
|
|
|
|
-> SyntaxContext {
|
2014-03-16 18:04:29 -05:00
|
|
|
mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
|
2014-07-03 16:38:59 -05:00
|
|
|
{apply_mark_internal(*mrk,tail,table)})
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test] fn unfold_marks_test() {
|
|
|
|
let mut t = new_sctable_internal();
|
|
|
|
|
|
|
|
assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),3);
|
|
|
|
{
|
|
|
|
let table = t.table.borrow();
|
2014-10-15 01:05:01 -05:00
|
|
|
assert!((*table)[2] == Mark(7,0));
|
|
|
|
assert!((*table)[3] == Mark(3,2));
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-25 18:27:36 -05:00
|
|
|
#[test]
|
|
|
|
fn test_marksof () {
|
2014-07-09 00:28:52 -05:00
|
|
|
let stopname = Name(242);
|
|
|
|
let name1 = Name(243);
|
2014-02-24 14:47:19 -06:00
|
|
|
let mut t = new_sctable_internal();
|
|
|
|
assert_eq!(marksof_internal (EMPTY_CTXT,stopname,&t),Vec::new());
|
|
|
|
// FIXME #5074: ANF'd to dodge nested calls
|
|
|
|
{ let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
|
|
|
|
assert_eq! (marksof_internal (ans,stopname,&t),vec!(4,98));}
|
|
|
|
// does xoring work?
|
|
|
|
{ let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
|
|
|
|
assert_eq! (marksof_internal (ans,stopname,&t), vec!(16));}
|
|
|
|
// does nested xoring work?
|
|
|
|
{ let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
|
|
|
|
assert_eq! (marksof_internal (ans, stopname,&t), vec!(16));}
|
|
|
|
// rename where stop doesn't match:
|
|
|
|
{ let chain = vec!(M(9),
|
2014-07-09 00:28:52 -05:00
|
|
|
R(id(name1.uint() as u32,
|
2014-07-03 16:38:59 -05:00
|
|
|
apply_mark_internal (4, EMPTY_CTXT,&mut t)),
|
2014-07-09 00:28:52 -05:00
|
|
|
Name(100101102)),
|
2014-02-24 14:47:19 -06:00
|
|
|
M(14));
|
|
|
|
let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
|
|
|
|
assert_eq! (marksof_internal (ans, stopname, &t), vec!(9,14));}
|
|
|
|
// rename where stop does match
|
2014-07-03 16:38:59 -05:00
|
|
|
{ let name1sc = apply_mark_internal(4, EMPTY_CTXT, &mut t);
|
2014-02-24 14:47:19 -06:00
|
|
|
let chain = vec!(M(9),
|
2014-07-09 00:28:52 -05:00
|
|
|
R(id(name1.uint() as u32, name1sc),
|
2014-02-24 14:47:19 -06:00
|
|
|
stopname),
|
|
|
|
M(14));
|
|
|
|
let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
|
|
|
|
assert_eq! (marksof_internal (ans, stopname, &t), vec!(9)); }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-25 18:27:36 -05:00
|
|
|
#[test]
|
|
|
|
fn resolve_tests () {
|
2014-02-24 14:47:19 -06:00
|
|
|
let a = 40;
|
|
|
|
let mut t = new_sctable_internal();
|
|
|
|
let mut rt = HashMap::new();
|
|
|
|
// - ctxt is MT
|
2014-07-09 00:28:52 -05:00
|
|
|
assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),Name(a));
|
2014-02-24 14:47:19 -06:00
|
|
|
// - simple ignored marks
|
|
|
|
{ let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
|
2014-07-09 00:28:52 -05:00
|
|
|
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(a));}
|
2014-02-24 14:47:19 -06:00
|
|
|
// - orthogonal rename where names don't match
|
2014-07-09 00:28:52 -05:00
|
|
|
{ let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),Name(51)),M(12)),EMPTY_CTXT,&mut t);
|
|
|
|
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(a));}
|
2014-02-24 14:47:19 -06:00
|
|
|
// - rename where names do match, but marks don't
|
2014-07-03 16:38:59 -05:00
|
|
|
{ let sc1 = apply_mark_internal(1,EMPTY_CTXT,&mut t);
|
2014-07-09 00:28:52 -05:00
|
|
|
let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50)),
|
2014-02-24 14:47:19 -06:00
|
|
|
M(1),
|
|
|
|
M(2)),
|
|
|
|
EMPTY_CTXT,&mut t);
|
2014-07-09 00:28:52 -05:00
|
|
|
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(a));}
|
2014-02-24 14:47:19 -06:00
|
|
|
// - rename where names and marks match
|
|
|
|
{ let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
|
2014-07-09 00:28:52 -05:00
|
|
|
let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50)),M(1),M(2)),EMPTY_CTXT,&mut t);
|
|
|
|
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(50)); }
|
2014-02-24 14:47:19 -06:00
|
|
|
// - rename where names and marks match by literal sharing
|
|
|
|
{ let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
|
2014-07-09 00:28:52 -05:00
|
|
|
let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50))),sc1,&mut t);
|
|
|
|
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(50)); }
|
2014-02-24 14:47:19 -06:00
|
|
|
// - two renames of the same var.. can only happen if you use
|
|
|
|
// local-expand to prevent the inner binding from being renamed
|
|
|
|
// during the rename-pass caused by the first:
|
|
|
|
println!("about to run bad test");
|
2014-07-09 00:28:52 -05:00
|
|
|
{ let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),Name(50)),
|
|
|
|
R(id(a,EMPTY_CTXT),Name(51))),
|
2014-02-24 14:47:19 -06:00
|
|
|
EMPTY_CTXT,&mut t);
|
2014-07-09 00:28:52 -05:00
|
|
|
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(51)); }
|
2014-02-24 14:47:19 -06:00
|
|
|
// the simplest double-rename:
|
2014-07-09 00:28:52 -05:00
|
|
|
{ let a_to_a50 = apply_rename_internal(id(a,EMPTY_CTXT),Name(50),EMPTY_CTXT,&mut t);
|
|
|
|
let a50_to_a51 = apply_rename_internal(id(a,a_to_a50),Name(51),a_to_a50,&mut t);
|
|
|
|
assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),Name(51));
|
2014-02-24 14:47:19 -06:00
|
|
|
// mark on the outside doesn't stop rename:
|
2014-07-03 16:38:59 -05:00
|
|
|
let sc = apply_mark_internal(9,a50_to_a51,&mut t);
|
2014-07-09 00:28:52 -05:00
|
|
|
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(51));
|
2014-02-24 14:47:19 -06:00
|
|
|
// but mark on the inside does:
|
2014-07-09 00:28:52 -05:00
|
|
|
let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),Name(51)),
|
2014-02-24 14:47:19 -06:00
|
|
|
M(9)),
|
|
|
|
a_to_a50,
|
|
|
|
&mut t);
|
2014-07-09 00:28:52 -05:00
|
|
|
assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),Name(50));}
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
2014-05-25 18:27:36 -05:00
|
|
|
#[test]
|
|
|
|
fn mtwt_resolve_test(){
|
2014-02-24 14:47:19 -06:00
|
|
|
let a = 40;
|
2014-07-09 00:28:52 -05:00
|
|
|
assert_eq!(resolve(id(a,EMPTY_CTXT)),Name(a));
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-25 18:27:36 -05:00
|
|
|
#[test]
|
|
|
|
fn hashing_tests () {
|
2014-02-24 14:47:19 -06:00
|
|
|
let mut t = new_sctable_internal();
|
2014-07-03 16:38:59 -05:00
|
|
|
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),2);
|
|
|
|
assert_eq!(apply_mark_internal(13,EMPTY_CTXT,&mut t),3);
|
2014-02-24 14:47:19 -06:00
|
|
|
// using the same one again should result in the same index:
|
2014-07-03 16:38:59 -05:00
|
|
|
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),2);
|
2014-02-24 14:47:19 -06:00
|
|
|
// I'm assuming that the rename table will behave the same....
|
|
|
|
}
|
|
|
|
|
2014-05-25 18:27:36 -05:00
|
|
|
#[test]
|
|
|
|
fn resolve_table_hashing_tests() {
|
2014-02-24 14:47:19 -06:00
|
|
|
let mut t = new_sctable_internal();
|
|
|
|
let mut rt = HashMap::new();
|
|
|
|
assert_eq!(rt.len(),0);
|
|
|
|
resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
|
|
|
|
assert_eq!(rt.len(),1);
|
|
|
|
resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
|
|
|
|
assert_eq!(rt.len(),2);
|
|
|
|
resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
|
|
|
|
assert_eq!(rt.len(),2);
|
|
|
|
}
|
2014-07-03 00:38:30 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn new_resolves_test() {
|
2014-07-09 00:28:52 -05:00
|
|
|
let renames = vec!((Ident{name:Name(23),ctxt:EMPTY_CTXT},Name(24)),
|
|
|
|
(Ident{name:Name(29),ctxt:EMPTY_CTXT},Name(29)));
|
2014-07-03 16:38:59 -05:00
|
|
|
let new_ctxt1 = apply_renames(&renames,EMPTY_CTXT);
|
2014-07-09 00:28:52 -05:00
|
|
|
assert_eq!(resolve(Ident{name:Name(23),ctxt:new_ctxt1}),Name(24));
|
|
|
|
assert_eq!(resolve(Ident{name:Name(29),ctxt:new_ctxt1}),Name(29));
|
2014-07-03 00:38:30 -05:00
|
|
|
}
|
2014-02-24 14:47:19 -06:00
|
|
|
}
|