about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2012-11-28 16:20:41 -0800
committerBrian Anderson <banderson@mozilla.com>2012-11-28 17:09:26 -0800
commit65bd40e300323f1e4806dd84834b8d71bb89c08c (patch)
tree53b9245b13ba9991627a0e23b8378a328af42ad4
parent9b95d511316ae7124046de61d19335fdfaa021cc (diff)
downloadrust-65bd40e300323f1e4806dd84834b8d71bb89c08c.tar.gz
rust-65bd40e300323f1e4806dd84834b8d71bb89c08c.zip
Remove uses of #[merge]
-rw-r--r--src/libcore/core.rc2
-rw-r--r--src/libcore/task.rs1301
-rw-r--r--src/libcore/task/mod.rs1304
-rw-r--r--src/librustc/driver.rs5
-rw-r--r--src/librustc/driver/mod.rs8
-rw-r--r--src/librustc/metadata.rs31
-rw-r--r--src/librustc/metadata/mod.rs33
-rw-r--r--src/librustc/middle/borrowck.rs618
-rw-r--r--src/librustc/middle/borrowck/mod.rs620
-rw-r--r--src/librustc/middle/typeck.rs376
-rw-r--r--src/librustc/middle/typeck/mod.rs377
-rw-r--r--src/librustc/rustc.rc10
-rw-r--r--src/libsyntax/ext/pipes.rs67
-rw-r--r--src/libsyntax/ext/pipes/mod.rs70
-rw-r--r--src/libsyntax/parse.rs196
-rw-r--r--src/libsyntax/parse/mod.rs198
-rw-r--r--src/libsyntax/syntax.rc5
17 files changed, 2614 insertions, 2607 deletions
diff --git a/src/libcore/core.rc b/src/libcore/core.rc
index 4c5a56cc6ff..ccd102e9ca5 100644
--- a/src/libcore/core.rc
+++ b/src/libcore/core.rc
@@ -144,7 +144,7 @@ pub mod send_map;
 
 // Concurrency
 pub mod comm;
-#[merge = "task/mod.rs"]
+#[path = "task/mod.rs"]
 pub mod task;
 pub mod pipes;
 
diff --git a/src/libcore/task.rs b/src/libcore/task.rs
deleted file mode 100644
index a2b5c3229ad..00000000000
--- a/src/libcore/task.rs
+++ /dev/null
@@ -1,1301 +0,0 @@
-// NB: transitionary, de-mode-ing.
-// tjc: Deprecated modes allowed because of function arg issue
-// in task::spawn. Re-forbid after snapshot.
-#[forbid(deprecated_pattern)];
-
-/*!
- * Task management.
- *
- * An executing Rust program consists of a tree of tasks, each with their own
- * stack, and sole ownership of their allocated heap data. Tasks communicate
- * with each other using ports and channels.
- *
- * When a task fails, that failure will propagate to its parent (the task
- * that spawned it) and the parent will fail as well. The reverse is not
- * true: when a parent task fails its children will continue executing. When
- * the root (main) task fails, all tasks fail, and then so does the entire
- * process.
- *
- * Tasks may execute in parallel and are scheduled automatically by the
- * runtime.
- *
- * # Example
- *
- * ~~~
- * do spawn {
- *     log(error, "Hello, World!");
- * }
- * ~~~
- */
-
-use cmp::Eq;
-use result::Result;
-use pipes::{stream, Chan, Port};
-use local_data_priv::{local_get, local_set};
-use util::replace;
-
-use rt::task_id;
-use rt::rust_task;
-
-/// A handle to a task
-pub enum Task {
-    TaskHandle(task_id)
-}
-
-impl Task : cmp::Eq {
-    pure fn eq(&self, other: &Task) -> bool { *(*self) == *(*other) }
-    pure fn ne(&self, other: &Task) -> bool { !(*self).eq(other) }
-}
-
-/**
- * Indicates the manner in which a task exited.
- *
- * A task that completes without failing is considered to exit successfully.
- * Supervised ancestors and linked siblings may yet fail after this task
- * succeeds. Also note that in such a case, it may be nondeterministic whether
- * linked failure or successful exit happen first.
- *
- * If you wish for this result's delivery to block until all linked and/or
- * children tasks complete, recommend using a result future.
- */
-pub enum TaskResult {
-    Success,
-    Failure,
-}
-
-impl TaskResult : Eq {
-    pure fn eq(&self, other: &TaskResult) -> bool {
-        match ((*self), (*other)) {
-            (Success, Success) | (Failure, Failure) => true,
-            (Success, _) | (Failure, _) => false
-        }
-    }
-    pure fn ne(&self, other: &TaskResult) -> bool { !(*self).eq(other) }
-}
-
-/// Scheduler modes
-pub enum SchedMode {
-    /// All tasks run in the same OS thread
-    SingleThreaded,
-    /// Tasks are distributed among available CPUs
-    ThreadPerCore,
-    /// Each task runs in its own OS thread
-    ThreadPerTask,
-    /// Tasks are distributed among a fixed number of OS threads
-    ManualThreads(uint),
-    /**
-     * Tasks are scheduled on the main OS thread
-     *
-     * The main OS thread is the thread used to launch the runtime which,
-     * in most cases, is the process's initial thread as created by the OS.
-     */
-    PlatformThread
-}
-
-impl SchedMode : cmp::Eq {
-    pure fn eq(&self, other: &SchedMode) -> bool {
-        match (*self) {
-            SingleThreaded => {
-                match (*other) {
-                    SingleThreaded => true,
-                    _ => false
-                }
-            }
-            ThreadPerCore => {
-                match (*other) {
-                    ThreadPerCore => true,
-                    _ => false
-                }
-            }
-            ThreadPerTask => {
-                match (*other) {
-                    ThreadPerTask => true,
-                    _ => false
-                }
-            }
-            ManualThreads(e0a) => {
-                match (*other) {
-                    ManualThreads(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            PlatformThread => {
-                match (*other) {
-                    PlatformThread => true,
-                    _ => false
-                }
-            }
-        }
-    }
-    pure fn ne(&self, other: &SchedMode) -> bool {
-        !(*self).eq(other)
-    }
-}
-
-/**
- * Scheduler configuration options
- *
- * # Fields
- *
- * * sched_mode - The operating mode of the scheduler
- *
- * * foreign_stack_size - The size of the foreign stack, in bytes
- *
- *     Rust code runs on Rust-specific stacks. When Rust code calls foreign
- *     code (via functions in foreign modules) it switches to a typical, large
- *     stack appropriate for running code written in languages like C. By
- *     default these foreign stacks have unspecified size, but with this
- *     option their size can be precisely specified.
- */
-pub type SchedOpts = {
-    mode: SchedMode,
-    foreign_stack_size: Option<uint>
-};
-
-/**
- * Task configuration options
- *
- * # Fields
- *
- * * linked - Propagate failure bidirectionally between child and parent.
- *            True by default. If both this and 'supervised' are false, then
- *            either task's failure will not affect the other ("unlinked").
- *
- * * supervised - Propagate failure unidirectionally from parent to child,
- *                but not from child to parent. False by default.
- *
- * * notify_chan - Enable lifecycle notifications on the given channel
- *
- * * sched - Specify the configuration of a new scheduler to create the task
- *           in
- *
- *     By default, every task is created in the same scheduler as its
- *     parent, where it is scheduled cooperatively with all other tasks
- *     in that scheduler. Some specialized applications may want more
- *     control over their scheduling, in which case they can be spawned
- *     into a new scheduler with the specific properties required.
- *
- *     This is of particular importance for libraries which want to call
- *     into foreign code that blocks. Without doing so in a different
- *     scheduler other tasks will be impeded or even blocked indefinitely.
- */
-pub type TaskOpts = {
-    linked: bool,
-    supervised: bool,
-    mut notify_chan: Option<Chan<TaskResult>>,
-    sched: Option<SchedOpts>,
-};
-
-/**
- * The task builder type.
- *
- * Provides detailed control over the properties and behavior of new tasks.
- */
-// NB: Builders are designed to be single-use because they do stateful
-// things that get weird when reusing - e.g. if you create a result future
-// it only applies to a single task, so then you have to maintain Some
-// potentially tricky state to ensure that everything behaves correctly
-// when you try to reuse the builder to spawn a new task. We'll just
-// sidestep that whole issue by making builders uncopyable and making
-// the run function move them in.
-
-// FIXME (#3724): Replace the 'consumed' bit with move mode on self
-pub enum TaskBuilder = {
-    opts: TaskOpts,
-    gen_body: fn@(v: fn~()) -> fn~(),
-    can_not_copy: Option<util::NonCopyable>,
-    mut consumed: bool,
-};
-
-/**
- * Generate the base configuration for spawning a task, off of which more
- * configuration methods can be chained.
- * For example, task().unlinked().spawn is equivalent to spawn_unlinked.
- */
-pub fn task() -> TaskBuilder {
-    TaskBuilder({
-        opts: default_task_opts(),
-        gen_body: |body| move body, // Identity function
-        can_not_copy: None,
-        mut consumed: false,
-    })
-}
-
-#[doc(hidden)] // FIXME #3538
-priv impl TaskBuilder {
-    fn consume() -> TaskBuilder {
-        if self.consumed {
-            fail ~"Cannot copy a task_builder"; // Fake move mode on self
-        }
-        self.consumed = true;
-        let notify_chan = replace(&mut self.opts.notify_chan, None);
-        TaskBuilder({
-            opts: {
-                linked: self.opts.linked,
-                supervised: self.opts.supervised,
-                mut notify_chan: move notify_chan,
-                sched: self.opts.sched
-            },
-            gen_body: self.gen_body,
-            can_not_copy: None,
-            mut consumed: false
-        })
-    }
-}
-
-impl TaskBuilder {
-    /**
-     * Decouple the child task's failure from the parent's. If either fails,
-     * the other will not be killed.
-     */
-    fn unlinked() -> TaskBuilder {
-        let notify_chan = replace(&mut self.opts.notify_chan, None);
-        TaskBuilder({
-            opts: {
-                linked: false,
-                supervised: self.opts.supervised,
-                mut notify_chan: move notify_chan,
-                sched: self.opts.sched
-            },
-            can_not_copy: None,
-            .. *self.consume()
-        })
-    }
-    /**
-     * Unidirectionally link the child task's failure with the parent's. The
-     * child's failure will not kill the parent, but the parent's will kill
-     * the child.
-     */
-    fn supervised() -> TaskBuilder {
-        let notify_chan = replace(&mut self.opts.notify_chan, None);
-        TaskBuilder({
-            opts: {
-                linked: false,
-                supervised: true,
-                mut notify_chan: move notify_chan,
-                sched: self.opts.sched
-            },
-            can_not_copy: None,
-            .. *self.consume()
-        })
-    }
-    /**
-     * Link the child task's and parent task's failures. If either fails, the
-     * other will be killed.
-     */
-    fn linked() -> TaskBuilder {
-        let notify_chan = replace(&mut self.opts.notify_chan, None);
-        TaskBuilder({
-            opts: {
-                linked: true,
-                supervised: false,
-                mut notify_chan: move notify_chan,
-                sched: self.opts.sched
-            },
-            can_not_copy: None,
-            .. *self.consume()
-        })
-    }
-
-    /**
-     * Get a future representing the exit status of the task.
-     *
-     * Taking the value of the future will block until the child task
-     * terminates. The future-receiving callback specified will be called
-     * *before* the task is spawned; as such, do not invoke .get() within the
-     * closure; rather, store it in an outer variable/list for later use.
-     *
-     * Note that the future returning by this function is only useful for
-     * obtaining the value of the next task to be spawning with the
-     * builder. If additional tasks are spawned with the same builder
-     * then a new result future must be obtained prior to spawning each
-     * task.
-     *
-     * # Failure
-     * Fails if a future_result was already set for this task.
-     */
-    fn future_result(blk: fn(v: Port<TaskResult>)) -> TaskBuilder {
-        // FIXME (#3725): Once linked failure and notification are
-        // handled in the library, I can imagine implementing this by just
-        // registering an arbitrary number of task::on_exit handlers and
-        // sending out messages.
-
-        if self.opts.notify_chan.is_some() {
-            fail ~"Can't set multiple future_results for one task!";
-        }
-
-        // Construct the future and give it to the caller.
-        let (notify_pipe_ch, notify_pipe_po) = stream::<TaskResult>();
-
-        blk(move notify_pipe_po);
-
-        // Reconfigure self to use a notify channel.
-        TaskBuilder({
-            opts: {
-                linked: self.opts.linked,
-                supervised: self.opts.supervised,
-                mut notify_chan: Some(move notify_pipe_ch),
-                sched: self.opts.sched
-            },
-            can_not_copy: None,
-            .. *self.consume()
-        })
-    }
-    /// Configure a custom scheduler mode for the task.
-    fn sched_mode(mode: SchedMode) -> TaskBuilder {
-        let notify_chan = replace(&mut self.opts.notify_chan, None);
-        TaskBuilder({
-            opts: {
-                linked: self.opts.linked,
-                supervised: self.opts.supervised,
-                mut notify_chan: move notify_chan,
-                sched: Some({ mode: mode, foreign_stack_size: None})
-            },
-            can_not_copy: None,
-            .. *self.consume()
-        })
-    }
-
-    /**
-     * Add a wrapper to the body of the spawned task.
-     *
-     * Before the task is spawned it is passed through a 'body generator'
-     * function that may perform local setup operations as well as wrap
-     * the task body in remote setup operations. With this the behavior
-     * of tasks can be extended in simple ways.
-     *
-     * This function augments the current body generator with a new body
-     * generator by applying the task body which results from the
-     * existing body generator to the new body generator.
-     */
-    fn add_wrapper(wrapper: fn@(v: fn~()) -> fn~()) -> TaskBuilder {
-        let prev_gen_body = self.gen_body;
-        let notify_chan = replace(&mut self.opts.notify_chan, None);
-        TaskBuilder({
-            opts: {
-                linked: self.opts.linked,
-                supervised: self.opts.supervised,
-                mut notify_chan: move notify_chan,
-                sched: self.opts.sched
-            },
-            // tjc: I think this is the line that gets miscompiled
-            // w/ last-use off, if we leave out the move prev_gen_body?
-            // that makes no sense, though...
-            gen_body: |move prev_gen_body,
-                       body| { wrapper(prev_gen_body(move body)) },
-            can_not_copy: None,
-            .. *self.consume()
-        })
-    }
-
-    /**
-     * Creates and executes a new child task
-     *
-     * Sets up a new task with its own call stack and schedules it to run
-     * the provided unique closure. The task has the properties and behavior
-     * specified by the task_builder.
-     *
-     * # Failure
-     *
-     * When spawning into a new scheduler, the number of threads requested
-     * must be greater than zero.
-     */
-    fn spawn(f: fn~()) {
-        let notify_chan = replace(&mut self.opts.notify_chan, None);
-        let x = self.consume();
-        let opts = {
-            linked: x.opts.linked,
-            supervised: x.opts.supervised,
-            mut notify_chan: move notify_chan,
-            sched: x.opts.sched
-        };
-        spawn::spawn_raw(move opts, x.gen_body(move f));
-    }
-    /// Runs a task, while transfering ownership of one argument to the child.
-    fn spawn_with<A: Send>(arg: A, f: fn~(v: A)) {
-        let arg = ~mut Some(move arg);
-        do self.spawn |move arg, move f| {
-            f(option::swap_unwrap(arg))
-        }
-    }
-
-    /**
-     * Runs a new task while providing a channel from the parent to the child
-     *
-     * Sets up a communication channel from the current task to the new
-     * child task, passes the port to child's body, and returns a channel
-     * linked to the port to the parent.
-     *
-     * This encapsulates some boilerplate handshaking logic that would
-     * otherwise be required to establish communication from the parent
-     * to the child.
-     */
-    fn spawn_listener<A: Send>(f: fn~(comm::Port<A>)) -> comm::Chan<A> {
-        let setup_po = comm::Port();
-        let setup_ch = comm::Chan(&setup_po);
-        do self.spawn |move f| {
-            let po = comm::Port();
-            let ch = comm::Chan(&po);
-            comm::send(setup_ch, ch);
-            f(move po);
-        }
-        comm::recv(setup_po)
-    }
-
-    /**
-     * Runs a new task, setting up communication in both directions
-     */
-    fn spawn_conversation<A: Send, B: Send>
-        (f: fn~(comm::Port<A>, comm::Chan<B>))
-        -> (comm::Port<B>, comm::Chan<A>) {
-        let from_child = comm::Port();
-        let to_parent = comm::Chan(&from_child);
-        let to_child = do self.spawn_listener |move f, from_parent| {
-            f(from_parent, to_parent)
-        };
-        (from_child, to_child)
-    }
-
-    /**
-     * Execute a function in another task and return either the return value
-     * of the function or result::err.
-     *
-     * # Return value
-     *
-     * If the function executed successfully then try returns result::ok
-     * containing the value returned by the function. If the function fails
-     * then try returns result::err containing nil.
-     *
-     * # Failure
-     * Fails if a future_result was already set for this task.
-     */
-    fn try<T: Send>(f: fn~() -> T) -> Result<T,()> {
-        let po = comm::Port();
-        let ch = comm::Chan(&po);
-        let mut result = None;
-
-        let fr_task_builder = self.future_result(|+r| {
-            result = Some(move r);
-        });
-        do fr_task_builder.spawn |move f| {
-            comm::send(ch, f());
-        }
-        match option::unwrap(move result).recv() {
-            Success => result::Ok(comm::recv(po)),
-            Failure => result::Err(())
-        }
-    }
-}
-
-
-/* Task construction */
-
-pub fn default_task_opts() -> TaskOpts {
-    /*!
-     * The default task options
-     *
-     * By default all tasks are supervised by their parent, are spawned
-     * into the same scheduler, and do not post lifecycle notifications.
-     */
-
-    {
-        linked: true,
-        supervised: false,
-        mut notify_chan: None,
-        sched: None
-    }
-}
-
-/* Spawn convenience functions */
-
-pub fn spawn(f: fn~()) {
-    /*!
-     * Creates and executes a new child task
-     *
-     * Sets up a new task with its own call stack and schedules it to run
-     * the provided unique closure.
-     *
-     * This function is equivalent to `task().spawn(f)`.
-     */
-
-    task().spawn(move f)
-}
-
-pub fn spawn_unlinked(f: fn~()) {
-    /*!
-     * Creates a child task unlinked from the current one. If either this
-     * task or the child task fails, the other will not be killed.
-     */
-
-    task().unlinked().spawn(move f)
-}
-
-pub fn spawn_supervised(f: fn~()) {
-    /*!
-     * Creates a child task unlinked from the current one. If either this
-     * task or the child task fails, the other will not be killed.
-     */
-
-    task().supervised().spawn(move f)
-}
-
-pub fn spawn_with<A:Send>(arg: A, f: fn~(v: A)) {
-    /*!
-     * Runs a task, while transfering ownership of one argument to the
-     * child.
-     *
-     * This is useful for transfering ownership of noncopyables to
-     * another task.
-     *
-     * This function is equivalent to `task().spawn_with(arg, f)`.
-     */
-
-    task().spawn_with(move arg, move f)
-}
-
-pub fn spawn_listener<A:Send>(f: fn~(comm::Port<A>)) -> comm::Chan<A> {
-    /*!
-     * Runs a new task while providing a channel from the parent to the child
-     *
-     * This function is equivalent to `task().spawn_listener(f)`.
-     */
-
-    task().spawn_listener(move f)
-}
-
-pub fn spawn_conversation<A: Send, B: Send>
-    (f: fn~(comm::Port<A>, comm::Chan<B>))
-    -> (comm::Port<B>, comm::Chan<A>) {
-    /*!
-     * Runs a new task, setting up communication in both directions
-     *
-     * This function is equivalent to `task().spawn_conversation(f)`.
-     */
-
-    task().spawn_conversation(move f)
-}
-
-pub fn spawn_sched(mode: SchedMode, f: fn~()) {
-    /*!
-     * Creates a new scheduler and executes a task on it
-     *
-     * Tasks subsequently spawned by that task will also execute on
-     * the new scheduler. When there are no more tasks to execute the
-     * scheduler terminates.
-     *
-     * # Failure
-     *
-     * In manual threads mode the number of threads requested must be
-     * greater than zero.
-     */
-
-    task().sched_mode(mode).spawn(move f)
-}
-
-pub fn try<T:Send>(f: fn~() -> T) -> Result<T,()> {
-    /*!
-     * Execute a function in another task and return either the return value
-     * of the function or result::err.
-     *
-     * This is equivalent to task().supervised().try.
-     */
-
-    task().supervised().try(move f)
-}
-
-
-/* Lifecycle functions */
-
-pub fn yield() {
-    //! Yield control to the task scheduler
-
-    let task_ = rt::rust_get_task();
-    let killed = rt::rust_task_yield(task_);
-    if killed && !failing() {
-        fail ~"killed";
-    }
-}
-
-pub fn failing() -> bool {
-    //! True if the running task has failed
-
-    rt::rust_task_is_unwinding(rt::rust_get_task())
-}
-
-pub fn get_task() -> Task {
-    //! Get a handle to the running task
-
-    TaskHandle(rt::get_task_id())
-}
-
-/**
- * Temporarily make the task unkillable
- *
- * # Example
- *
- * ~~~
- * do task::unkillable {
- *     // detach / yield / destroy must all be called together
- *     rustrt::rust_port_detach(po);
- *     // This must not result in the current task being killed
- *     task::yield();
- *     rustrt::rust_port_destroy(po);
- * }
- * ~~~
- */
-pub unsafe fn unkillable<U>(f: fn() -> U) -> U {
-    struct AllowFailure {
-        t: *rust_task,
-        drop { rt::rust_task_allow_kill(self.t); }
-    }
-
-    fn AllowFailure(t: *rust_task) -> AllowFailure{
-        AllowFailure {
-            t: t
-        }
-    }
-
-    let t = rt::rust_get_task();
-    let _allow_failure = AllowFailure(t);
-    rt::rust_task_inhibit_kill(t);
-    f()
-}
-
-/// The inverse of unkillable. Only ever to be used nested in unkillable().
-pub unsafe fn rekillable<U>(f: fn() -> U) -> U {
-    struct DisallowFailure {
-        t: *rust_task,
-        drop { rt::rust_task_inhibit_kill(self.t); }
-    }
-
-    fn DisallowFailure(t: *rust_task) -> DisallowFailure {
-        DisallowFailure {
-            t: t
-        }
-    }
-
-    let t = rt::rust_get_task();
-    let _allow_failure = DisallowFailure(t);
-    rt::rust_task_allow_kill(t);
-    f()
-}
-
-/**
- * A stronger version of unkillable that also inhibits scheduling operations.
- * For use with exclusive ARCs, which use pthread mutexes directly.
- */
-pub unsafe fn atomically<U>(f: fn() -> U) -> U {
-    struct DeferInterrupts {
-        t: *rust_task,
-        drop {
-            rt::rust_task_allow_yield(self.t);
-            rt::rust_task_allow_kill(self.t);
-        }
-    }
-
-    fn DeferInterrupts(t: *rust_task) -> DeferInterrupts {
-        DeferInterrupts {
-            t: t
-        }
-    }
-
-    let t = rt::rust_get_task();
-    let _interrupts = DeferInterrupts(t);
-    rt::rust_task_inhibit_kill(t);
-    rt::rust_task_inhibit_yield(t);
-    f()
-}
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_cant_dup_task_builder() {
-    let b = task().unlinked();
-    do b.spawn { }
-    // FIXME(#3724): For now, this is a -runtime- failure, because we haven't
-    // got move mode on self. When 3724 is fixed, this test should fail to
-    // compile instead, and should go in tests/compile-fail.
-    do b.spawn { } // b should have been consumed by the previous call
-}
-
-// The following 8 tests test the following 2^3 combinations:
-// {un,}linked {un,}supervised failure propagation {up,down}wards.
-
-// !!! These tests are dangerous. If Something is buggy, they will hang, !!!
-// !!! instead of exiting cleanly. This might wedge the buildbots.       !!!
-
-#[test] #[ignore(cfg(windows))]
-fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
-    let po = comm::Port();
-    let ch = comm::Chan(&po);
-    do spawn_unlinked {
-        do spawn_unlinked {
-            // Give middle task a chance to fail-but-not-kill-us.
-            for iter::repeat(16) { task::yield(); }
-            comm::send(ch, ()); // If killed first, grandparent hangs.
-        }
-        fail; // Shouldn't kill either (grand)parent or (grand)child.
-    }
-    comm::recv(po);
-}
-#[test] #[ignore(cfg(windows))]
-fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
-    do spawn_unlinked { fail; }
-}
-#[test] #[ignore(cfg(windows))]
-fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
-    do spawn_supervised { fail; }
-    // Give child a chance to fail-but-not-kill-us.
-    for iter::repeat(16) { task::yield(); }
-}
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_unlinked_sup_fail_down() {
-    do spawn_supervised { loop { task::yield(); } }
-    fail; // Shouldn't leave a child hanging around.
-}
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
-    let po = comm::Port::<()>();
-    let _ch = comm::Chan(&po);
-    // Unidirectional "parenting" shouldn't override bidirectional linked.
-    // We have to cheat with opts - the interface doesn't support them because
-    // they don't make sense (redundant with task().supervised()).
-    let opts = {
-        let mut opts = default_task_opts();
-        opts.linked = true;
-        opts.supervised = true;
-        move opts
-    };
-
-    let b0 = task();
-    let b1 = TaskBuilder({
-        opts: move opts,
-        can_not_copy: None,
-        .. *b0
-    });
-    do b1.spawn { fail; }
-    comm::recv(po); // We should get punted awake
-}
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
-    // We have to cheat with opts - the interface doesn't support them because
-    // they don't make sense (redundant with task().supervised()).
-    let opts = {
-        let mut opts = default_task_opts();
-        opts.linked = true;
-        opts.supervised = true;
-        move opts
-    };
-
-    let b0 = task();
-    let b1 = TaskBuilder({
-        opts: move opts,
-        can_not_copy: None,
-        .. *b0
-    });
-    do b1.spawn { loop { task::yield(); } }
-    fail; // *both* mechanisms would be wrong if this didn't kill the child...
-}
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
-    let po = comm::Port::<()>();
-    let _ch = comm::Chan(&po);
-    // Default options are to spawn linked & unsupervised.
-    do spawn { fail; }
-    comm::recv(po); // We should get punted awake
-}
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
-    // Default options are to spawn linked & unsupervised.
-    do spawn { loop { task::yield(); } }
-    fail;
-}
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
-    // Make sure the above test is the same as this one.
-    do task().linked().spawn { loop { task::yield(); } }
-    fail;
-}
-
-// A couple bonus linked failure tests - testing for failure propagation even
-// when the middle task exits successfully early before kill signals are sent.
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_failure_propagate_grandchild() {
-    // Middle task exits; does grandparent's failure propagate across the gap?
-    do spawn_supervised {
-        do spawn_supervised {
-            loop { task::yield(); }
-        }
-    }
-    for iter::repeat(16) { task::yield(); }
-    fail;
-}
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_failure_propagate_secondborn() {
-    // First-born child exits; does parent's failure propagate to sibling?
-    do spawn_supervised {
-        do spawn { // linked
-            loop { task::yield(); }
-        }
-    }
-    for iter::repeat(16) { task::yield(); }
-    fail;
-}
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_failure_propagate_nephew_or_niece() {
-    // Our sibling exits; does our failure propagate to sibling's child?
-    do spawn { // linked
-        do spawn_supervised {
-            loop { task::yield(); }
-        }
-    }
-    for iter::repeat(16) { task::yield(); }
-    fail;
-}
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_spawn_linked_sup_propagate_sibling() {
-    // Middle sibling exits - does eldest's failure propagate to youngest?
-    do spawn { // linked
-        do spawn { // linked
-            loop { task::yield(); }
-        }
-    }
-    for iter::repeat(16) { task::yield(); }
-    fail;
-}
-
-#[test]
-fn test_run_basic() {
-    let po = comm::Port();
-    let ch = comm::Chan(&po);
-    do task().spawn {
-        comm::send(ch, ());
-    }
-    comm::recv(po);
-}
-
-#[test]
-fn test_add_wrapper() {
-    let po = comm::Port();
-    let ch = comm::Chan(&po);
-    let b0 = task();
-    let b1 = do b0.add_wrapper |body| {
-        fn~(move body) {
-            body();
-            comm::send(ch, ());
-        }
-    };
-    do b1.spawn { }
-    comm::recv(po);
-}
-
-#[test]
-#[ignore(cfg(windows))]
-fn test_future_result() {
-    let mut result = None;
-    do task().future_result(|+r| { result = Some(move r); }).spawn { }
-    assert option::unwrap(move result).recv() == Success;
-
-    result = None;
-    do task().future_result(|+r|
-        { result = Some(move r); }).unlinked().spawn {
-        fail;
-    }
-    assert option::unwrap(move result).recv() == Failure;
-}
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_back_to_the_future_result() {
-    let _ = task().future_result(util::ignore).future_result(util::ignore);
-}
-
-#[test]
-fn test_spawn_listiner_bidi() {
-    let po = comm::Port();
-    let ch = comm::Chan(&po);
-    let ch = do spawn_listener |po| {
-        // Now the child has a port called 'po' to read from and
-        // an environment-captured channel called 'ch'.
-        let res: ~str = comm::recv(po);
-        assert res == ~"ping";
-        comm::send(ch, ~"pong");
-    };
-    // Likewise, the parent has both a 'po' and 'ch'
-    comm::send(ch, ~"ping");
-    let res: ~str = comm::recv(po);
-    assert res == ~"pong";
-}
-
-#[test]
-fn test_spawn_conversation() {
-    let (recv_str, send_int) = do spawn_conversation |recv_int, send_str| {
-        let input = comm::recv(recv_int);
-        let output = int::str(input);
-        comm::send(send_str, move output);
-    };
-    comm::send(send_int, 1);
-    assert comm::recv(recv_str) == ~"1";
-}
-
-#[test]
-fn test_try_success() {
-    match do try {
-        ~"Success!"
-    } {
-        result::Ok(~"Success!") => (),
-        _ => fail
-    }
-}
-
-#[test]
-#[ignore(cfg(windows))]
-fn test_try_fail() {
-    match do try {
-        fail
-    } {
-        result::Err(()) => (),
-        result::Ok(()) => fail
-    }
-}
-
-#[test]
-#[should_fail]
-#[ignore(cfg(windows))]
-fn test_spawn_sched_no_threads() {
-    do spawn_sched(ManualThreads(0u)) { }
-}
-
-#[test]
-fn test_spawn_sched() {
-    let po = comm::Port();
-    let ch = comm::Chan(&po);
-
-    fn f(i: int, ch: comm::Chan<()>) {
-        let parent_sched_id = rt::rust_get_sched_id();
-
-        do spawn_sched(SingleThreaded) {
-            let child_sched_id = rt::rust_get_sched_id();
-            assert parent_sched_id != child_sched_id;
-
-            if (i == 0) {
-                comm::send(ch, ());
-            } else {
-                f(i - 1, ch);
-            }
-        };
-
-    }
-    f(10, ch);
-    comm::recv(po);
-}
-
-#[test]
-fn test_spawn_sched_childs_on_same_sched() {
-    let po = comm::Port();
-    let ch = comm::Chan(&po);
-
-    do spawn_sched(SingleThreaded) {
-        let parent_sched_id = rt::rust_get_sched_id();
-        do spawn {
-            let child_sched_id = rt::rust_get_sched_id();
-            // This should be on the same scheduler
-            assert parent_sched_id == child_sched_id;
-            comm::send(ch, ());
-        };
-    };
-
-    comm::recv(po);
-}
-
-#[nolink]
-#[cfg(test)]
-extern mod testrt {
-    fn rust_dbg_lock_create() -> *libc::c_void;
-    fn rust_dbg_lock_destroy(lock: *libc::c_void);
-    fn rust_dbg_lock_lock(lock: *libc::c_void);
-    fn rust_dbg_lock_unlock(lock: *libc::c_void);
-    fn rust_dbg_lock_wait(lock: *libc::c_void);
-    fn rust_dbg_lock_signal(lock: *libc::c_void);
-}
-
-#[test]
-fn test_spawn_sched_blocking() {
-
-    // Testing that a task in one scheduler can block in foreign code
-    // without affecting other schedulers
-    for iter::repeat(20u) {
-
-        let start_po = comm::Port();
-        let start_ch = comm::Chan(&start_po);
-        let fin_po = comm::Port();
-        let fin_ch = comm::Chan(&fin_po);
-
-        let lock = testrt::rust_dbg_lock_create();
-
-        do spawn_sched(SingleThreaded) {
-            testrt::rust_dbg_lock_lock(lock);
-
-            comm::send(start_ch, ());
-
-            // Block the scheduler thread
-            testrt::rust_dbg_lock_wait(lock);
-            testrt::rust_dbg_lock_unlock(lock);
-
-            comm::send(fin_ch, ());
-        };
-
-        // Wait until the other task has its lock
-        comm::recv(start_po);
-
-        fn pingpong(po: comm::Port<int>, ch: comm::Chan<int>) {
-            let mut val = 20;
-            while val > 0 {
-                val = comm::recv(po);
-                comm::send(ch, val - 1);
-            }
-        }
-
-        let setup_po = comm::Port();
-        let setup_ch = comm::Chan(&setup_po);
-        let parent_po = comm::Port();
-        let parent_ch = comm::Chan(&parent_po);
-        do spawn {
-            let child_po = comm::Port();
-            comm::send(setup_ch, comm::Chan(&child_po));
-            pingpong(child_po, parent_ch);
-        };
-
-        let child_ch = comm::recv(setup_po);
-        comm::send(child_ch, 20);
-        pingpong(parent_po, child_ch);
-        testrt::rust_dbg_lock_lock(lock);
-        testrt::rust_dbg_lock_signal(lock);
-        testrt::rust_dbg_lock_unlock(lock);
-        comm::recv(fin_po);
-        testrt::rust_dbg_lock_destroy(lock);
-    }
-}
-
-#[cfg(test)]
-fn avoid_copying_the_body(spawnfn: fn(v: fn~())) {
-    let p = comm::Port::<uint>();
-    let ch = comm::Chan(&p);
-
-    let x = ~1;
-    let x_in_parent = ptr::addr_of(&(*x)) as uint;
-
-    do spawnfn |move x| {
-        let x_in_child = ptr::addr_of(&(*x)) as uint;
-        comm::send(ch, x_in_child);
-    }
-
-    let x_in_child = comm::recv(p);
-    assert x_in_parent == x_in_child;
-}
-
-#[test]
-fn test_avoid_copying_the_body_spawn() {
-    avoid_copying_the_body(spawn);
-}
-
-#[test]
-fn test_avoid_copying_the_body_spawn_listener() {
-    do avoid_copying_the_body |f| {
-        spawn_listener(fn~(move f, _po: comm::Port<int>) {
-            f();
-        });
-    }
-}
-
-#[test]
-fn test_avoid_copying_the_body_task_spawn() {
-    do avoid_copying_the_body |f| {
-        do task().spawn |move f| {
-            f();
-        }
-    }
-}
-
-#[test]
-fn test_avoid_copying_the_body_spawn_listener_1() {
-    do avoid_copying_the_body |f| {
-        task().spawn_listener(fn~(move f, _po: comm::Port<int>) {
-            f();
-        });
-    }
-}
-
-#[test]
-fn test_avoid_copying_the_body_try() {
-    do avoid_copying_the_body |f| {
-        do try |move f| {
-            f()
-        };
-    }
-}
-
-#[test]
-fn test_avoid_copying_the_body_unlinked() {
-    do avoid_copying_the_body |f| {
-        do spawn_unlinked |move f| {
-            f();
-        }
-    }
-}
-
-#[test]
-fn test_platform_thread() {
-    let po = comm::Port();
-    let ch = comm::Chan(&po);
-    do task().sched_mode(PlatformThread).spawn {
-        comm::send(ch, ());
-    }
-    comm::recv(po);
-}
-
-#[test]
-#[ignore(cfg(windows))]
-#[should_fail]
-fn test_unkillable() {
-    let po = comm::Port();
-    let ch = po.chan();
-
-    // We want to do this after failing
-    do spawn_unlinked {
-        for iter::repeat(10) { yield() }
-        ch.send(());
-    }
-
-    do spawn {
-        yield();
-        // We want to fail after the unkillable task
-        // blocks on recv
-        fail;
-    }
-
-    unsafe {
-        do unkillable {
-            let p = ~0;
-            let pp: *uint = cast::transmute(move p);
-
-            // If we are killed here then the box will leak
-            po.recv();
-
-            let _p: ~int = cast::transmute(move pp);
-        }
-    }
-
-    // Now we can be killed
-    po.recv();
-}
-
-#[test]
-#[ignore(cfg(windows))]
-#[should_fail]
-fn test_unkillable_nested() {
-    let (ch, po) = pipes::stream();
-
-    // We want to do this after failing
-    do spawn_unlinked |move ch| {
-        for iter::repeat(10) { yield() }
-        ch.send(());
-    }
-
-    do spawn {
-        yield();
-        // We want to fail after the unkillable task
-        // blocks on recv
-        fail;
-    }
-
-    unsafe {
-        do unkillable {
-            do unkillable {} // Here's the difference from the previous test.
-            let p = ~0;
-            let pp: *uint = cast::transmute(move p);
-
-            // If we are killed here then the box will leak
-            po.recv();
-
-            let _p: ~int = cast::transmute(move pp);
-        }
-    }
-
-    // Now we can be killed
-    po.recv();
-}
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_atomically() {
-    unsafe { do atomically { yield(); } }
-}
-
-#[test]
-fn test_atomically2() {
-    unsafe { do atomically { } } yield(); // shouldn't fail
-}
-
-#[test] #[should_fail] #[ignore(cfg(windows))]
-fn test_atomically_nested() {
-    unsafe { do atomically { do atomically { } yield(); } }
-}
-
-#[test]
-fn test_child_doesnt_ref_parent() {
-    // If the child refcounts the parent task, this will stack overflow when
-    // climbing the task tree to dereference each ancestor. (See #1789)
-    // (well, it would if the constant were 8000+ - I lowered it to be more
-    // valgrind-friendly. try this at home, instead..!)
-    const generations: uint = 16;
-    fn child_no(x: uint) -> fn~() {
-        return || {
-            if x < generations {
-                task::spawn(child_no(x+1));
-            }
-        }
-    }
-    task::spawn(child_no(0));
-}
-
-#[test]
-fn test_sched_thread_per_core() {
-    let (chan, port) = pipes::stream();
-
-    do spawn_sched(ThreadPerCore) |move chan| {
-        let cores = rt::rust_num_threads();
-        let reported_threads = rt::rust_sched_threads();
-        assert(cores as uint == reported_threads as uint);
-        chan.send(());
-    }
-
-    port.recv();
-}
-
-#[test]
-fn test_spawn_thread_on_demand() {
-    let (chan, port) = pipes::stream();
-
-    do spawn_sched(ManualThreads(2)) |move chan| {
-        let max_threads = rt::rust_sched_threads();
-        assert(max_threads as int == 2);
-        let running_threads = rt::rust_sched_current_nonlazy_threads();
-        assert(running_threads as int == 1);
-
-        let (chan2, port2) = pipes::stream();
-
-        do spawn() |move chan2| {
-            chan2.send(());
-        }
-
-        let running_threads2 = rt::rust_sched_current_nonlazy_threads();
-        assert(running_threads2 as int == 2);
-
-        port2.recv();
-        chan.send(());
-    }
-
-    port.recv();
-}
diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs
index 887983e8b6c..90781044277 100644
--- a/src/libcore/task/mod.rs
+++ b/src/libcore/task/mod.rs
@@ -1,8 +1,1306 @@
+// NB: transitionary, de-mode-ing.
+// tjc: Deprecated modes allowed because of function arg issue
+// in task::spawn. Re-forbid after snapshot.
+#[forbid(deprecated_pattern)];
 
-mod local_data_priv;
+/*!
+ * Task management.
+ *
+ * An executing Rust program consists of a tree of tasks, each with their own
+ * stack, and sole ownership of their allocated heap data. Tasks communicate
+ * with each other using ports and channels.
+ *
+ * When a task fails, that failure will propagate to its parent (the task
+ * that spawned it) and the parent will fail as well. The reverse is not
+ * true: when a parent task fails its children will continue executing. When
+ * the root (main) task fails, all tasks fail, and then so does the entire
+ * process.
+ *
+ * Tasks may execute in parallel and are scheduled automatically by the
+ * runtime.
+ *
+ * # Example
+ *
+ * ~~~
+ * do spawn {
+ *     log(error, "Hello, World!");
+ * }
+ * ~~~
+ */
 
-pub mod local_data;
+use cmp::Eq;
+use result::Result;
+use pipes::{stream, Chan, Port};
+use local_data_priv::{local_get, local_set};
+use util::replace;
 
-pub mod rt;
+use rt::task_id;
+use rt::rust_task;
 
+mod local_data_priv;
+pub mod local_data;
+pub mod rt;
 pub mod spawn;
+
+/// A handle to a task
+pub enum Task {
+    TaskHandle(task_id)
+}
+
+impl Task : cmp::Eq {
+    pure fn eq(&self, other: &Task) -> bool { *(*self) == *(*other) }
+    pure fn ne(&self, other: &Task) -> bool { !(*self).eq(other) }
+}
+
+/**
+ * Indicates the manner in which a task exited.
+ *
+ * A task that completes without failing is considered to exit successfully.
+ * Supervised ancestors and linked siblings may yet fail after this task
+ * succeeds. Also note that in such a case, it may be nondeterministic whether
+ * linked failure or successful exit happen first.
+ *
+ * If you wish for this result's delivery to block until all linked and/or
+ * children tasks complete, recommend using a result future.
+ */
+pub enum TaskResult {
+    Success,
+    Failure,
+}
+
+impl TaskResult : Eq {
+    pure fn eq(&self, other: &TaskResult) -> bool {
+        match ((*self), (*other)) {
+            (Success, Success) | (Failure, Failure) => true,
+            (Success, _) | (Failure, _) => false
+        }
+    }
+    pure fn ne(&self, other: &TaskResult) -> bool { !(*self).eq(other) }
+}
+
+/// Scheduler modes
+pub enum SchedMode {
+    /// All tasks run in the same OS thread
+    SingleThreaded,
+    /// Tasks are distributed among available CPUs
+    ThreadPerCore,
+    /// Each task runs in its own OS thread
+    ThreadPerTask,
+    /// Tasks are distributed among a fixed number of OS threads
+    ManualThreads(uint),
+    /**
+     * Tasks are scheduled on the main OS thread
+     *
+     * The main OS thread is the thread used to launch the runtime which,
+     * in most cases, is the process's initial thread as created by the OS.
+     */
+    PlatformThread
+}
+
+impl SchedMode : cmp::Eq {
+    pure fn eq(&self, other: &SchedMode) -> bool {
+        match (*self) {
+            SingleThreaded => {
+                match (*other) {
+                    SingleThreaded => true,
+                    _ => false
+                }
+            }
+            ThreadPerCore => {
+                match (*other) {
+                    ThreadPerCore => true,
+                    _ => false
+                }
+            }
+            ThreadPerTask => {
+                match (*other) {
+                    ThreadPerTask => true,
+                    _ => false
+                }
+            }
+            ManualThreads(e0a) => {
+                match (*other) {
+                    ManualThreads(e0b) => e0a == e0b,
+                    _ => false
+                }
+            }
+            PlatformThread => {
+                match (*other) {
+                    PlatformThread => true,
+                    _ => false
+                }
+            }
+        }
+    }
+    pure fn ne(&self, other: &SchedMode) -> bool {
+        !(*self).eq(other)
+    }
+}
+
+/**
+ * Scheduler configuration options
+ *
+ * # Fields
+ *
+ * * sched_mode - The operating mode of the scheduler
+ *
+ * * foreign_stack_size - The size of the foreign stack, in bytes
+ *
+ *     Rust code runs on Rust-specific stacks. When Rust code calls foreign
+ *     code (via functions in foreign modules) it switches to a typical, large
+ *     stack appropriate for running code written in languages like C. By
+ *     default these foreign stacks have unspecified size, but with this
+ *     option their size can be precisely specified.
+ */
+pub type SchedOpts = {
+    mode: SchedMode,
+    foreign_stack_size: Option<uint>
+};
+
+/**
+ * Task configuration options
+ *
+ * # Fields
+ *
+ * * linked - Propagate failure bidirectionally between child and parent.
+ *            True by default. If both this and 'supervised' are false, then
+ *            either task's failure will not affect the other ("unlinked").
+ *
+ * * supervised - Propagate failure unidirectionally from parent to child,
+ *                but not from child to parent. False by default.
+ *
+ * * notify_chan - Enable lifecycle notifications on the given channel
+ *
+ * * sched - Specify the configuration of a new scheduler to create the task
+ *           in
+ *
+ *     By default, every task is created in the same scheduler as its
+ *     parent, where it is scheduled cooperatively with all other tasks
+ *     in that scheduler. Some specialized applications may want more
+ *     control over their scheduling, in which case they can be spawned
+ *     into a new scheduler with the specific properties required.
+ *
+ *     This is of particular importance for libraries which want to call
+ *     into foreign code that blocks. Without doing so in a different
+ *     scheduler other tasks will be impeded or even blocked indefinitely.
+ */
+pub type TaskOpts = {
+    linked: bool,
+    supervised: bool,
+    mut notify_chan: Option<Chan<TaskResult>>,
+    sched: Option<SchedOpts>,
+};
+
+/**
+ * The task builder type.
+ *
+ * Provides detailed control over the properties and behavior of new tasks.
+ */
+// NB: Builders are designed to be single-use because they do stateful
+// things that get weird when reusing - e.g. if you create a result future
+// it only applies to a single task, so then you have to maintain Some
+// potentially tricky state to ensure that everything behaves correctly
+// when you try to reuse the builder to spawn a new task. We'll just
+// sidestep that whole issue by making builders uncopyable and making
+// the run function move them in.
+
+// FIXME (#3724): Replace the 'consumed' bit with move mode on self
+pub enum TaskBuilder = {
+    opts: TaskOpts,
+    gen_body: fn@(v: fn~()) -> fn~(),
+    can_not_copy: Option<util::NonCopyable>,
+    mut consumed: bool,
+};
+
+/**
+ * Generate the base configuration for spawning a task, off of which more
+ * configuration methods can be chained.
+ * For example, task().unlinked().spawn is equivalent to spawn_unlinked.
+ */
+pub fn task() -> TaskBuilder {
+    TaskBuilder({
+        opts: default_task_opts(),
+        gen_body: |body| move body, // Identity function
+        can_not_copy: None,
+        mut consumed: false,
+    })
+}
+
+#[doc(hidden)] // FIXME #3538
+priv impl TaskBuilder {
+    fn consume() -> TaskBuilder {
+        if self.consumed {
+            fail ~"Cannot copy a task_builder"; // Fake move mode on self
+        }
+        self.consumed = true;
+        let notify_chan = replace(&mut self.opts.notify_chan, None);
+        TaskBuilder({
+            opts: {
+                linked: self.opts.linked,
+                supervised: self.opts.supervised,
+                mut notify_chan: move notify_chan,
+                sched: self.opts.sched
+            },
+            gen_body: self.gen_body,
+            can_not_copy: None,
+            mut consumed: false
+        })
+    }
+}
+
+impl TaskBuilder {
+    /**
+     * Decouple the child task's failure from the parent's. If either fails,
+     * the other will not be killed.
+     */
+    fn unlinked() -> TaskBuilder {
+        let notify_chan = replace(&mut self.opts.notify_chan, None);
+        TaskBuilder({
+            opts: {
+                linked: false,
+                supervised: self.opts.supervised,
+                mut notify_chan: move notify_chan,
+                sched: self.opts.sched
+            },
+            can_not_copy: None,
+            .. *self.consume()
+        })
+    }
+    /**
+     * Unidirectionally link the child task's failure with the parent's. The
+     * child's failure will not kill the parent, but the parent's will kill
+     * the child.
+     */
+    fn supervised() -> TaskBuilder {
+        let notify_chan = replace(&mut self.opts.notify_chan, None);
+        TaskBuilder({
+            opts: {
+                linked: false,
+                supervised: true,
+                mut notify_chan: move notify_chan,
+                sched: self.opts.sched
+            },
+            can_not_copy: None,
+            .. *self.consume()
+        })
+    }
+    /**
+     * Link the child task's and parent task's failures. If either fails, the
+     * other will be killed.
+     */
+    fn linked() -> TaskBuilder {
+        let notify_chan = replace(&mut self.opts.notify_chan, None);
+        TaskBuilder({
+            opts: {
+                linked: true,
+                supervised: false,
+                mut notify_chan: move notify_chan,
+                sched: self.opts.sched
+            },
+            can_not_copy: None,
+            .. *self.consume()
+        })
+    }
+
+    /**
+     * Get a future representing the exit status of the task.
+     *
+     * Taking the value of the future will block until the child task
+     * terminates. The future-receiving callback specified will be called
+     * *before* the task is spawned; as such, do not invoke .get() within the
+     * closure; rather, store it in an outer variable/list for later use.
+     *
+     * Note that the future returning by this function is only useful for
+     * obtaining the value of the next task to be spawning with the
+     * builder. If additional tasks are spawned with the same builder
+     * then a new result future must be obtained prior to spawning each
+     * task.
+     *
+     * # Failure
+     * Fails if a future_result was already set for this task.
+     */
+    fn future_result(blk: fn(v: Port<TaskResult>)) -> TaskBuilder {
+        // FIXME (#3725): Once linked failure and notification are
+        // handled in the library, I can imagine implementing this by just
+        // registering an arbitrary number of task::on_exit handlers and
+        // sending out messages.
+
+        if self.opts.notify_chan.is_some() {
+            fail ~"Can't set multiple future_results for one task!";
+        }
+
+        // Construct the future and give it to the caller.
+        let (notify_pipe_ch, notify_pipe_po) = stream::<TaskResult>();
+
+        blk(move notify_pipe_po);
+
+        // Reconfigure self to use a notify channel.
+        TaskBuilder({
+            opts: {
+                linked: self.opts.linked,
+                supervised: self.opts.supervised,
+                mut notify_chan: Some(move notify_pipe_ch),
+                sched: self.opts.sched
+            },
+            can_not_copy: None,
+            .. *self.consume()
+        })
+    }
+    /// Configure a custom scheduler mode for the task.
+    fn sched_mode(mode: SchedMode) -> TaskBuilder {
+        let notify_chan = replace(&mut self.opts.notify_chan, None);
+        TaskBuilder({
+            opts: {
+                linked: self.opts.linked,
+                supervised: self.opts.supervised,
+                mut notify_chan: move notify_chan,
+                sched: Some({ mode: mode, foreign_stack_size: None})
+            },
+            can_not_copy: None,
+            .. *self.consume()
+        })
+    }
+
+    /**
+     * Add a wrapper to the body of the spawned task.
+     *
+     * Before the task is spawned it is passed through a 'body generator'
+     * function that may perform local setup operations as well as wrap
+     * the task body in remote setup operations. With this the behavior
+     * of tasks can be extended in simple ways.
+     *
+     * This function augments the current body generator with a new body
+     * generator by applying the task body which results from the
+     * existing body generator to the new body generator.
+     */
+    fn add_wrapper(wrapper: fn@(v: fn~()) -> fn~()) -> TaskBuilder {
+        let prev_gen_body = self.gen_body;
+        let notify_chan = replace(&mut self.opts.notify_chan, None);
+        TaskBuilder({
+            opts: {
+                linked: self.opts.linked,
+                supervised: self.opts.supervised,
+                mut notify_chan: move notify_chan,
+                sched: self.opts.sched
+            },
+            // tjc: I think this is the line that gets miscompiled
+            // w/ last-use off, if we leave out the move prev_gen_body?
+            // that makes no sense, though...
+            gen_body: |move prev_gen_body,
+                       body| { wrapper(prev_gen_body(move body)) },
+            can_not_copy: None,
+            .. *self.consume()
+        })
+    }
+
+    /**
+     * Creates and executes a new child task
+     *
+     * Sets up a new task with its own call stack and schedules it to run
+     * the provided unique closure. The task has the properties and behavior
+     * specified by the task_builder.
+     *
+     * # Failure
+     *
+     * When spawning into a new scheduler, the number of threads requested
+     * must be greater than zero.
+     */
+    fn spawn(f: fn~()) {
+        let notify_chan = replace(&mut self.opts.notify_chan, None);
+        let x = self.consume();
+        let opts = {
+            linked: x.opts.linked,
+            supervised: x.opts.supervised,
+            mut notify_chan: move notify_chan,
+            sched: x.opts.sched
+        };
+        spawn::spawn_raw(move opts, x.gen_body(move f));
+    }
+    /// Runs a task, while transfering ownership of one argument to the child.
+    fn spawn_with<A: Send>(arg: A, f: fn~(v: A)) {
+        let arg = ~mut Some(move arg);
+        do self.spawn |move arg, move f| {
+            f(option::swap_unwrap(arg))
+        }
+    }
+
+    /**
+     * Runs a new task while providing a channel from the parent to the child
+     *
+     * Sets up a communication channel from the current task to the new
+     * child task, passes the port to child's body, and returns a channel
+     * linked to the port to the parent.
+     *
+     * This encapsulates some boilerplate handshaking logic that would
+     * otherwise be required to establish communication from the parent
+     * to the child.
+     */
+    fn spawn_listener<A: Send>(f: fn~(comm::Port<A>)) -> comm::Chan<A> {
+        let setup_po = comm::Port();
+        let setup_ch = comm::Chan(&setup_po);
+        do self.spawn |move f| {
+            let po = comm::Port();
+            let ch = comm::Chan(&po);
+            comm::send(setup_ch, ch);
+            f(move po);
+        }
+        comm::recv(setup_po)
+    }
+
+    /**
+     * Runs a new task, setting up communication in both directions
+     */
+    fn spawn_conversation<A: Send, B: Send>
+        (f: fn~(comm::Port<A>, comm::Chan<B>))
+        -> (comm::Port<B>, comm::Chan<A>) {
+        let from_child = comm::Port();
+        let to_parent = comm::Chan(&from_child);
+        let to_child = do self.spawn_listener |move f, from_parent| {
+            f(from_parent, to_parent)
+        };
+        (from_child, to_child)
+    }
+
+    /**
+     * Execute a function in another task and return either the return value
+     * of the function or result::err.
+     *
+     * # Return value
+     *
+     * If the function executed successfully then try returns result::ok
+     * containing the value returned by the function. If the function fails
+     * then try returns result::err containing nil.
+     *
+     * # Failure
+     * Fails if a future_result was already set for this task.
+     */
+    fn try<T: Send>(f: fn~() -> T) -> Result<T,()> {
+        let po = comm::Port();
+        let ch = comm::Chan(&po);
+        let mut result = None;
+
+        let fr_task_builder = self.future_result(|+r| {
+            result = Some(move r);
+        });
+        do fr_task_builder.spawn |move f| {
+            comm::send(ch, f());
+        }
+        match option::unwrap(move result).recv() {
+            Success => result::Ok(comm::recv(po)),
+            Failure => result::Err(())
+        }
+    }
+}
+
+
+/* Task construction */
+
+pub fn default_task_opts() -> TaskOpts {
+    /*!
+     * The default task options
+     *
+     * By default all tasks are supervised by their parent, are spawned
+     * into the same scheduler, and do not post lifecycle notifications.
+     */
+
+    {
+        linked: true,
+        supervised: false,
+        mut notify_chan: None,
+        sched: None
+    }
+}
+
+/* Spawn convenience functions */
+
+pub fn spawn(f: fn~()) {
+    /*!
+     * Creates and executes a new child task
+     *
+     * Sets up a new task with its own call stack and schedules it to run
+     * the provided unique closure.
+     *
+     * This function is equivalent to `task().spawn(f)`.
+     */
+
+    task().spawn(move f)
+}
+
+pub fn spawn_unlinked(f: fn~()) {
+    /*!
+     * Creates a child task unlinked from the current one. If either this
+     * task or the child task fails, the other will not be killed.
+     */
+
+    task().unlinked().spawn(move f)
+}
+
+pub fn spawn_supervised(f: fn~()) {
+    /*!
+     * Creates a child task unlinked from the current one. If either this
+     * task or the child task fails, the other will not be killed.
+     */
+
+    task().supervised().spawn(move f)
+}
+
+pub fn spawn_with<A:Send>(arg: A, f: fn~(v: A)) {
+    /*!
+     * Runs a task, while transfering ownership of one argument to the
+     * child.
+     *
+     * This is useful for transfering ownership of noncopyables to
+     * another task.
+     *
+     * This function is equivalent to `task().spawn_with(arg, f)`.
+     */
+
+    task().spawn_with(move arg, move f)
+}
+
+pub fn spawn_listener<A:Send>(f: fn~(comm::Port<A>)) -> comm::Chan<A> {
+    /*!
+     * Runs a new task while providing a channel from the parent to the child
+     *
+     * This function is equivalent to `task().spawn_listener(f)`.
+     */
+
+    task().spawn_listener(move f)
+}
+
+pub fn spawn_conversation<A: Send, B: Send>
+    (f: fn~(comm::Port<A>, comm::Chan<B>))
+    -> (comm::Port<B>, comm::Chan<A>) {
+    /*!
+     * Runs a new task, setting up communication in both directions
+     *
+     * This function is equivalent to `task().spawn_conversation(f)`.
+     */
+
+    task().spawn_conversation(move f)
+}
+
+pub fn spawn_sched(mode: SchedMode, f: fn~()) {
+    /*!
+     * Creates a new scheduler and executes a task on it
+     *
+     * Tasks subsequently spawned by that task will also execute on
+     * the new scheduler. When there are no more tasks to execute the
+     * scheduler terminates.
+     *
+     * # Failure
+     *
+     * In manual threads mode the number of threads requested must be
+     * greater than zero.
+     */
+
+    task().sched_mode(mode).spawn(move f)
+}
+
+pub fn try<T:Send>(f: fn~() -> T) -> Result<T,()> {
+    /*!
+     * Execute a function in another task and return either the return value
+     * of the function or result::err.
+     *
+     * This is equivalent to task().supervised().try.
+     */
+
+    task().supervised().try(move f)
+}
+
+
+/* Lifecycle functions */
+
+pub fn yield() {
+    //! Yield control to the task scheduler
+
+    let task_ = rt::rust_get_task();
+    let killed = rt::rust_task_yield(task_);
+    if killed && !failing() {
+        fail ~"killed";
+    }
+}
+
+pub fn failing() -> bool {
+    //! True if the running task has failed
+
+    rt::rust_task_is_unwinding(rt::rust_get_task())
+}
+
+pub fn get_task() -> Task {
+    //! Get a handle to the running task
+
+    TaskHandle(rt::get_task_id())
+}
+
+/**
+ * Temporarily make the task unkillable
+ *
+ * # Example
+ *
+ * ~~~
+ * do task::unkillable {
+ *     // detach / yield / destroy must all be called together
+ *     rustrt::rust_port_detach(po);
+ *     // This must not result in the current task being killed
+ *     task::yield();
+ *     rustrt::rust_port_destroy(po);
+ * }
+ * ~~~
+ */
+pub unsafe fn unkillable<U>(f: fn() -> U) -> U {
+    struct AllowFailure {
+        t: *rust_task,
+        drop { rt::rust_task_allow_kill(self.t); }
+    }
+
+    fn AllowFailure(t: *rust_task) -> AllowFailure{
+        AllowFailure {
+            t: t
+        }
+    }
+
+    let t = rt::rust_get_task();
+    let _allow_failure = AllowFailure(t);
+    rt::rust_task_inhibit_kill(t);
+    f()
+}
+
+/// The inverse of unkillable. Only ever to be used nested in unkillable().
+pub unsafe fn rekillable<U>(f: fn() -> U) -> U {
+    struct DisallowFailure {
+        t: *rust_task,
+        drop { rt::rust_task_inhibit_kill(self.t); }
+    }
+
+    fn DisallowFailure(t: *rust_task) -> DisallowFailure {
+        DisallowFailure {
+            t: t
+        }
+    }
+
+    let t = rt::rust_get_task();
+    let _allow_failure = DisallowFailure(t);
+    rt::rust_task_allow_kill(t);
+    f()
+}
+
+/**
+ * A stronger version of unkillable that also inhibits scheduling operations.
+ * For use with exclusive ARCs, which use pthread mutexes directly.
+ */
+pub unsafe fn atomically<U>(f: fn() -> U) -> U {
+    struct DeferInterrupts {
+        t: *rust_task,
+        drop {
+            rt::rust_task_allow_yield(self.t);
+            rt::rust_task_allow_kill(self.t);
+        }
+    }
+
+    fn DeferInterrupts(t: *rust_task) -> DeferInterrupts {
+        DeferInterrupts {
+            t: t
+        }
+    }
+
+    let t = rt::rust_get_task();
+    let _interrupts = DeferInterrupts(t);
+    rt::rust_task_inhibit_kill(t);
+    rt::rust_task_inhibit_yield(t);
+    f()
+}
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_cant_dup_task_builder() {
+    let b = task().unlinked();
+    do b.spawn { }
+    // FIXME(#3724): For now, this is a -runtime- failure, because we haven't
+    // got move mode on self. When 3724 is fixed, this test should fail to
+    // compile instead, and should go in tests/compile-fail.
+    do b.spawn { } // b should have been consumed by the previous call
+}
+
+// The following 8 tests test the following 2^3 combinations:
+// {un,}linked {un,}supervised failure propagation {up,down}wards.
+
+// !!! These tests are dangerous. If Something is buggy, they will hang, !!!
+// !!! instead of exiting cleanly. This might wedge the buildbots.       !!!
+
+#[test] #[ignore(cfg(windows))]
+fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
+    let po = comm::Port();
+    let ch = comm::Chan(&po);
+    do spawn_unlinked {
+        do spawn_unlinked {
+            // Give middle task a chance to fail-but-not-kill-us.
+            for iter::repeat(16) { task::yield(); }
+            comm::send(ch, ()); // If killed first, grandparent hangs.
+        }
+        fail; // Shouldn't kill either (grand)parent or (grand)child.
+    }
+    comm::recv(po);
+}
+#[test] #[ignore(cfg(windows))]
+fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
+    do spawn_unlinked { fail; }
+}
+#[test] #[ignore(cfg(windows))]
+fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
+    do spawn_supervised { fail; }
+    // Give child a chance to fail-but-not-kill-us.
+    for iter::repeat(16) { task::yield(); }
+}
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_unlinked_sup_fail_down() {
+    do spawn_supervised { loop { task::yield(); } }
+    fail; // Shouldn't leave a child hanging around.
+}
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
+    let po = comm::Port::<()>();
+    let _ch = comm::Chan(&po);
+    // Unidirectional "parenting" shouldn't override bidirectional linked.
+    // We have to cheat with opts - the interface doesn't support them because
+    // they don't make sense (redundant with task().supervised()).
+    let opts = {
+        let mut opts = default_task_opts();
+        opts.linked = true;
+        opts.supervised = true;
+        move opts
+    };
+
+    let b0 = task();
+    let b1 = TaskBuilder({
+        opts: move opts,
+        can_not_copy: None,
+        .. *b0
+    });
+    do b1.spawn { fail; }
+    comm::recv(po); // We should get punted awake
+}
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
+    // We have to cheat with opts - the interface doesn't support them because
+    // they don't make sense (redundant with task().supervised()).
+    let opts = {
+        let mut opts = default_task_opts();
+        opts.linked = true;
+        opts.supervised = true;
+        move opts
+    };
+
+    let b0 = task();
+    let b1 = TaskBuilder({
+        opts: move opts,
+        can_not_copy: None,
+        .. *b0
+    });
+    do b1.spawn { loop { task::yield(); } }
+    fail; // *both* mechanisms would be wrong if this didn't kill the child...
+}
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
+    let po = comm::Port::<()>();
+    let _ch = comm::Chan(&po);
+    // Default options are to spawn linked & unsupervised.
+    do spawn { fail; }
+    comm::recv(po); // We should get punted awake
+}
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
+    // Default options are to spawn linked & unsupervised.
+    do spawn { loop { task::yield(); } }
+    fail;
+}
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
+    // Make sure the above test is the same as this one.
+    do task().linked().spawn { loop { task::yield(); } }
+    fail;
+}
+
+// A couple bonus linked failure tests - testing for failure propagation even
+// when the middle task exits successfully early before kill signals are sent.
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_failure_propagate_grandchild() {
+    // Middle task exits; does grandparent's failure propagate across the gap?
+    do spawn_supervised {
+        do spawn_supervised {
+            loop { task::yield(); }
+        }
+    }
+    for iter::repeat(16) { task::yield(); }
+    fail;
+}
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_failure_propagate_secondborn() {
+    // First-born child exits; does parent's failure propagate to sibling?
+    do spawn_supervised {
+        do spawn { // linked
+            loop { task::yield(); }
+        }
+    }
+    for iter::repeat(16) { task::yield(); }
+    fail;
+}
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_failure_propagate_nephew_or_niece() {
+    // Our sibling exits; does our failure propagate to sibling's child?
+    do spawn { // linked
+        do spawn_supervised {
+            loop { task::yield(); }
+        }
+    }
+    for iter::repeat(16) { task::yield(); }
+    fail;
+}
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_spawn_linked_sup_propagate_sibling() {
+    // Middle sibling exits - does eldest's failure propagate to youngest?
+    do spawn { // linked
+        do spawn { // linked
+            loop { task::yield(); }
+        }
+    }
+    for iter::repeat(16) { task::yield(); }
+    fail;
+}
+
+#[test]
+fn test_run_basic() {
+    let po = comm::Port();
+    let ch = comm::Chan(&po);
+    do task().spawn {
+        comm::send(ch, ());
+    }
+    comm::recv(po);
+}
+
+#[test]
+fn test_add_wrapper() {
+    let po = comm::Port();
+    let ch = comm::Chan(&po);
+    let b0 = task();
+    let b1 = do b0.add_wrapper |body| {
+        fn~(move body) {
+            body();
+            comm::send(ch, ());
+        }
+    };
+    do b1.spawn { }
+    comm::recv(po);
+}
+
+#[test]
+#[ignore(cfg(windows))]
+fn test_future_result() {
+    let mut result = None;
+    do task().future_result(|+r| { result = Some(move r); }).spawn { }
+    assert option::unwrap(move result).recv() == Success;
+
+    result = None;
+    do task().future_result(|+r|
+        { result = Some(move r); }).unlinked().spawn {
+        fail;
+    }
+    assert option::unwrap(move result).recv() == Failure;
+}
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_back_to_the_future_result() {
+    let _ = task().future_result(util::ignore).future_result(util::ignore);
+}
+
+#[test]
+fn test_spawn_listiner_bidi() {
+    let po = comm::Port();
+    let ch = comm::Chan(&po);
+    let ch = do spawn_listener |po| {
+        // Now the child has a port called 'po' to read from and
+        // an environment-captured channel called 'ch'.
+        let res: ~str = comm::recv(po);
+        assert res == ~"ping";
+        comm::send(ch, ~"pong");
+    };
+    // Likewise, the parent has both a 'po' and 'ch'
+    comm::send(ch, ~"ping");
+    let res: ~str = comm::recv(po);
+    assert res == ~"pong";
+}
+
+#[test]
+fn test_spawn_conversation() {
+    let (recv_str, send_int) = do spawn_conversation |recv_int, send_str| {
+        let input = comm::recv(recv_int);
+        let output = int::str(input);
+        comm::send(send_str, move output);
+    };
+    comm::send(send_int, 1);
+    assert comm::recv(recv_str) == ~"1";
+}
+
+#[test]
+fn test_try_success() {
+    match do try {
+        ~"Success!"
+    } {
+        result::Ok(~"Success!") => (),
+        _ => fail
+    }
+}
+
+#[test]
+#[ignore(cfg(windows))]
+fn test_try_fail() {
+    match do try {
+        fail
+    } {
+        result::Err(()) => (),
+        result::Ok(()) => fail
+    }
+}
+
+#[test]
+#[should_fail]
+#[ignore(cfg(windows))]
+fn test_spawn_sched_no_threads() {
+    do spawn_sched(ManualThreads(0u)) { }
+}
+
+#[test]
+fn test_spawn_sched() {
+    let po = comm::Port();
+    let ch = comm::Chan(&po);
+
+    fn f(i: int, ch: comm::Chan<()>) {
+        let parent_sched_id = rt::rust_get_sched_id();
+
+        do spawn_sched(SingleThreaded) {
+            let child_sched_id = rt::rust_get_sched_id();
+            assert parent_sched_id != child_sched_id;
+
+            if (i == 0) {
+                comm::send(ch, ());
+            } else {
+                f(i - 1, ch);
+            }
+        };
+
+    }
+    f(10, ch);
+    comm::recv(po);
+}
+
+#[test]
+fn test_spawn_sched_childs_on_same_sched() {
+    let po = comm::Port();
+    let ch = comm::Chan(&po);
+
+    do spawn_sched(SingleThreaded) {
+        let parent_sched_id = rt::rust_get_sched_id();
+        do spawn {
+            let child_sched_id = rt::rust_get_sched_id();
+            // This should be on the same scheduler
+            assert parent_sched_id == child_sched_id;
+            comm::send(ch, ());
+        };
+    };
+
+    comm::recv(po);
+}
+
+#[nolink]
+#[cfg(test)]
+extern mod testrt {
+    fn rust_dbg_lock_create() -> *libc::c_void;
+    fn rust_dbg_lock_destroy(lock: *libc::c_void);
+    fn rust_dbg_lock_lock(lock: *libc::c_void);
+    fn rust_dbg_lock_unlock(lock: *libc::c_void);
+    fn rust_dbg_lock_wait(lock: *libc::c_void);
+    fn rust_dbg_lock_signal(lock: *libc::c_void);
+}
+
+#[test]
+fn test_spawn_sched_blocking() {
+
+    // Testing that a task in one scheduler can block in foreign code
+    // without affecting other schedulers
+    for iter::repeat(20u) {
+
+        let start_po = comm::Port();
+        let start_ch = comm::Chan(&start_po);
+        let fin_po = comm::Port();
+        let fin_ch = comm::Chan(&fin_po);
+
+        let lock = testrt::rust_dbg_lock_create();
+
+        do spawn_sched(SingleThreaded) {
+            testrt::rust_dbg_lock_lock(lock);
+
+            comm::send(start_ch, ());
+
+            // Block the scheduler thread
+            testrt::rust_dbg_lock_wait(lock);
+            testrt::rust_dbg_lock_unlock(lock);
+
+            comm::send(fin_ch, ());
+        };
+
+        // Wait until the other task has its lock
+        comm::recv(start_po);
+
+        fn pingpong(po: comm::Port<int>, ch: comm::Chan<int>) {
+            let mut val = 20;
+            while val > 0 {
+                val = comm::recv(po);
+                comm::send(ch, val - 1);
+            }
+        }
+
+        let setup_po = comm::Port();
+        let setup_ch = comm::Chan(&setup_po);
+        let parent_po = comm::Port();
+        let parent_ch = comm::Chan(&parent_po);
+        do spawn {
+            let child_po = comm::Port();
+            comm::send(setup_ch, comm::Chan(&child_po));
+            pingpong(child_po, parent_ch);
+        };
+
+        let child_ch = comm::recv(setup_po);
+        comm::send(child_ch, 20);
+        pingpong(parent_po, child_ch);
+        testrt::rust_dbg_lock_lock(lock);
+        testrt::rust_dbg_lock_signal(lock);
+        testrt::rust_dbg_lock_unlock(lock);
+        comm::recv(fin_po);
+        testrt::rust_dbg_lock_destroy(lock);
+    }
+}
+
+#[cfg(test)]
+fn avoid_copying_the_body(spawnfn: fn(v: fn~())) {
+    let p = comm::Port::<uint>();
+    let ch = comm::Chan(&p);
+
+    let x = ~1;
+    let x_in_parent = ptr::addr_of(&(*x)) as uint;
+
+    do spawnfn |move x| {
+        let x_in_child = ptr::addr_of(&(*x)) as uint;
+        comm::send(ch, x_in_child);
+    }
+
+    let x_in_child = comm::recv(p);
+    assert x_in_parent == x_in_child;
+}
+
+#[test]
+fn test_avoid_copying_the_body_spawn() {
+    avoid_copying_the_body(spawn);
+}
+
+#[test]
+fn test_avoid_copying_the_body_spawn_listener() {
+    do avoid_copying_the_body |f| {
+        spawn_listener(fn~(move f, _po: comm::Port<int>) {
+            f();
+        });
+    }
+}
+
+#[test]
+fn test_avoid_copying_the_body_task_spawn() {
+    do avoid_copying_the_body |f| {
+        do task().spawn |move f| {
+            f();
+        }
+    }
+}
+
+#[test]
+fn test_avoid_copying_the_body_spawn_listener_1() {
+    do avoid_copying_the_body |f| {
+        task().spawn_listener(fn~(move f, _po: comm::Port<int>) {
+            f();
+        });
+    }
+}
+
+#[test]
+fn test_avoid_copying_the_body_try() {
+    do avoid_copying_the_body |f| {
+        do try |move f| {
+            f()
+        };
+    }
+}
+
+#[test]
+fn test_avoid_copying_the_body_unlinked() {
+    do avoid_copying_the_body |f| {
+        do spawn_unlinked |move f| {
+            f();
+        }
+    }
+}
+
+#[test]
+fn test_platform_thread() {
+    let po = comm::Port();
+    let ch = comm::Chan(&po);
+    do task().sched_mode(PlatformThread).spawn {
+        comm::send(ch, ());
+    }
+    comm::recv(po);
+}
+
+#[test]
+#[ignore(cfg(windows))]
+#[should_fail]
+fn test_unkillable() {
+    let po = comm::Port();
+    let ch = po.chan();
+
+    // We want to do this after failing
+    do spawn_unlinked {
+        for iter::repeat(10) { yield() }
+        ch.send(());
+    }
+
+    do spawn {
+        yield();
+        // We want to fail after the unkillable task
+        // blocks on recv
+        fail;
+    }
+
+    unsafe {
+        do unkillable {
+            let p = ~0;
+            let pp: *uint = cast::transmute(move p);
+
+            // If we are killed here then the box will leak
+            po.recv();
+
+            let _p: ~int = cast::transmute(move pp);
+        }
+    }
+
+    // Now we can be killed
+    po.recv();
+}
+
+#[test]
+#[ignore(cfg(windows))]
+#[should_fail]
+fn test_unkillable_nested() {
+    let (ch, po) = pipes::stream();
+
+    // We want to do this after failing
+    do spawn_unlinked |move ch| {
+        for iter::repeat(10) { yield() }
+        ch.send(());
+    }
+
+    do spawn {
+        yield();
+        // We want to fail after the unkillable task
+        // blocks on recv
+        fail;
+    }
+
+    unsafe {
+        do unkillable {
+            do unkillable {} // Here's the difference from the previous test.
+            let p = ~0;
+            let pp: *uint = cast::transmute(move p);
+
+            // If we are killed here then the box will leak
+            po.recv();
+
+            let _p: ~int = cast::transmute(move pp);
+        }
+    }
+
+    // Now we can be killed
+    po.recv();
+}
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_atomically() {
+    unsafe { do atomically { yield(); } }
+}
+
+#[test]
+fn test_atomically2() {
+    unsafe { do atomically { } } yield(); // shouldn't fail
+}
+
+#[test] #[should_fail] #[ignore(cfg(windows))]
+fn test_atomically_nested() {
+    unsafe { do atomically { do atomically { } yield(); } }
+}
+
+#[test]
+fn test_child_doesnt_ref_parent() {
+    // If the child refcounts the parent task, this will stack overflow when
+    // climbing the task tree to dereference each ancestor. (See #1789)
+    // (well, it would if the constant were 8000+ - I lowered it to be more
+    // valgrind-friendly. try this at home, instead..!)
+    const generations: uint = 16;
+    fn child_no(x: uint) -> fn~() {
+        return || {
+            if x < generations {
+                task::spawn(child_no(x+1));
+            }
+        }
+    }
+    task::spawn(child_no(0));
+}
+
+#[test]
+fn test_sched_thread_per_core() {
+    let (chan, port) = pipes::stream();
+
+    do spawn_sched(ThreadPerCore) |move chan| {
+        let cores = rt::rust_num_threads();
+        let reported_threads = rt::rust_sched_threads();
+        assert(cores as uint == reported_threads as uint);
+        chan.send(());
+    }
+
+    port.recv();
+}
+
+#[test]
+fn test_spawn_thread_on_demand() {
+    let (chan, port) = pipes::stream();
+
+    do spawn_sched(ManualThreads(2)) |move chan| {
+        let max_threads = rt::rust_sched_threads();
+        assert(max_threads as int == 2);
+        let running_threads = rt::rust_sched_current_nonlazy_threads();
+        assert(running_threads as int == 1);
+
+        let (chan2, port2) = pipes::stream();
+
+        do spawn() |move chan2| {
+            chan2.send(());
+        }
+
+        let running_threads2 = rt::rust_sched_current_nonlazy_threads();
+        assert(running_threads2 as int == 2);
+
+        port2.recv();
+        chan.send(());
+    }
+
+    port.recv();
+}
diff --git a/src/librustc/driver.rs b/src/librustc/driver.rs
deleted file mode 100644
index cbe6345a9f1..00000000000
--- a/src/librustc/driver.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-use syntax::diagnostic;
-export diagnostic;
-
-export driver;
-export session;
diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs
index 7d9a55571d4..a9d5afeb522 100644
--- a/src/librustc/driver/mod.rs
+++ b/src/librustc/driver/mod.rs
@@ -1,4 +1,12 @@
 #[legacy_exports];
+
+use syntax::diagnostic;
+
+export diagnostic;
+
+export driver;
+export session;
+
 #[legacy_exports]
 mod driver;
 #[legacy_exports]
diff --git a/src/librustc/metadata.rs b/src/librustc/metadata.rs
deleted file mode 100644
index 2d2d6a3f79e..00000000000
--- a/src/librustc/metadata.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Define the rustc API's that the metadata module has access to
-// Over time we will reduce these dependencies and, once metadata has
-// no dependencies on rustc it can move into its own crate.
-
-mod middle {
-    #[legacy_exports];
-    pub use middle_::ty;
-    pub use middle_::resolve;
-}
-
-mod front {
-    #[legacy_exports];
-}
-
-mod back {
-    #[legacy_exports];
-}
-
-mod driver {
-    #[legacy_exports];
-}
-
-mod util {
-    #[legacy_exports];
-    pub use util_::ppaux;
-}
-
-mod lib {
-    #[legacy_exports];
-    pub use lib_::llvm;
-}
diff --git a/src/librustc/metadata/mod.rs b/src/librustc/metadata/mod.rs
index a82ad39d412..dfe983cc32b 100644
--- a/src/librustc/metadata/mod.rs
+++ b/src/librustc/metadata/mod.rs
@@ -30,3 +30,36 @@ mod csearch;
 mod loader;
 #[legacy_exports]
 mod filesearch;
+
+
+// Define the rustc API's that the metadata module has access to
+// Over time we will reduce these dependencies and, once metadata has
+// no dependencies on rustc it can move into its own crate.
+
+mod middle {
+    #[legacy_exports];
+    pub use middle_::ty;
+    pub use middle_::resolve;
+}
+
+mod front {
+    #[legacy_exports];
+}
+
+mod back {
+    #[legacy_exports];
+}
+
+mod driver {
+    #[legacy_exports];
+}
+
+mod util {
+    #[legacy_exports];
+    pub use util_::ppaux;
+}
+
+mod lib {
+    #[legacy_exports];
+    pub use lib_::llvm;
+}
diff --git a/src/librustc/middle/borrowck.rs b/src/librustc/middle/borrowck.rs
deleted file mode 100644
index df573b121f8..00000000000
--- a/src/librustc/middle/borrowck.rs
+++ /dev/null
@@ -1,618 +0,0 @@
-/*!
-# Borrow check
-
-This pass is in job of enforcing *memory safety* and *purity*.  As
-memory safety is by far the more complex topic, I'll focus on that in
-this description, but purity will be covered later on. In the context
-of Rust, memory safety means three basic things:
-
-- no writes to immutable memory;
-- all pointers point to non-freed memory;
-- all pointers point to memory of the same type as the pointer.
-
-The last point might seem confusing: after all, for the most part,
-this condition is guaranteed by the type check.  However, there are
-two cases where the type check effectively delegates to borrow check.
-
-The first case has to do with enums.  If there is a pointer to the
-interior of an enum, and the enum is in a mutable location (such as a
-local variable or field declared to be mutable), it is possible that
-the user will overwrite the enum with a new value of a different
-variant, and thus effectively change the type of the memory that the
-pointer is pointing at.
-
-The second case has to do with mutability.  Basically, the type
-checker has only a limited understanding of mutability.  It will allow
-(for example) the user to get an immutable pointer with the address of
-a mutable local variable.  It will also allow a `@mut T` or `~mut T`
-pointer to be borrowed as a `&r.T` pointer.  These seeming oversights
-are in fact intentional; they allow the user to temporarily treat a
-mutable value as immutable.  It is up to the borrow check to guarantee
-that the value in question is not in fact mutated during the lifetime
-`r` of the reference.
-
-# Definition of unstable memory
-
-The primary danger to safety arises due to *unstable memory*.
-Unstable memory is memory whose validity or type may change as a
-result of an assignment, move, or a variable going out of scope.
-There are two cases in Rust where memory is unstable: the contents of
-unique boxes and enums.
-
-Unique boxes are unstable because when the variable containing the
-unique box is re-assigned, moves, or goes out of scope, the unique box
-is freed or---in the case of a move---potentially given to another
-task.  In either case, if there is an extant and usable pointer into
-the box, then safety guarantees would be compromised.
-
-Enum values are unstable because they are reassigned the types of
-their contents may change if they are assigned with a different
-variant than they had previously.
-
-# Safety criteria that must be enforced
-
-Whenever a piece of memory is borrowed for lifetime L, there are two
-things which the borrow checker must guarantee.  First, it must
-guarantee that the memory address will remain allocated (and owned by
-the current task) for the entirety of the lifetime L.  Second, it must
-guarantee that the type of the data will not change for the entirety
-of the lifetime L.  In exchange, the region-based type system will
-guarantee that the pointer is not used outside the lifetime L.  These
-guarantees are to some extent independent but are also inter-related.
-
-In some cases, the type of a pointer cannot be invalidated but the
-lifetime can.  For example, imagine a pointer to the interior of
-a shared box like:
-
-    let mut x = @mut {f: 5, g: 6};
-    let y = &mut x.f;
-
-Here, a pointer was created to the interior of a shared box which
-contains a record.  Even if `*x` were to be mutated like so:
-
-    *x = {f: 6, g: 7};
-
-This would cause `*y` to change from 5 to 6, but the pointer pointer
-`y` remains valid.  It still points at an integer even if that integer
-has been overwritten.
-
-However, if we were to reassign `x` itself, like so:
-
-    x = @{f: 6, g: 7};
-
-This could potentially invalidate `y`, because if `x` were the final
-reference to the shared box, then that memory would be released and
-now `y` points at freed memory.  (We will see that to prevent this
-scenario we will *root* shared boxes that reside in mutable memory
-whose contents are borrowed; rooting means that we create a temporary
-to ensure that the box is not collected).
-
-In other cases, like an enum on the stack, the memory cannot be freed
-but its type can change:
-
-    let mut x = Some(5);
-    match x {
-      Some(ref y) => { ... }
-      None => { ... }
-    }
-
-Here as before, the pointer `y` would be invalidated if we were to
-reassign `x` to `none`.  (We will see that this case is prevented
-because borrowck tracks data which resides on the stack and prevents
-variables from reassigned if there may be pointers to their interior)
-
-Finally, in some cases, both dangers can arise.  For example, something
-like the following:
-
-    let mut x = ~some(5);
-    match x {
-      ~some(ref y) => { ... }
-      ~none => { ... }
-    }
-
-In this case, if `x` to be reassigned or `*x` were to be mutated, then
-the pointer `y` would be invalided.  (This case is also prevented by
-borrowck tracking data which is owned by the current stack frame)
-
-# Summary of the safety check
-
-In order to enforce mutability, the borrow check has a few tricks up
-its sleeve:
-
-- When data is owned by the current stack frame, we can identify every
-  possible assignment to a local variable and simply prevent
-  potentially dangerous assignments directly.
-
-- If data is owned by a shared box, we can root the box to increase
-  its lifetime.
-
-- If data is found within a borrowed pointer, we can assume that the
-  data will remain live for the entirety of the borrowed pointer.
-
-- We can rely on the fact that pure actions (such as calling pure
-  functions) do not mutate data which is not owned by the current
-  stack frame.
-
-# Possible future directions
-
-There are numerous ways that the `borrowck` could be strengthened, but
-these are the two most likely:
-
-- flow-sensitivity: we do not currently consider flow at all but only
-  block-scoping.  This means that innocent code like the following is
-  rejected:
-
-      let mut x: int;
-      ...
-      x = 5;
-      let y: &int = &x; // immutable ptr created
-      ...
-
-  The reason is that the scope of the pointer `y` is the entire
-  enclosing block, and the assignment `x = 5` occurs within that
-  block.  The analysis is not smart enough to see that `x = 5` always
-  happens before the immutable pointer is created.  This is relatively
-  easy to fix and will surely be fixed at some point.
-
-- finer-grained purity checks: currently, our fallback for
-  guaranteeing random references into mutable, aliasable memory is to
-  require *total purity*.  This is rather strong.  We could use local
-  type-based alias analysis to distinguish writes that could not
-  possibly invalid the references which must be guaranteed.  This
-  would only work within the function boundaries; function calls would
-  still require total purity.  This seems less likely to be
-  implemented in the short term as it would make the code
-  significantly more complex; there is currently no code to analyze
-  the types and determine the possible impacts of a write.
-
-# How the code works
-
-The borrow check code is divided into several major modules, each of
-which is documented in its own file.
-
-The `gather_loans` and `check_loans` are the two major passes of the
-analysis.  The `gather_loans` pass runs over the IR once to determine
-what memory must remain valid and for how long.  Its name is a bit of
-a misnomer; it does in fact gather up the set of loans which are
-granted, but it also determines when @T pointers must be rooted and
-for which scopes purity must be required.
-
-The `check_loans` pass walks the IR and examines the loans and purity
-requirements computed in `gather_loans`.  It checks to ensure that (a)
-the conditions of all loans are honored; (b) no contradictory loans
-were granted (for example, loaning out the same memory as mutable and
-immutable simultaneously); and (c) any purity requirements are
-honored.
-
-The remaining modules are helper modules used by `gather_loans` and
-`check_loans`:
-
-- `categorization` has the job of analyzing an expression to determine
-  what kind of memory is used in evaluating it (for example, where
-  dereferences occur and what kind of pointer is dereferenced; whether
-  the memory is mutable; etc)
-- `loan` determines when data uniquely tied to the stack frame can be
-  loaned out.
-- `preserve` determines what actions (if any) must be taken to preserve
-  aliasable data.  This is the code which decides when to root
-  an @T pointer or to require purity.
-
-# Maps that are created
-
-Borrowck results in two maps.
-
-- `root_map`: identifies those expressions or patterns whose result
-  needs to be rooted.  Conceptually the root_map maps from an
-  expression or pattern node to a `node_id` identifying the scope for
-  which the expression must be rooted (this `node_id` should identify
-  a block or call).  The actual key to the map is not an expression id,
-  however, but a `root_map_key`, which combines an expression id with a
-  deref count and is used to cope with auto-deref.
-
-- `mutbl_map`: identifies those local variables which are modified or
-  moved. This is used by trans to guarantee that such variables are
-  given a memory location and not used as immediates.
- */
-
-use syntax::ast;
-use syntax::ast::{mutability, m_mutbl, m_imm, m_const};
-use syntax::visit;
-use syntax::ast_util;
-use syntax::ast_map;
-use syntax::codemap::span;
-use util::ppaux::{ty_to_str, region_to_str, explain_region,
-                  expr_repr, note_and_explain_region};
-use std::map::{HashMap, Set};
-use std::list;
-use std::list::{List, Cons, Nil};
-use result::{Result, Ok, Err};
-use syntax::print::pprust;
-use util::common::indenter;
-use ty::to_str;
-use dvec::DVec;
-use mem_categorization::*;
-
-export check_crate, root_map, mutbl_map;
-
-fn check_crate(tcx: ty::ctxt,
-               method_map: typeck::method_map,
-               last_use_map: liveness::last_use_map,
-               crate: @ast::crate) -> (root_map, mutbl_map) {
-
-    let bccx = borrowck_ctxt_(@{tcx: tcx,
-                                method_map: method_map,
-                                last_use_map: last_use_map,
-                                root_map: root_map(),
-                                mutbl_map: HashMap(),
-                                mut loaned_paths_same: 0,
-                                mut loaned_paths_imm: 0,
-                                mut stable_paths: 0,
-                                mut req_pure_paths: 0,
-                                mut guaranteed_paths: 0});
-
-    let req_maps = gather_loans::gather_loans(bccx, crate);
-    check_loans::check_loans(bccx, req_maps, crate);
-
-    if tcx.sess.borrowck_stats() {
-        io::println(~"--- borrowck stats ---");
-        io::println(fmt!("paths requiring guarantees: %u",
-                        bccx.guaranteed_paths));
-        io::println(fmt!("paths requiring loans     : %s",
-                         make_stat(bccx, bccx.loaned_paths_same)));
-        io::println(fmt!("paths requiring imm loans : %s",
-                         make_stat(bccx, bccx.loaned_paths_imm)));
-        io::println(fmt!("stable paths              : %s",
-                         make_stat(bccx, bccx.stable_paths)));
-        io::println(fmt!("paths requiring purity    : %s",
-                         make_stat(bccx, bccx.req_pure_paths)));
-    }
-
-    return (bccx.root_map, bccx.mutbl_map);
-
-    fn make_stat(bccx: borrowck_ctxt, stat: uint) -> ~str {
-        let stat_f = stat as float;
-        let total = bccx.guaranteed_paths as float;
-        fmt!("%u (%.0f%%)", stat  , stat_f * 100f / total)
-    }
-}
-
-// ----------------------------------------------------------------------
-// Type definitions
-
-type borrowck_ctxt_ = {tcx: ty::ctxt,
-                       method_map: typeck::method_map,
-                       last_use_map: liveness::last_use_map,
-                       root_map: root_map,
-                       mutbl_map: mutbl_map,
-
-                       // Statistics:
-                       mut loaned_paths_same: uint,
-                       mut loaned_paths_imm: uint,
-                       mut stable_paths: uint,
-                       mut req_pure_paths: uint,
-                       mut guaranteed_paths: uint};
-
-enum borrowck_ctxt {
-    borrowck_ctxt_(@borrowck_ctxt_)
-}
-
-// a map mapping id's of expressions of gc'd type (@T, @[], etc) where
-// the box needs to be kept live to the id of the scope for which they
-// must stay live.
-type root_map = HashMap<root_map_key, ast::node_id>;
-
-// the keys to the root map combine the `id` of the expression with
-// the number of types that it is autodereferenced.  So, for example,
-// if you have an expression `x.f` and x has type ~@T, we could add an
-// entry {id:x, derefs:0} to refer to `x` itself, `{id:x, derefs:1}`
-// to refer to the deref of the unique pointer, and so on.
-type root_map_key = {id: ast::node_id, derefs: uint};
-
-// set of ids of local vars / formal arguments that are modified / moved.
-// this is used in trans for optimization purposes.
-type mutbl_map = std::map::HashMap<ast::node_id, ()>;
-
-// Errors that can occur"]
-enum bckerr_code {
-    err_mut_uniq,
-    err_mut_variant,
-    err_root_not_permitted,
-    err_mutbl(ast::mutability),
-    err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
-    err_out_of_scope(ty::Region, ty::Region) // superscope, subscope
-}
-
-impl bckerr_code : cmp::Eq {
-    pure fn eq(&self, other: &bckerr_code) -> bool {
-        match (*self) {
-            err_mut_uniq => {
-                match (*other) {
-                    err_mut_uniq => true,
-                    _ => false
-                }
-            }
-            err_mut_variant => {
-                match (*other) {
-                    err_mut_variant => true,
-                    _ => false
-                }
-            }
-            err_root_not_permitted => {
-                match (*other) {
-                    err_root_not_permitted => true,
-                    _ => false
-                }
-            }
-            err_mutbl(e0a) => {
-                match (*other) {
-                    err_mutbl(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            err_out_of_root_scope(e0a, e1a) => {
-                match (*other) {
-                    err_out_of_root_scope(e0b, e1b) =>
-                        e0a == e0b && e1a == e1b,
-                    _ => false
-                }
-            }
-            err_out_of_scope(e0a, e1a) => {
-                match (*other) {
-                    err_out_of_scope(e0b, e1b) => e0a == e0b && e1a == e1b,
-                    _ => false
-                }
-            }
-        }
-    }
-    pure fn ne(&self, other: &bckerr_code) -> bool { !(*self).eq(other) }
-}
-
-// Combination of an error code and the categorization of the expression
-// that caused it
-type bckerr = {cmt: cmt, code: bckerr_code};
-
-impl bckerr : cmp::Eq {
-    pure fn eq(&self, other: &bckerr) -> bool {
-        (*self).cmt == (*other).cmt && (*self).code == (*other).code
-    }
-    pure fn ne(&self, other: &bckerr) -> bool { !(*self).eq(other) }
-}
-
-// shorthand for something that fails with `bckerr` or succeeds with `T`
-type bckres<T> = Result<T, bckerr>;
-
-/// a complete record of a loan that was granted
-struct Loan {lp: @loan_path, cmt: cmt, mutbl: ast::mutability}
-
-/// maps computed by `gather_loans` that are then used by `check_loans`
-///
-/// - `req_loan_map`: map from each block/expr to the required loans needed
-///   for the duration of that block/expr
-/// - `pure_map`: map from block/expr that must be pure to the error message
-///   that should be reported if they are not pure
-type req_maps = {
-    req_loan_map: HashMap<ast::node_id, @DVec<Loan>>,
-    pure_map: HashMap<ast::node_id, bckerr>
-};
-
-fn save_and_restore<T:Copy,U>(save_and_restore_t: &mut T, f: fn() -> U) -> U {
-    let old_save_and_restore_t = *save_and_restore_t;
-    let u = f();
-    *save_and_restore_t = old_save_and_restore_t;
-    move u
-}
-
-/// Creates and returns a new root_map
-
-impl root_map_key : cmp::Eq {
-    pure fn eq(&self, other: &root_map_key) -> bool {
-        (*self).id == (*other).id && (*self).derefs == (*other).derefs
-    }
-    pure fn ne(&self, other: &root_map_key) -> bool {
-        ! ((*self) == (*other))
-    }
-}
-
-#[cfg(stage0)]
-impl root_map_key : to_bytes::IterBytes {
-    pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
-        to_bytes::iter_bytes_2(&self.id, &self.derefs, lsb0, f);
-    }
-}
-#[cfg(stage1)]
-#[cfg(stage2)]
-impl root_map_key : to_bytes::IterBytes {
-    pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
-        to_bytes::iter_bytes_2(&self.id, &self.derefs, lsb0, f);
-    }
-}
-
-fn root_map() -> root_map {
-    return HashMap();
-
-    pure fn root_map_key_eq(k1: &root_map_key, k2: &root_map_key) -> bool {
-        k1.id == k2.id && k1.derefs == k2.derefs
-    }
-
-    pure fn root_map_key_hash(k: &root_map_key) -> uint {
-        (k.id << 4) as uint | k.derefs
-    }
-}
-
-// ___________________________________________________________________________
-// Misc
-
-impl borrowck_ctxt {
-    fn is_subregion_of(r_sub: ty::Region, r_sup: ty::Region) -> bool {
-        region::is_subregion_of(self.tcx.region_map, r_sub, r_sup)
-    }
-
-    fn cat_expr(expr: @ast::expr) -> cmt {
-        cat_expr(self.tcx, self.method_map, expr)
-    }
-
-    fn cat_expr_unadjusted(expr: @ast::expr) -> cmt {
-        cat_expr_unadjusted(self.tcx, self.method_map, expr)
-    }
-
-    fn cat_expr_autoderefd(expr: @ast::expr,
-                           adj: @ty::AutoAdjustment)
-                        -> cmt {
-        cat_expr_autoderefd(self.tcx, self.method_map, expr, adj)
-    }
-
-    fn cat_def(id: ast::node_id,
-               span: span,
-               ty: ty::t,
-               def: ast::def) -> cmt {
-        cat_def(self.tcx, self.method_map, id, span, ty, def)
-    }
-
-    fn cat_variant<N: ast_node>(arg: N,
-                                enum_did: ast::def_id,
-                                cmt: cmt) -> cmt {
-        cat_variant(self.tcx, self.method_map, arg, enum_did, cmt)
-    }
-
-    fn cat_discr(cmt: cmt, alt_id: ast::node_id) -> cmt {
-        return @{cat:cat_discr(cmt, alt_id),.. *cmt};
-    }
-
-    fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
-        mc.cat_pattern(cmt, pat, op);
-    }
-
-    fn report_if_err(bres: bckres<()>) {
-        match bres {
-          Ok(()) => (),
-          Err(e) => self.report(e)
-        }
-    }
-
-    fn report(err: bckerr) {
-        self.span_err(
-            err.cmt.span,
-            fmt!("illegal borrow: %s",
-                 self.bckerr_to_str(err)));
-        self.note_and_explain_bckerr(err);
-    }
-
-    fn span_err(s: span, m: ~str) {
-        self.tcx.sess.span_err(s, m);
-    }
-
-    fn span_note(s: span, m: ~str) {
-        self.tcx.sess.span_note(s, m);
-    }
-
-    fn add_to_mutbl_map(cmt: cmt) {
-        match cmt.cat {
-          cat_local(id) | cat_arg(id) => {
-            self.mutbl_map.insert(id, ());
-          }
-          cat_stack_upvar(cmt) => {
-            self.add_to_mutbl_map(cmt);
-          }
-          _ => ()
-        }
-    }
-
-    fn bckerr_to_str(err: bckerr) -> ~str {
-        match err.code {
-            err_mutbl(req) => {
-                fmt!("creating %s alias to %s",
-                     self.mut_to_str(req),
-                     self.cmt_to_str(err.cmt))
-            }
-            err_mut_uniq => {
-                ~"unique value in aliasable, mutable location"
-            }
-            err_mut_variant => {
-                ~"enum variant in aliasable, mutable location"
-            }
-            err_root_not_permitted => {
-                // note: I don't expect users to ever see this error
-                // message, reasons are discussed in attempt_root() in
-                // preserve.rs.
-                ~"rooting is not permitted"
-            }
-            err_out_of_root_scope(*) => {
-                ~"cannot root managed value long enough"
-            }
-            err_out_of_scope(*) => {
-                ~"borrowed value does not live long enough"
-            }
-        }
-    }
-
-    fn note_and_explain_bckerr(err: bckerr) {
-        let code = err.code;
-        match code {
-            err_mutbl(*) | err_mut_uniq | err_mut_variant |
-            err_root_not_permitted => {}
-
-            err_out_of_root_scope(super_scope, sub_scope) => {
-                note_and_explain_region(
-                    self.tcx,
-                    ~"managed value would have to be rooted for ",
-                    sub_scope,
-                    ~"...");
-                note_and_explain_region(
-                    self.tcx,
-                    ~"...but can only be rooted for ",
-                    super_scope,
-                    ~"");
-            }
-
-            err_out_of_scope(super_scope, sub_scope) => {
-                note_and_explain_region(
-                    self.tcx,
-                    ~"borrowed pointer must be valid for ",
-                    sub_scope,
-                    ~"...");
-                note_and_explain_region(
-                    self.tcx,
-                    ~"...but borrowed value is only valid for ",
-                    super_scope,
-                    ~"");
-          }
-        }
-    }
-
-
-    fn cmt_to_str(cmt: cmt) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
-        mc.cmt_to_str(cmt)
-    }
-
-    fn cmt_to_repr(cmt: cmt) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
-        mc.cmt_to_repr(cmt)
-    }
-
-    fn mut_to_str(mutbl: ast::mutability) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
-        mc.mut_to_str(mutbl)
-    }
-
-    fn loan_to_repr(loan: &Loan) -> ~str {
-        fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)",
-             loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl)
-    }
-}
-
-// The inherent mutability of a component is its default mutability
-// assuming it is embedded in an immutable context.  In general, the
-// mutability can be "overridden" if the component is embedded in a
-// mutable structure.
-fn inherent_mutability(ck: comp_kind) -> mutability {
-    match ck {
-      comp_tuple | comp_anon_field | comp_variant(_) => m_imm,
-      comp_field(_, m) | comp_index(_, m)            => m
-    }
-}
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index c4411008404..6ef80321ec2 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -1,4 +1,239 @@
+/*!
+# Borrow check
+
+This pass is in job of enforcing *memory safety* and *purity*.  As
+memory safety is by far the more complex topic, I'll focus on that in
+this description, but purity will be covered later on. In the context
+of Rust, memory safety means three basic things:
+
+- no writes to immutable memory;
+- all pointers point to non-freed memory;
+- all pointers point to memory of the same type as the pointer.
+
+The last point might seem confusing: after all, for the most part,
+this condition is guaranteed by the type check.  However, there are
+two cases where the type check effectively delegates to borrow check.
+
+The first case has to do with enums.  If there is a pointer to the
+interior of an enum, and the enum is in a mutable location (such as a
+local variable or field declared to be mutable), it is possible that
+the user will overwrite the enum with a new value of a different
+variant, and thus effectively change the type of the memory that the
+pointer is pointing at.
+
+The second case has to do with mutability.  Basically, the type
+checker has only a limited understanding of mutability.  It will allow
+(for example) the user to get an immutable pointer with the address of
+a mutable local variable.  It will also allow a `@mut T` or `~mut T`
+pointer to be borrowed as a `&r.T` pointer.  These seeming oversights
+are in fact intentional; they allow the user to temporarily treat a
+mutable value as immutable.  It is up to the borrow check to guarantee
+that the value in question is not in fact mutated during the lifetime
+`r` of the reference.
+
+# Definition of unstable memory
+
+The primary danger to safety arises due to *unstable memory*.
+Unstable memory is memory whose validity or type may change as a
+result of an assignment, move, or a variable going out of scope.
+There are two cases in Rust where memory is unstable: the contents of
+unique boxes and enums.
+
+Unique boxes are unstable because when the variable containing the
+unique box is re-assigned, moves, or goes out of scope, the unique box
+is freed or---in the case of a move---potentially given to another
+task.  In either case, if there is an extant and usable pointer into
+the box, then safety guarantees would be compromised.
+
+Enum values are unstable because they are reassigned the types of
+their contents may change if they are assigned with a different
+variant than they had previously.
+
+# Safety criteria that must be enforced
+
+Whenever a piece of memory is borrowed for lifetime L, there are two
+things which the borrow checker must guarantee.  First, it must
+guarantee that the memory address will remain allocated (and owned by
+the current task) for the entirety of the lifetime L.  Second, it must
+guarantee that the type of the data will not change for the entirety
+of the lifetime L.  In exchange, the region-based type system will
+guarantee that the pointer is not used outside the lifetime L.  These
+guarantees are to some extent independent but are also inter-related.
+
+In some cases, the type of a pointer cannot be invalidated but the
+lifetime can.  For example, imagine a pointer to the interior of
+a shared box like:
+
+    let mut x = @mut {f: 5, g: 6};
+    let y = &mut x.f;
+
+Here, a pointer was created to the interior of a shared box which
+contains a record.  Even if `*x` were to be mutated like so:
+
+    *x = {f: 6, g: 7};
+
+This would cause `*y` to change from 5 to 6, but the pointer pointer
+`y` remains valid.  It still points at an integer even if that integer
+has been overwritten.
+
+However, if we were to reassign `x` itself, like so:
+
+    x = @{f: 6, g: 7};
+
+This could potentially invalidate `y`, because if `x` were the final
+reference to the shared box, then that memory would be released and
+now `y` points at freed memory.  (We will see that to prevent this
+scenario we will *root* shared boxes that reside in mutable memory
+whose contents are borrowed; rooting means that we create a temporary
+to ensure that the box is not collected).
+
+In other cases, like an enum on the stack, the memory cannot be freed
+but its type can change:
+
+    let mut x = Some(5);
+    match x {
+      Some(ref y) => { ... }
+      None => { ... }
+    }
+
+Here as before, the pointer `y` would be invalidated if we were to
+reassign `x` to `none`.  (We will see that this case is prevented
+because borrowck tracks data which resides on the stack and prevents
+variables from reassigned if there may be pointers to their interior)
+
+Finally, in some cases, both dangers can arise.  For example, something
+like the following:
+
+    let mut x = ~some(5);
+    match x {
+      ~some(ref y) => { ... }
+      ~none => { ... }
+    }
+
+In this case, if `x` to be reassigned or `*x` were to be mutated, then
+the pointer `y` would be invalided.  (This case is also prevented by
+borrowck tracking data which is owned by the current stack frame)
+
+# Summary of the safety check
+
+In order to enforce mutability, the borrow check has a few tricks up
+its sleeve:
+
+- When data is owned by the current stack frame, we can identify every
+  possible assignment to a local variable and simply prevent
+  potentially dangerous assignments directly.
+
+- If data is owned by a shared box, we can root the box to increase
+  its lifetime.
+
+- If data is found within a borrowed pointer, we can assume that the
+  data will remain live for the entirety of the borrowed pointer.
+
+- We can rely on the fact that pure actions (such as calling pure
+  functions) do not mutate data which is not owned by the current
+  stack frame.
+
+# Possible future directions
+
+There are numerous ways that the `borrowck` could be strengthened, but
+these are the two most likely:
+
+- flow-sensitivity: we do not currently consider flow at all but only
+  block-scoping.  This means that innocent code like the following is
+  rejected:
+
+      let mut x: int;
+      ...
+      x = 5;
+      let y: &int = &x; // immutable ptr created
+      ...
+
+  The reason is that the scope of the pointer `y` is the entire
+  enclosing block, and the assignment `x = 5` occurs within that
+  block.  The analysis is not smart enough to see that `x = 5` always
+  happens before the immutable pointer is created.  This is relatively
+  easy to fix and will surely be fixed at some point.
+
+- finer-grained purity checks: currently, our fallback for
+  guaranteeing random references into mutable, aliasable memory is to
+  require *total purity*.  This is rather strong.  We could use local
+  type-based alias analysis to distinguish writes that could not
+  possibly invalid the references which must be guaranteed.  This
+  would only work within the function boundaries; function calls would
+  still require total purity.  This seems less likely to be
+  implemented in the short term as it would make the code
+  significantly more complex; there is currently no code to analyze
+  the types and determine the possible impacts of a write.
+
+# How the code works
+
+The borrow check code is divided into several major modules, each of
+which is documented in its own file.
+
+The `gather_loans` and `check_loans` are the two major passes of the
+analysis.  The `gather_loans` pass runs over the IR once to determine
+what memory must remain valid and for how long.  Its name is a bit of
+a misnomer; it does in fact gather up the set of loans which are
+granted, but it also determines when @T pointers must be rooted and
+for which scopes purity must be required.
+
+The `check_loans` pass walks the IR and examines the loans and purity
+requirements computed in `gather_loans`.  It checks to ensure that (a)
+the conditions of all loans are honored; (b) no contradictory loans
+were granted (for example, loaning out the same memory as mutable and
+immutable simultaneously); and (c) any purity requirements are
+honored.
+
+The remaining modules are helper modules used by `gather_loans` and
+`check_loans`:
+
+- `categorization` has the job of analyzing an expression to determine
+  what kind of memory is used in evaluating it (for example, where
+  dereferences occur and what kind of pointer is dereferenced; whether
+  the memory is mutable; etc)
+- `loan` determines when data uniquely tied to the stack frame can be
+  loaned out.
+- `preserve` determines what actions (if any) must be taken to preserve
+  aliasable data.  This is the code which decides when to root
+  an @T pointer or to require purity.
+
+# Maps that are created
+
+Borrowck results in two maps.
+
+- `root_map`: identifies those expressions or patterns whose result
+  needs to be rooted.  Conceptually the root_map maps from an
+  expression or pattern node to a `node_id` identifying the scope for
+  which the expression must be rooted (this `node_id` should identify
+  a block or call).  The actual key to the map is not an expression id,
+  however, but a `root_map_key`, which combines an expression id with a
+  deref count and is used to cope with auto-deref.
+
+- `mutbl_map`: identifies those local variables which are modified or
+  moved. This is used by trans to guarantee that such variables are
+  given a memory location and not used as immediates.
+ */
+
 #[legacy_exports];
+
+use syntax::ast;
+use syntax::ast::{mutability, m_mutbl, m_imm, m_const};
+use syntax::visit;
+use syntax::ast_util;
+use syntax::ast_map;
+use syntax::codemap::span;
+use util::ppaux::{ty_to_str, region_to_str, explain_region,
+                  expr_repr, note_and_explain_region};
+use std::map::{HashMap, Set};
+use std::list;
+use std::list::{List, Cons, Nil};
+use result::{Result, Ok, Err};
+use syntax::print::pprust;
+use util::common::indenter;
+use ty::to_str;
+use dvec::DVec;
+use mem_categorization::*;
+
 #[legacy_exports]
 mod check_loans;
 #[legacy_exports]
@@ -7,3 +242,388 @@ mod gather_loans;
 mod loan;
 #[legacy_exports]
 mod preserve;
+
+export check_crate, root_map, mutbl_map;
+
+fn check_crate(tcx: ty::ctxt,
+               method_map: typeck::method_map,
+               last_use_map: liveness::last_use_map,
+               crate: @ast::crate) -> (root_map, mutbl_map) {
+
+    let bccx = borrowck_ctxt_(@{tcx: tcx,
+                                method_map: method_map,
+                                last_use_map: last_use_map,
+                                root_map: root_map(),
+                                mutbl_map: HashMap(),
+                                mut loaned_paths_same: 0,
+                                mut loaned_paths_imm: 0,
+                                mut stable_paths: 0,
+                                mut req_pure_paths: 0,
+                                mut guaranteed_paths: 0});
+
+    let req_maps = gather_loans::gather_loans(bccx, crate);
+    check_loans::check_loans(bccx, req_maps, crate);
+
+    if tcx.sess.borrowck_stats() {
+        io::println(~"--- borrowck stats ---");
+        io::println(fmt!("paths requiring guarantees: %u",
+                        bccx.guaranteed_paths));
+        io::println(fmt!("paths requiring loans     : %s",
+                         make_stat(bccx, bccx.loaned_paths_same)));
+        io::println(fmt!("paths requiring imm loans : %s",
+                         make_stat(bccx, bccx.loaned_paths_imm)));
+        io::println(fmt!("stable paths              : %s",
+                         make_stat(bccx, bccx.stable_paths)));
+        io::println(fmt!("paths requiring purity    : %s",
+                         make_stat(bccx, bccx.req_pure_paths)));
+    }
+
+    return (bccx.root_map, bccx.mutbl_map);
+
+    fn make_stat(bccx: borrowck_ctxt, stat: uint) -> ~str {
+        let stat_f = stat as float;
+        let total = bccx.guaranteed_paths as float;
+        fmt!("%u (%.0f%%)", stat  , stat_f * 100f / total)
+    }
+}
+
+// ----------------------------------------------------------------------
+// Type definitions
+
+type borrowck_ctxt_ = {tcx: ty::ctxt,
+                       method_map: typeck::method_map,
+                       last_use_map: liveness::last_use_map,
+                       root_map: root_map,
+                       mutbl_map: mutbl_map,
+
+                       // Statistics:
+                       mut loaned_paths_same: uint,
+                       mut loaned_paths_imm: uint,
+                       mut stable_paths: uint,
+                       mut req_pure_paths: uint,
+                       mut guaranteed_paths: uint};
+
+enum borrowck_ctxt {
+    borrowck_ctxt_(@borrowck_ctxt_)
+}
+
+// a map mapping id's of expressions of gc'd type (@T, @[], etc) where
+// the box needs to be kept live to the id of the scope for which they
+// must stay live.
+type root_map = HashMap<root_map_key, ast::node_id>;
+
+// the keys to the root map combine the `id` of the expression with
+// the number of types that it is autodereferenced.  So, for example,
+// if you have an expression `x.f` and x has type ~@T, we could add an
+// entry {id:x, derefs:0} to refer to `x` itself, `{id:x, derefs:1}`
+// to refer to the deref of the unique pointer, and so on.
+type root_map_key = {id: ast::node_id, derefs: uint};
+
+// set of ids of local vars / formal arguments that are modified / moved.
+// this is used in trans for optimization purposes.
+type mutbl_map = std::map::HashMap<ast::node_id, ()>;
+
+// Errors that can occur"]
+enum bckerr_code {
+    err_mut_uniq,
+    err_mut_variant,
+    err_root_not_permitted,
+    err_mutbl(ast::mutability),
+    err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
+    err_out_of_scope(ty::Region, ty::Region) // superscope, subscope
+}
+
+impl bckerr_code : cmp::Eq {
+    pure fn eq(&self, other: &bckerr_code) -> bool {
+        match (*self) {
+            err_mut_uniq => {
+                match (*other) {
+                    err_mut_uniq => true,
+                    _ => false
+                }
+            }
+            err_mut_variant => {
+                match (*other) {
+                    err_mut_variant => true,
+                    _ => false
+                }
+            }
+            err_root_not_permitted => {
+                match (*other) {
+                    err_root_not_permitted => true,
+                    _ => false
+                }
+            }
+            err_mutbl(e0a) => {
+                match (*other) {
+                    err_mutbl(e0b) => e0a == e0b,
+                    _ => false
+                }
+            }
+            err_out_of_root_scope(e0a, e1a) => {
+                match (*other) {
+                    err_out_of_root_scope(e0b, e1b) =>
+                        e0a == e0b && e1a == e1b,
+                    _ => false
+                }
+            }
+            err_out_of_scope(e0a, e1a) => {
+                match (*other) {
+                    err_out_of_scope(e0b, e1b) => e0a == e0b && e1a == e1b,
+                    _ => false
+                }
+            }
+        }
+    }
+    pure fn ne(&self, other: &bckerr_code) -> bool { !(*self).eq(other) }
+}
+
+// Combination of an error code and the categorization of the expression
+// that caused it
+type bckerr = {cmt: cmt, code: bckerr_code};
+
+impl bckerr : cmp::Eq {
+    pure fn eq(&self, other: &bckerr) -> bool {
+        (*self).cmt == (*other).cmt && (*self).code == (*other).code
+    }
+    pure fn ne(&self, other: &bckerr) -> bool { !(*self).eq(other) }
+}
+
+// shorthand for something that fails with `bckerr` or succeeds with `T`
+type bckres<T> = Result<T, bckerr>;
+
+/// a complete record of a loan that was granted
+struct Loan {lp: @loan_path, cmt: cmt, mutbl: ast::mutability}
+
+/// maps computed by `gather_loans` that are then used by `check_loans`
+///
+/// - `req_loan_map`: map from each block/expr to the required loans needed
+///   for the duration of that block/expr
+/// - `pure_map`: map from block/expr that must be pure to the error message
+///   that should be reported if they are not pure
+type req_maps = {
+    req_loan_map: HashMap<ast::node_id, @DVec<Loan>>,
+    pure_map: HashMap<ast::node_id, bckerr>
+};
+
+fn save_and_restore<T:Copy,U>(save_and_restore_t: &mut T, f: fn() -> U) -> U {
+    let old_save_and_restore_t = *save_and_restore_t;
+    let u = f();
+    *save_and_restore_t = old_save_and_restore_t;
+    move u
+}
+
+/// Creates and returns a new root_map
+
+impl root_map_key : cmp::Eq {
+    pure fn eq(&self, other: &root_map_key) -> bool {
+        (*self).id == (*other).id && (*self).derefs == (*other).derefs
+    }
+    pure fn ne(&self, other: &root_map_key) -> bool {
+        ! ((*self) == (*other))
+    }
+}
+
+#[cfg(stage0)]
+impl root_map_key : to_bytes::IterBytes {
+    pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
+        to_bytes::iter_bytes_2(&self.id, &self.derefs, lsb0, f);
+    }
+}
+#[cfg(stage1)]
+#[cfg(stage2)]
+impl root_map_key : to_bytes::IterBytes {
+    pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
+        to_bytes::iter_bytes_2(&self.id, &self.derefs, lsb0, f);
+    }
+}
+
+fn root_map() -> root_map {
+    return HashMap();
+
+    pure fn root_map_key_eq(k1: &root_map_key, k2: &root_map_key) -> bool {
+        k1.id == k2.id && k1.derefs == k2.derefs
+    }
+
+    pure fn root_map_key_hash(k: &root_map_key) -> uint {
+        (k.id << 4) as uint | k.derefs
+    }
+}
+
+// ___________________________________________________________________________
+// Misc
+
+impl borrowck_ctxt {
+    fn is_subregion_of(r_sub: ty::Region, r_sup: ty::Region) -> bool {
+        region::is_subregion_of(self.tcx.region_map, r_sub, r_sup)
+    }
+
+    fn cat_expr(expr: @ast::expr) -> cmt {
+        cat_expr(self.tcx, self.method_map, expr)
+    }
+
+    fn cat_expr_unadjusted(expr: @ast::expr) -> cmt {
+        cat_expr_unadjusted(self.tcx, self.method_map, expr)
+    }
+
+    fn cat_expr_autoderefd(expr: @ast::expr,
+                           adj: @ty::AutoAdjustment)
+                        -> cmt {
+        cat_expr_autoderefd(self.tcx, self.method_map, expr, adj)
+    }
+
+    fn cat_def(id: ast::node_id,
+               span: span,
+               ty: ty::t,
+               def: ast::def) -> cmt {
+        cat_def(self.tcx, self.method_map, id, span, ty, def)
+    }
+
+    fn cat_variant<N: ast_node>(arg: N,
+                                enum_did: ast::def_id,
+                                cmt: cmt) -> cmt {
+        cat_variant(self.tcx, self.method_map, arg, enum_did, cmt)
+    }
+
+    fn cat_discr(cmt: cmt, alt_id: ast::node_id) -> cmt {
+        return @{cat:cat_discr(cmt, alt_id),.. *cmt};
+    }
+
+    fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
+        let mc = &mem_categorization_ctxt {tcx: self.tcx,
+                                           method_map: self.method_map};
+        mc.cat_pattern(cmt, pat, op);
+    }
+
+    fn report_if_err(bres: bckres<()>) {
+        match bres {
+          Ok(()) => (),
+          Err(e) => self.report(e)
+        }
+    }
+
+    fn report(err: bckerr) {
+        self.span_err(
+            err.cmt.span,
+            fmt!("illegal borrow: %s",
+                 self.bckerr_to_str(err)));
+        self.note_and_explain_bckerr(err);
+    }
+
+    fn span_err(s: span, m: ~str) {
+        self.tcx.sess.span_err(s, m);
+    }
+
+    fn span_note(s: span, m: ~str) {
+        self.tcx.sess.span_note(s, m);
+    }
+
+    fn add_to_mutbl_map(cmt: cmt) {
+        match cmt.cat {
+          cat_local(id) | cat_arg(id) => {
+            self.mutbl_map.insert(id, ());
+          }
+          cat_stack_upvar(cmt) => {
+            self.add_to_mutbl_map(cmt);
+          }
+          _ => ()
+        }
+    }
+
+    fn bckerr_to_str(err: bckerr) -> ~str {
+        match err.code {
+            err_mutbl(req) => {
+                fmt!("creating %s alias to %s",
+                     self.mut_to_str(req),
+                     self.cmt_to_str(err.cmt))
+            }
+            err_mut_uniq => {
+                ~"unique value in aliasable, mutable location"
+            }
+            err_mut_variant => {
+                ~"enum variant in aliasable, mutable location"
+            }
+            err_root_not_permitted => {
+                // note: I don't expect users to ever see this error
+                // message, reasons are discussed in attempt_root() in
+                // preserve.rs.
+                ~"rooting is not permitted"
+            }
+            err_out_of_root_scope(*) => {
+                ~"cannot root managed value long enough"
+            }
+            err_out_of_scope(*) => {
+                ~"borrowed value does not live long enough"
+            }
+        }
+    }
+
+    fn note_and_explain_bckerr(err: bckerr) {
+        let code = err.code;
+        match code {
+            err_mutbl(*) | err_mut_uniq | err_mut_variant |
+            err_root_not_permitted => {}
+
+            err_out_of_root_scope(super_scope, sub_scope) => {
+                note_and_explain_region(
+                    self.tcx,
+                    ~"managed value would have to be rooted for ",
+                    sub_scope,
+                    ~"...");
+                note_and_explain_region(
+                    self.tcx,
+                    ~"...but can only be rooted for ",
+                    super_scope,
+                    ~"");
+            }
+
+            err_out_of_scope(super_scope, sub_scope) => {
+                note_and_explain_region(
+                    self.tcx,
+                    ~"borrowed pointer must be valid for ",
+                    sub_scope,
+                    ~"...");
+                note_and_explain_region(
+                    self.tcx,
+                    ~"...but borrowed value is only valid for ",
+                    super_scope,
+                    ~"");
+          }
+        }
+    }
+
+
+    fn cmt_to_str(cmt: cmt) -> ~str {
+        let mc = &mem_categorization_ctxt {tcx: self.tcx,
+                                           method_map: self.method_map};
+        mc.cmt_to_str(cmt)
+    }
+
+    fn cmt_to_repr(cmt: cmt) -> ~str {
+        let mc = &mem_categorization_ctxt {tcx: self.tcx,
+                                           method_map: self.method_map};
+        mc.cmt_to_repr(cmt)
+    }
+
+    fn mut_to_str(mutbl: ast::mutability) -> ~str {
+        let mc = &mem_categorization_ctxt {tcx: self.tcx,
+                                           method_map: self.method_map};
+        mc.mut_to_str(mutbl)
+    }
+
+    fn loan_to_repr(loan: &Loan) -> ~str {
+        fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)",
+             loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl)
+    }
+}
+
+// The inherent mutability of a component is its default mutability
+// assuming it is embedded in an immutable context.  In general, the
+// mutability can be "overridden" if the component is embedded in a
+// mutable structure.
+fn inherent_mutability(ck: comp_kind) -> mutability {
+    match ck {
+      comp_tuple | comp_anon_field | comp_variant(_) => m_imm,
+      comp_field(_, m) | comp_index(_, m)            => m
+    }
+}
diff --git a/src/librustc/middle/typeck.rs b/src/librustc/middle/typeck.rs
deleted file mode 100644
index 421c68fa9a2..00000000000
--- a/src/librustc/middle/typeck.rs
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
-
-typeck.rs, an introduction
-
-The type checker is responsible for:
-
-1. Determining the type of each expression
-2. Resolving methods and traits
-3. Guaranteeing that most type rules are met ("most?", you say, "why most?"
-   Well, dear reader, read on)
-
-The main entry point is `check_crate()`.  Type checking operates in two major
-phases: collect and check.  The collect phase passes over all items and
-determines their type, without examining their "innards".  The check phase
-then checks function bodies and so forth.
-
-Within the check phase, we check each function body one at a time (bodies of
-function expressions are checked as part of the containing function).
-Inference is used to supply types wherever they are unknown. The actual
-checking of a function itself has several phases (check, regionck, writeback),
-as discussed in the documentation for the `check` module.
-
-The type checker is defined into various submodules which are documented
-independently:
-
-- astconv: converts the AST representation of types
-  into the `ty` representation
-
-- collect: computes the types of each top-level item and enters them into
-  the `cx.tcache` table for later use
-
-- check: walks over function bodies and type checks them, inferring types for
-  local variables, type parameters, etc as necessary.
-
-- infer: finds the types to use for each type variable such that
-  all subtyping and assignment constraints are met.  In essence, the check
-  module specifies the constraints, and the infer module solves them.
-
-*/
-
-use result::Result;
-use syntax::{ast, ast_util, ast_map};
-use ast::spanned;
-use ast::{required, provided};
-use syntax::ast_map::node_id_to_str;
-use syntax::ast_util::{local_def, respan, split_trait_methods};
-use syntax::visit;
-use metadata::csearch;
-use util::common::{block_query, loop_query};
-use syntax::codemap::span;
-use pat_util::{pat_id_map, PatIdMap};
-use middle::ty;
-use middle::ty::{arg, field, node_type_table, mk_nil, ty_param_bounds_and_ty};
-use middle::ty::{ty_param_substs_and_ty, vstore_uniq};
-use std::smallintmap;
-use std::map;
-use std::map::HashMap;
-use syntax::print::pprust::*;
-use util::ppaux::{ty_to_str, tys_to_str, region_to_str,
-                  bound_region_to_str, vstore_to_str, expr_repr};
-use util::common::{indent, indenter};
-use std::list;
-use list::{List, Nil, Cons};
-use dvec::DVec;
-
-export check;
-export check_crate;
-export infer;
-export method_map;
-export method_origin;
-export method_map_entry;
-export vtable_map;
-export vtable_res;
-export vtable_origin;
-export method_static, method_param, method_trait, method_self;
-export vtable_static, vtable_param, vtable_trait;
-export provided_methods_map;
-
-#[auto_serialize]
-#[auto_deserialize]
-enum method_origin {
-    // fully statically resolved method
-    method_static(ast::def_id),
-
-    // method invoked on a type parameter with a bounded trait
-    method_param(method_param),
-
-    // method invoked on a trait instance
-    method_trait(ast::def_id, uint, ty::vstore),
-
-    // method invoked on "self" inside a default method
-    method_self(ast::def_id, uint),
-}
-
-// details for a method invoked with a receiver whose type is a type parameter
-// with a bounded trait.
-#[auto_serialize]
-#[auto_deserialize]
-type method_param = {
-    // the trait containing the method to be invoked
-    trait_id: ast::def_id,
-
-    // index of the method to be invoked amongst the trait's methods
-    method_num: uint,
-
-    // index of the type parameter (from those that are in scope) that is
-    // the type of the receiver
-    param_num: uint,
-
-    // index of the bound for this type parameter which specifies the trait
-    bound_num: uint
-};
-
-type method_map_entry = {
-    // the type and mode of the self parameter, which is not reflected
-    // in the fn type (FIXME #3446)
-    self_arg: ty::arg,
-
-    // method details being invoked
-    origin: method_origin
-};
-
-// maps from an expression id that corresponds to a method call to the details
-// of the method to be invoked
-type method_map = HashMap<ast::node_id, method_map_entry>;
-
-// Resolutions for bounds of all parameters, left to right, for a given path.
-type vtable_res = @~[vtable_origin];
-
-enum vtable_origin {
-    /*
-      Statically known vtable. def_id gives the class or impl item
-      from whence comes the vtable, and tys are the type substs.
-      vtable_res is the vtable itself
-     */
-    vtable_static(ast::def_id, ~[ty::t], vtable_res),
-    /*
-      Dynamic vtable, comes from a parameter that has a bound on it:
-      fn foo<T: quux, baz, bar>(a: T) -- a's vtable would have a
-      vtable_param origin
-
-      The first uint is the param number (identifying T in the example),
-      and the second is the bound number (identifying baz)
-     */
-    vtable_param(uint, uint),
-    /*
-      Dynamic vtable, comes from something known to have a trait
-      type. def_id refers to the trait item, tys are the substs
-     */
-    vtable_trait(ast::def_id, ~[ty::t]),
-}
-
-impl vtable_origin {
-    fn to_str(tcx: ty::ctxt) -> ~str {
-        match self {
-            vtable_static(def_id, ref tys, ref vtable_res) => {
-                fmt!("vtable_static(%?:%s, %?, %?)",
-                     def_id, ty::item_path_str(tcx, def_id),
-                     tys,
-                     vtable_res.map(|o| o.to_str(tcx)))
-            }
-
-            vtable_param(x, y) => {
-                fmt!("vtable_param(%?, %?)", x, y)
-            }
-
-            vtable_trait(def_id, ref tys) => {
-                fmt!("vtable_trait(%?:%s, %?)",
-                     def_id, ty::item_path_str(tcx, def_id),
-                     tys.map(|t| ty_to_str(tcx, *t)))
-            }
-        }
-    }
-}
-
-type vtable_map = HashMap<ast::node_id, vtable_res>;
-
-type crate_ctxt_ = {// A mapping from method call sites to traits that have
-                    // that method.
-                    trait_map: resolve::TraitMap,
-                    method_map: method_map,
-                    vtable_map: vtable_map,
-                    coherence_info: @coherence::CoherenceInfo,
-                    tcx: ty::ctxt};
-
-enum crate_ctxt {
-    crate_ctxt_(crate_ctxt_)
-}
-
-// Functions that write types into the node type table
-fn write_ty_to_tcx(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t) {
-    debug!("write_ty_to_tcx(%d, %s)", node_id, ty_to_str(tcx, ty));
-    smallintmap::insert(*tcx.node_types, node_id as uint, ty);
-}
-fn write_substs_to_tcx(tcx: ty::ctxt,
-                       node_id: ast::node_id,
-                       +substs: ~[ty::t]) {
-    if substs.len() > 0u {
-        debug!("write_substs_to_tcx(%d, %?)", node_id,
-               substs.map(|t| ty_to_str(tcx, *t)));
-        tcx.node_type_substs.insert(node_id, substs);
-    }
-}
-
-fn lookup_def_tcx(tcx: ty::ctxt, sp: span, id: ast::node_id) -> ast::def {
-    match tcx.def_map.find(id) {
-      Some(x) => x,
-      _ => {
-        tcx.sess.span_fatal(sp, ~"internal error looking up a definition")
-      }
-    }
-}
-
-fn lookup_def_ccx(ccx: @crate_ctxt, sp: span, id: ast::node_id) -> ast::def {
-    lookup_def_tcx(ccx.tcx, sp, id)
-}
-
-fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
-    {bounds: @~[], region_param: None, ty: t}
-}
-
-fn require_same_types(
-    tcx: ty::ctxt,
-    maybe_infcx: Option<infer::infer_ctxt>,
-    t1_is_expected: bool,
-    span: span,
-    t1: ty::t,
-    t2: ty::t,
-    msg: fn() -> ~str) -> bool {
-
-    let l_tcx, l_infcx;
-    match maybe_infcx {
-      None => {
-        l_tcx = tcx;
-        l_infcx = infer::new_infer_ctxt(tcx);
-      }
-      Some(i) => {
-        l_tcx = i.tcx;
-        l_infcx = i;
-      }
-    }
-
-    match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
-        result::Ok(()) => true,
-        result::Err(ref terr) => {
-            l_tcx.sess.span_err(span, msg() + ~": " +
-                                ty::type_err_to_str(l_tcx, terr));
-            ty::note_and_explain_type_err(l_tcx, terr);
-            false
-        }
-    }
-}
-
-// a list of mapping from in-scope-region-names ("isr") to the
-// corresponding ty::Region
-type isr_alist = @List<(ty::bound_region, ty::Region)>;
-
-trait get_and_find_region {
-    fn get(br: ty::bound_region) -> ty::Region;
-    fn find(br: ty::bound_region) -> Option<ty::Region>;
-}
-
-impl isr_alist: get_and_find_region {
-    fn get(br: ty::bound_region) -> ty::Region {
-        self.find(br).get()
-    }
-
-    fn find(br: ty::bound_region) -> Option<ty::Region> {
-        for list::each(self) |isr| {
-            let (isr_br, isr_r) = *isr;
-            if isr_br == br { return Some(isr_r); }
-        }
-        return None;
-    }
-}
-
-fn arg_is_argv_ty(tcx: ty::ctxt, a: ty::arg) -> bool {
-    match ty::resolved_mode(tcx, a.mode) {
-        ast::by_val => { /*ok*/ }
-        _ => {
-            return false;
-        }
-    }
-
-    match ty::get(a.ty).sty {
-      ty::ty_evec(mt, vstore_uniq) => {
-        if mt.mutbl != ast::m_imm { return false; }
-        match ty::get(mt.ty).sty {
-          ty::ty_estr(vstore_uniq) => return true,
-          _ => return false
-        }
-      }
-      _ => return false
-    }
-}
-
-fn check_main_fn_ty(ccx: @crate_ctxt,
-                    main_id: ast::node_id,
-                    main_span: span) {
-
-    let tcx = ccx.tcx;
-    let main_t = ty::node_id_to_type(tcx, main_id);
-    match ty::get(main_t).sty {
-        ty::ty_fn(fn_ty) => {
-            match tcx.items.find(main_id) {
-                Some(ast_map::node_item(it,_)) => {
-                    match it.node {
-                        ast::item_fn(_,_,ps,_) if vec::is_not_empty(ps) => {
-                            tcx.sess.span_err(
-                                main_span,
-                                ~"main function is not allowed \
-                                  to have type parameters");
-                            return;
-                        }
-                        _ => ()
-                    }
-                }
-                _ => ()
-            }
-            let mut ok = ty::type_is_nil(fn_ty.sig.output);
-            let num_args = vec::len(fn_ty.sig.inputs);
-            ok &= num_args == 0u;
-            if !ok {
-                tcx.sess.span_err(
-                    main_span,
-                    fmt!("Wrong type in main function: found `%s`, \
-                          expected `fn() -> ()`",
-                         ty_to_str(tcx, main_t)));
-            }
-        }
-        _ => {
-            tcx.sess.span_bug(main_span,
-                              ~"main has a non-function type: found `" +
-                              ty_to_str(tcx, main_t) + ~"`");
-        }
-    }
-}
-
-fn check_for_main_fn(ccx: @crate_ctxt) {
-    let tcx = ccx.tcx;
-    if !tcx.sess.building_library {
-        match copy tcx.sess.main_fn {
-          Some((id, sp)) => check_main_fn_ty(ccx, id, sp),
-          None => tcx.sess.err(~"main function not found")
-        }
-    }
-}
-
-fn check_crate(tcx: ty::ctxt,
-               trait_map: resolve::TraitMap,
-               crate: @ast::crate)
-            -> (method_map, vtable_map) {
-
-    let ccx = @crate_ctxt_({trait_map: trait_map,
-                            method_map: std::map::HashMap(),
-                            vtable_map: std::map::HashMap(),
-                            coherence_info: @coherence::CoherenceInfo(),
-                            tcx: tcx});
-    collect::collect_item_types(ccx, crate);
-    coherence::check_coherence(ccx, crate);
-    deriving::check_deriving(ccx, crate);
-
-    check::check_item_types(ccx, crate);
-    check_for_main_fn(ccx);
-    tcx.sess.abort_if_errors();
-    (ccx.method_map, ccx.vtable_map)
-}
-//
-// Local Variables:
-// mode: rust
-// fill-column: 78;
-// indent-tabs-mode: nil
-// c-basic-offset: 4
-// buffer-file-coding-system: utf-8-unix
-// End:
-//
diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs
index 11077081d91..6241cac2f5d 100644
--- a/src/librustc/middle/typeck/mod.rs
+++ b/src/librustc/middle/typeck/mod.rs
@@ -1,5 +1,83 @@
+/*
+
+typeck.rs, an introduction
+
+The type checker is responsible for:
+
+1. Determining the type of each expression
+2. Resolving methods and traits
+3. Guaranteeing that most type rules are met ("most?", you say, "why most?"
+   Well, dear reader, read on)
+
+The main entry point is `check_crate()`.  Type checking operates in two major
+phases: collect and check.  The collect phase passes over all items and
+determines their type, without examining their "innards".  The check phase
+then checks function bodies and so forth.
+
+Within the check phase, we check each function body one at a time (bodies of
+function expressions are checked as part of the containing function).
+Inference is used to supply types wherever they are unknown. The actual
+checking of a function itself has several phases (check, regionck, writeback),
+as discussed in the documentation for the `check` module.
+
+The type checker is defined into various submodules which are documented
+independently:
+
+- astconv: converts the AST representation of types
+  into the `ty` representation
+
+- collect: computes the types of each top-level item and enters them into
+  the `cx.tcache` table for later use
+
+- check: walks over function bodies and type checks them, inferring types for
+  local variables, type parameters, etc as necessary.
+
+- infer: finds the types to use for each type variable such that
+  all subtyping and assignment constraints are met.  In essence, the check
+  module specifies the constraints, and the infer module solves them.
+
+*/
+
 #[legacy_exports];
 
+use result::Result;
+use syntax::{ast, ast_util, ast_map};
+use ast::spanned;
+use ast::{required, provided};
+use syntax::ast_map::node_id_to_str;
+use syntax::ast_util::{local_def, respan, split_trait_methods};
+use syntax::visit;
+use metadata::csearch;
+use util::common::{block_query, loop_query};
+use syntax::codemap::span;
+use pat_util::{pat_id_map, PatIdMap};
+use middle::ty;
+use middle::ty::{arg, field, node_type_table, mk_nil, ty_param_bounds_and_ty};
+use middle::ty::{ty_param_substs_and_ty, vstore_uniq};
+use std::smallintmap;
+use std::map;
+use std::map::HashMap;
+use syntax::print::pprust::*;
+use util::ppaux::{ty_to_str, tys_to_str, region_to_str,
+                  bound_region_to_str, vstore_to_str, expr_repr};
+use util::common::{indent, indenter};
+use std::list;
+use list::{List, Nil, Cons};
+use dvec::DVec;
+
+export check;
+export check_crate;
+export infer;
+export method_map;
+export method_origin;
+export method_map_entry;
+export vtable_map;
+export vtable_res;
+export vtable_origin;
+export method_static, method_param, method_trait, method_self;
+export vtable_static, vtable_param, vtable_trait;
+export provided_methods_map;
+
 #[legacy_exports]
 #[merge = "check/mod.rs"]
 pub mod check;
@@ -14,3 +92,302 @@ mod collect;
 #[legacy_exports]
 mod coherence;
 mod deriving;
+
+#[auto_serialize]
+#[auto_deserialize]
+enum method_origin {
+    // fully statically resolved method
+    method_static(ast::def_id),
+
+    // method invoked on a type parameter with a bounded trait
+    method_param(method_param),
+
+    // method invoked on a trait instance
+    method_trait(ast::def_id, uint, ty::vstore),
+
+    // method invoked on "self" inside a default method
+    method_self(ast::def_id, uint),
+}
+
+// details for a method invoked with a receiver whose type is a type parameter
+// with a bounded trait.
+#[auto_serialize]
+#[auto_deserialize]
+type method_param = {
+    // the trait containing the method to be invoked
+    trait_id: ast::def_id,
+
+    // index of the method to be invoked amongst the trait's methods
+    method_num: uint,
+
+    // index of the type parameter (from those that are in scope) that is
+    // the type of the receiver
+    param_num: uint,
+
+    // index of the bound for this type parameter which specifies the trait
+    bound_num: uint
+};
+
+type method_map_entry = {
+    // the type and mode of the self parameter, which is not reflected
+    // in the fn type (FIXME #3446)
+    self_arg: ty::arg,
+
+    // method details being invoked
+    origin: method_origin
+};
+
+// maps from an expression id that corresponds to a method call to the details
+// of the method to be invoked
+type method_map = HashMap<ast::node_id, method_map_entry>;
+
+// Resolutions for bounds of all parameters, left to right, for a given path.
+type vtable_res = @~[vtable_origin];
+
+enum vtable_origin {
+    /*
+      Statically known vtable. def_id gives the class or impl item
+      from whence comes the vtable, and tys are the type substs.
+      vtable_res is the vtable itself
+     */
+    vtable_static(ast::def_id, ~[ty::t], vtable_res),
+    /*
+      Dynamic vtable, comes from a parameter that has a bound on it:
+      fn foo<T: quux, baz, bar>(a: T) -- a's vtable would have a
+      vtable_param origin
+
+      The first uint is the param number (identifying T in the example),
+      and the second is the bound number (identifying baz)
+     */
+    vtable_param(uint, uint),
+    /*
+      Dynamic vtable, comes from something known to have a trait
+      type. def_id refers to the trait item, tys are the substs
+     */
+    vtable_trait(ast::def_id, ~[ty::t]),
+}
+
+impl vtable_origin {
+    fn to_str(tcx: ty::ctxt) -> ~str {
+        match self {
+            vtable_static(def_id, ref tys, ref vtable_res) => {
+                fmt!("vtable_static(%?:%s, %?, %?)",
+                     def_id, ty::item_path_str(tcx, def_id),
+                     tys,
+                     vtable_res.map(|o| o.to_str(tcx)))
+            }
+
+            vtable_param(x, y) => {
+                fmt!("vtable_param(%?, %?)", x, y)
+            }
+
+            vtable_trait(def_id, ref tys) => {
+                fmt!("vtable_trait(%?:%s, %?)",
+                     def_id, ty::item_path_str(tcx, def_id),
+                     tys.map(|t| ty_to_str(tcx, *t)))
+            }
+        }
+    }
+}
+
+type vtable_map = HashMap<ast::node_id, vtable_res>;
+
+type crate_ctxt_ = {// A mapping from method call sites to traits that have
+                    // that method.
+                    trait_map: resolve::TraitMap,
+                    method_map: method_map,
+                    vtable_map: vtable_map,
+                    coherence_info: @coherence::CoherenceInfo,
+                    tcx: ty::ctxt};
+
+enum crate_ctxt {
+    crate_ctxt_(crate_ctxt_)
+}
+
+// Functions that write types into the node type table
+fn write_ty_to_tcx(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t) {
+    debug!("write_ty_to_tcx(%d, %s)", node_id, ty_to_str(tcx, ty));
+    smallintmap::insert(*tcx.node_types, node_id as uint, ty);
+}
+fn write_substs_to_tcx(tcx: ty::ctxt,
+                       node_id: ast::node_id,
+                       +substs: ~[ty::t]) {
+    if substs.len() > 0u {
+        debug!("write_substs_to_tcx(%d, %?)", node_id,
+               substs.map(|t| ty_to_str(tcx, *t)));
+        tcx.node_type_substs.insert(node_id, substs);
+    }
+}
+
+fn lookup_def_tcx(tcx: ty::ctxt, sp: span, id: ast::node_id) -> ast::def {
+    match tcx.def_map.find(id) {
+      Some(x) => x,
+      _ => {
+        tcx.sess.span_fatal(sp, ~"internal error looking up a definition")
+      }
+    }
+}
+
+fn lookup_def_ccx(ccx: @crate_ctxt, sp: span, id: ast::node_id) -> ast::def {
+    lookup_def_tcx(ccx.tcx, sp, id)
+}
+
+fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
+    {bounds: @~[], region_param: None, ty: t}
+}
+
+fn require_same_types(
+    tcx: ty::ctxt,
+    maybe_infcx: Option<infer::infer_ctxt>,
+    t1_is_expected: bool,
+    span: span,
+    t1: ty::t,
+    t2: ty::t,
+    msg: fn() -> ~str) -> bool {
+
+    let l_tcx, l_infcx;
+    match maybe_infcx {
+      None => {
+        l_tcx = tcx;
+        l_infcx = infer::new_infer_ctxt(tcx);
+      }
+      Some(i) => {
+        l_tcx = i.tcx;
+        l_infcx = i;
+      }
+    }
+
+    match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
+        result::Ok(()) => true,
+        result::Err(ref terr) => {
+            l_tcx.sess.span_err(span, msg() + ~": " +
+                                ty::type_err_to_str(l_tcx, terr));
+            ty::note_and_explain_type_err(l_tcx, terr);
+            false
+        }
+    }
+}
+
+// a list of mapping from in-scope-region-names ("isr") to the
+// corresponding ty::Region
+type isr_alist = @List<(ty::bound_region, ty::Region)>;
+
+trait get_and_find_region {
+    fn get(br: ty::bound_region) -> ty::Region;
+    fn find(br: ty::bound_region) -> Option<ty::Region>;
+}
+
+impl isr_alist: get_and_find_region {
+    fn get(br: ty::bound_region) -> ty::Region {
+        self.find(br).get()
+    }
+
+    fn find(br: ty::bound_region) -> Option<ty::Region> {
+        for list::each(self) |isr| {
+            let (isr_br, isr_r) = *isr;
+            if isr_br == br { return Some(isr_r); }
+        }
+        return None;
+    }
+}
+
+fn arg_is_argv_ty(tcx: ty::ctxt, a: ty::arg) -> bool {
+    match ty::resolved_mode(tcx, a.mode) {
+        ast::by_val => { /*ok*/ }
+        _ => {
+            return false;
+        }
+    }
+
+    match ty::get(a.ty).sty {
+      ty::ty_evec(mt, vstore_uniq) => {
+        if mt.mutbl != ast::m_imm { return false; }
+        match ty::get(mt.ty).sty {
+          ty::ty_estr(vstore_uniq) => return true,
+          _ => return false
+        }
+      }
+      _ => return false
+    }
+}
+
+fn check_main_fn_ty(ccx: @crate_ctxt,
+                    main_id: ast::node_id,
+                    main_span: span) {
+
+    let tcx = ccx.tcx;
+    let main_t = ty::node_id_to_type(tcx, main_id);
+    match ty::get(main_t).sty {
+        ty::ty_fn(fn_ty) => {
+            match tcx.items.find(main_id) {
+                Some(ast_map::node_item(it,_)) => {
+                    match it.node {
+                        ast::item_fn(_,_,ps,_) if vec::is_not_empty(ps) => {
+                            tcx.sess.span_err(
+                                main_span,
+                                ~"main function is not allowed \
+                                  to have type parameters");
+                            return;
+                        }
+                        _ => ()
+                    }
+                }
+                _ => ()
+            }
+            let mut ok = ty::type_is_nil(fn_ty.sig.output);
+            let num_args = vec::len(fn_ty.sig.inputs);
+            ok &= num_args == 0u;
+            if !ok {
+                tcx.sess.span_err(
+                    main_span,
+                    fmt!("Wrong type in main function: found `%s`, \
+                          expected `fn() -> ()`",
+                         ty_to_str(tcx, main_t)));
+            }
+        }
+        _ => {
+            tcx.sess.span_bug(main_span,
+                              ~"main has a non-function type: found `" +
+                              ty_to_str(tcx, main_t) + ~"`");
+        }
+    }
+}
+
+fn check_for_main_fn(ccx: @crate_ctxt) {
+    let tcx = ccx.tcx;
+    if !tcx.sess.building_library {
+        match copy tcx.sess.main_fn {
+          Some((id, sp)) => check_main_fn_ty(ccx, id, sp),
+          None => tcx.sess.err(~"main function not found")
+        }
+    }
+}
+
+fn check_crate(tcx: ty::ctxt,
+               trait_map: resolve::TraitMap,
+               crate: @ast::crate)
+            -> (method_map, vtable_map) {
+
+    let ccx = @crate_ctxt_({trait_map: trait_map,
+                            method_map: std::map::HashMap(),
+                            vtable_map: std::map::HashMap(),
+                            coherence_info: @coherence::CoherenceInfo(),
+                            tcx: tcx});
+    collect::collect_item_types(ccx, crate);
+    coherence::check_coherence(ccx, crate);
+    deriving::check_deriving(ccx, crate);
+
+    check::check_item_types(ccx, crate);
+    check_for_main_fn(ccx);
+    tcx.sess.abort_if_errors();
+    (ccx.method_map, ccx.vtable_map)
+}
+//
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
+//
diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc
index 80592fea500..14af4bc443e 100644
--- a/src/librustc/rustc.rc
+++ b/src/librustc/rustc.rc
@@ -122,8 +122,7 @@ mod middle {
     #[legacy_exports]
     #[path = "middle/resolve.rs"]
     mod resolve;
-    #[path = "middle/typeck.rs"]
-    #[merge = "middle/typeck/mod.rs"]
+    #[path = "middle/typeck/mod.rs"]
     pub mod typeck;
     #[legacy_exports]
     #[path = "middle/check_loop.rs"]
@@ -137,8 +136,7 @@ mod middle {
     #[legacy_exports]
     #[path = "middle/lint.rs"]
     mod lint;
-    #[path = "middle/borrowck.rs"]
-    #[merge = "middle/borrowck/mod.rs"]
+    #[path = "middle/borrowck/mod.rs"]
     mod borrowck;
     #[legacy_exports]
     #[path = "middle/mem_categorization.rs"]
@@ -216,10 +214,10 @@ mod back {
     mod target_strs;
 }
 
-#[merge = "metadata/mod.rs"]
+#[path = "metadata/mod.rs"]
 mod metadata;
 
-#[merge = "driver/mod.rs"]
+#[path = "driver/mod.rs"]
 mod driver;
 
 mod util {
diff --git a/src/libsyntax/ext/pipes.rs b/src/libsyntax/ext/pipes.rs
deleted file mode 100644
index b4c49b12d59..00000000000
--- a/src/libsyntax/ext/pipes.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-/*! Implementation of proto! extension.
-
-This is frequently called the pipe compiler. It handles code such as...
-
-~~~
-proto! pingpong (
-    ping: send {
-        ping -> pong
-    }
-    pong: recv {
-        pong -> ping
-    }
-)
-~~~
-
-There are several components:
-
- * The parser (libsyntax/ext/pipes/parse_proto.rs)
-   * Responsible for building an AST from a protocol specification.
-
- * The checker (libsyntax/ext/pipes/check.rs)
-   * Basic correctness checking for protocols (i.e. no undefined states, etc.)
-
- * The analyzer (libsyntax/ext/pipes/liveness.rs)
-   * Determines whether the protocol is bounded or unbounded.
-
- * The compiler (libsynatx/ext/pipes/pipec.rs)
-   * Generates a Rust AST from the protocol AST and the results of analysis.
-
-There is more documentation in each of the files referenced above.
-
-FIXME (#3072) - This is still incomplete.
-
-*/
-
-use codemap::span;
-use ext::base::ext_ctxt;
-use ast::tt_delim;
-use parse::lexer::{new_tt_reader, reader};
-use parse::parser::Parser;
-use parse::common::parser_common;
-
-use pipes::parse_proto::proto_parser;
-
-use pipes::proto::{visit, protocol};
-
-fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident,
-                tt: ~[ast::token_tree]) -> base::mac_result
-{
-    let sess = cx.parse_sess();
-    let cfg = cx.cfg();
-    let tt_rdr = new_tt_reader(cx.parse_sess().span_diagnostic,
-                               cx.parse_sess().interner, None, tt);
-    let rdr = tt_rdr as reader;
-    let rust_parser = Parser(sess, cfg, rdr.dup());
-
-    let proto = rust_parser.parse_proto(cx.str_of(id));
-
-    // check for errors
-    visit(proto, cx);
-
-    // do analysis
-    liveness::analyze(proto, cx);
-
-    // compile
-    base::mr_item(proto.compile(cx))
-}
diff --git a/src/libsyntax/ext/pipes/mod.rs b/src/libsyntax/ext/pipes/mod.rs
index 638ccad0143..b064f39eb3a 100644
--- a/src/libsyntax/ext/pipes/mod.rs
+++ b/src/libsyntax/ext/pipes/mod.rs
@@ -1,3 +1,49 @@
+/*! Implementation of proto! extension.
+
+This is frequently called the pipe compiler. It handles code such as...
+
+~~~
+proto! pingpong (
+    ping: send {
+        ping -> pong
+    }
+    pong: recv {
+        pong -> ping
+    }
+)
+~~~
+
+There are several components:
+
+ * The parser (libsyntax/ext/pipes/parse_proto.rs)
+   * Responsible for building an AST from a protocol specification.
+
+ * The checker (libsyntax/ext/pipes/check.rs)
+   * Basic correctness checking for protocols (i.e. no undefined states, etc.)
+
+ * The analyzer (libsyntax/ext/pipes/liveness.rs)
+   * Determines whether the protocol is bounded or unbounded.
+
+ * The compiler (libsynatx/ext/pipes/pipec.rs)
+   * Generates a Rust AST from the protocol AST and the results of analysis.
+
+There is more documentation in each of the files referenced above.
+
+FIXME (#3072) - This is still incomplete.
+
+*/
+
+use codemap::span;
+use ext::base::ext_ctxt;
+use ast::tt_delim;
+use parse::lexer::{new_tt_reader, reader};
+use parse::parser::Parser;
+use parse::common::parser_common;
+
+use pipes::parse_proto::proto_parser;
+
+use pipes::proto::{visit, protocol};
+
 #[legacy_exports]
 mod ast_builder;
 #[legacy_exports]
@@ -10,3 +56,27 @@ mod proto;
 mod check;
 #[legacy_exports]
 mod liveness;
+
+
+fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident,
+                tt: ~[ast::token_tree]) -> base::mac_result
+{
+    let sess = cx.parse_sess();
+    let cfg = cx.cfg();
+    let tt_rdr = new_tt_reader(cx.parse_sess().span_diagnostic,
+                               cx.parse_sess().interner, None, tt);
+    let rdr = tt_rdr as reader;
+    let rust_parser = Parser(sess, cfg, rdr.dup());
+
+    let proto = rust_parser.parse_proto(cx.str_of(id));
+
+    // check for errors
+    visit(proto, cx);
+
+    // do analysis
+    liveness::analyze(proto, cx);
+
+    // compile
+    base::mr_item(proto.compile(cx))
+}
+
diff --git a/src/libsyntax/parse.rs b/src/libsyntax/parse.rs
deleted file mode 100644
index 9d243556c03..00000000000
--- a/src/libsyntax/parse.rs
+++ /dev/null
@@ -1,196 +0,0 @@
-//! The main parser interface
-
-#[legacy_exports];
-
-export parser;
-export common;
-export lexer;
-export token;
-export comments;
-export prec;
-export classify;
-export attr;
-
-export parse_sess;
-export new_parse_sess, new_parse_sess_special_handler;
-export next_node_id;
-export new_parser_from_file, new_parser_etc_from_file;
-export new_parser_from_source_str;
-export new_parser_from_tts;
-export new_sub_parser_from_file;
-export parse_crate_from_file, parse_crate_from_crate_file;
-export parse_crate_from_source_str;
-export parse_expr_from_source_str, parse_item_from_source_str;
-export parse_stmt_from_source_str;
-export parse_tts_from_source_str;
-export parse_from_source_str;
-
-use parser::Parser;
-use attr::parser_attr;
-use common::parser_common;
-use ast::node_id;
-use util::interner;
-use diagnostic::{span_handler, mk_span_handler, mk_handler, emitter};
-use lexer::{reader, string_reader};
-use parse::token::{ident_interner, mk_ident_interner};
-use codemap::{span, CodeMap, FileMap, CharPos, BytePos};
-
-type parse_sess = @{
-    cm: @codemap::CodeMap,
-    mut next_id: node_id,
-    span_diagnostic: span_handler,
-    interner: @ident_interner,
-};
-
-fn new_parse_sess(demitter: Option<emitter>) -> parse_sess {
-    let cm = @CodeMap::new();
-    return @{cm: cm,
-             mut next_id: 1,
-             span_diagnostic: mk_span_handler(mk_handler(demitter), cm),
-             interner: mk_ident_interner(),
-            };
-}
-
-fn new_parse_sess_special_handler(sh: span_handler, cm: @codemap::CodeMap)
-    -> parse_sess {
-    return @{cm: cm,
-             mut next_id: 1,
-             span_diagnostic: sh,
-             interner: mk_ident_interner(),
-             };
-}
-
-fn parse_crate_from_file(input: &Path, cfg: ast::crate_cfg,
-                         sess: parse_sess) -> @ast::crate {
-    let p = new_crate_parser_from_file(sess, cfg, input);
-    let r = p.parse_crate_mod(cfg);
-    return r;
-}
-
-fn parse_crate_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
-                               sess: parse_sess) -> @ast::crate {
-    let p = new_parser_from_source_str(sess, cfg, name,
-                                       codemap::FssNone, source);
-    let r = p.parse_crate_mod(cfg);
-    p.abort_if_errors();
-    return r;
-}
-
-fn parse_expr_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
-                              sess: parse_sess) -> @ast::expr {
-    let p = new_parser_from_source_str(sess, cfg, name,
-                                       codemap::FssNone, source);
-    let r = p.parse_expr();
-    p.abort_if_errors();
-    return r;
-}
-
-fn parse_item_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
-                              +attrs: ~[ast::attribute],
-                              sess: parse_sess) -> Option<@ast::item> {
-    let p = new_parser_from_source_str(sess, cfg, name,
-                                       codemap::FssNone, source);
-    let r = p.parse_item(attrs);
-    p.abort_if_errors();
-    return r;
-}
-
-fn parse_stmt_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
-                              +attrs: ~[ast::attribute],
-                              sess: parse_sess) -> @ast::stmt {
-    let p = new_parser_from_source_str(sess, cfg, name,
-                                       codemap::FssNone, source);
-    let r = p.parse_stmt(attrs);
-    p.abort_if_errors();
-    return r;
-}
-
-fn parse_tts_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
-                             sess: parse_sess) -> ~[ast::token_tree] {
-    let p = new_parser_from_source_str(sess, cfg, name,
-                                       codemap::FssNone, source);
-    p.quote_depth += 1u;
-    let r = p.parse_all_token_trees();
-    p.abort_if_errors();
-    return r;
-}
-
-fn parse_from_source_str<T>(f: fn (p: Parser) -> T,
-                            name: ~str, ss: codemap::FileSubstr,
-                            source: @~str, cfg: ast::crate_cfg,
-                            sess: parse_sess)
-    -> T
-{
-    let p = new_parser_from_source_str(sess, cfg, name, ss,
-                                       source);
-    let r = f(p);
-    if !p.reader.is_eof() {
-        p.reader.fatal(~"expected end-of-string");
-    }
-    p.abort_if_errors();
-    move r
-}
-
-fn next_node_id(sess: parse_sess) -> node_id {
-    let rv = sess.next_id;
-    sess.next_id += 1;
-    // ID 0 is reserved for the crate and doesn't actually exist in the AST
-    assert rv != 0;
-    return rv;
-}
-
-fn new_parser_from_source_str(sess: parse_sess, cfg: ast::crate_cfg,
-                              +name: ~str, +ss: codemap::FileSubstr,
-                              source: @~str) -> Parser {
-    let filemap = sess.cm.new_filemap_w_substr(name, ss, source);
-    let srdr = lexer::new_string_reader(sess.span_diagnostic, filemap,
-                                        sess.interner);
-    return Parser(sess, cfg, srdr as reader);
-}
-
-fn new_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg,
-                        path: &Path) -> Result<Parser, ~str> {
-    match io::read_whole_file_str(path) {
-      result::Ok(move src) => {
-
-          let filemap = sess.cm.new_filemap(path.to_str(), @move src);
-          let srdr = lexer::new_string_reader(sess.span_diagnostic, filemap,
-                                              sess.interner);
-
-          Ok(Parser(sess, cfg, srdr as reader))
-
-      }
-      result::Err(move e) => Err(move e)
-    }
-}
-
-/// Create a new parser for an entire crate, handling errors as appropriate
-/// if the file doesn't exist
-fn new_crate_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg,
-                              path: &Path) -> Parser {
-    match new_parser_from_file(sess, cfg, path) {
-        Ok(move parser) => move parser,
-        Err(move e) => {
-            sess.span_diagnostic.handler().fatal(e)
-        }
-    }
-}
-
-/// Create a new parser based on a span from an existing parser. Handles
-/// error messages correctly when the file does not exist.
-fn new_sub_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg,
-                            path: &Path, sp: span) -> Parser {
-    match new_parser_from_file(sess, cfg, path) {
-        Ok(move parser) => move parser,
-        Err(move e) => {
-            sess.span_diagnostic.span_fatal(sp, e)
-        }
-    }
-}
-
-fn new_parser_from_tts(sess: parse_sess, cfg: ast::crate_cfg,
-                       tts: ~[ast::token_tree]) -> Parser {
-    let trdr = lexer::new_tt_reader(sess.span_diagnostic, sess.interner,
-                                    None, tts);
-    return Parser(sess, cfg, trdr as reader)
-}
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index a7c5f20fedf..c290e7cf307 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -1,3 +1,40 @@
+//! The main parser interface
+
+#[legacy_exports];
+
+export parser;
+export common;
+export lexer;
+export token;
+export comments;
+export prec;
+export classify;
+export attr;
+
+export parse_sess;
+export new_parse_sess, new_parse_sess_special_handler;
+export next_node_id;
+export new_parser_from_file, new_parser_etc_from_file;
+export new_parser_from_source_str;
+export new_parser_from_tts;
+export new_sub_parser_from_file;
+export parse_crate_from_file, parse_crate_from_crate_file;
+export parse_crate_from_source_str;
+export parse_expr_from_source_str, parse_item_from_source_str;
+export parse_stmt_from_source_str;
+export parse_tts_from_source_str;
+export parse_from_source_str;
+
+use parser::Parser;
+use attr::parser_attr;
+use common::parser_common;
+use ast::node_id;
+use util::interner;
+use diagnostic::{span_handler, mk_span_handler, mk_handler, emitter};
+use lexer::{reader, string_reader};
+use parse::token::{ident_interner, mk_ident_interner};
+use codemap::{span, CodeMap, FileMap, CharPos, BytePos};
+
 
 #[legacy_exports]
 mod lexer;
@@ -26,3 +63,164 @@ mod classify;
 /// Reporting obsolete syntax
 #[legacy_exports]
 mod obsolete;
+
+
+type parse_sess = @{
+    cm: @codemap::CodeMap,
+    mut next_id: node_id,
+    span_diagnostic: span_handler,
+    interner: @ident_interner,
+};
+
+fn new_parse_sess(demitter: Option<emitter>) -> parse_sess {
+    let cm = @CodeMap::new();
+    return @{cm: cm,
+             mut next_id: 1,
+             span_diagnostic: mk_span_handler(mk_handler(demitter), cm),
+             interner: mk_ident_interner(),
+            };
+}
+
+fn new_parse_sess_special_handler(sh: span_handler, cm: @codemap::CodeMap)
+    -> parse_sess {
+    return @{cm: cm,
+             mut next_id: 1,
+             span_diagnostic: sh,
+             interner: mk_ident_interner(),
+             };
+}
+
+fn parse_crate_from_file(input: &Path, cfg: ast::crate_cfg,
+                         sess: parse_sess) -> @ast::crate {
+    let p = new_crate_parser_from_file(sess, cfg, input);
+    let r = p.parse_crate_mod(cfg);
+    return r;
+}
+
+fn parse_crate_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
+                               sess: parse_sess) -> @ast::crate {
+    let p = new_parser_from_source_str(sess, cfg, name,
+                                       codemap::FssNone, source);
+    let r = p.parse_crate_mod(cfg);
+    p.abort_if_errors();
+    return r;
+}
+
+fn parse_expr_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
+                              sess: parse_sess) -> @ast::expr {
+    let p = new_parser_from_source_str(sess, cfg, name,
+                                       codemap::FssNone, source);
+    let r = p.parse_expr();
+    p.abort_if_errors();
+    return r;
+}
+
+fn parse_item_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
+                              +attrs: ~[ast::attribute],
+                              sess: parse_sess) -> Option<@ast::item> {
+    let p = new_parser_from_source_str(sess, cfg, name,
+                                       codemap::FssNone, source);
+    let r = p.parse_item(attrs);
+    p.abort_if_errors();
+    return r;
+}
+
+fn parse_stmt_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
+                              +attrs: ~[ast::attribute],
+                              sess: parse_sess) -> @ast::stmt {
+    let p = new_parser_from_source_str(sess, cfg, name,
+                                       codemap::FssNone, source);
+    let r = p.parse_stmt(attrs);
+    p.abort_if_errors();
+    return r;
+}
+
+fn parse_tts_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
+                             sess: parse_sess) -> ~[ast::token_tree] {
+    let p = new_parser_from_source_str(sess, cfg, name,
+                                       codemap::FssNone, source);
+    p.quote_depth += 1u;
+    let r = p.parse_all_token_trees();
+    p.abort_if_errors();
+    return r;
+}
+
+fn parse_from_source_str<T>(f: fn (p: Parser) -> T,
+                            name: ~str, ss: codemap::FileSubstr,
+                            source: @~str, cfg: ast::crate_cfg,
+                            sess: parse_sess)
+    -> T
+{
+    let p = new_parser_from_source_str(sess, cfg, name, ss,
+                                       source);
+    let r = f(p);
+    if !p.reader.is_eof() {
+        p.reader.fatal(~"expected end-of-string");
+    }
+    p.abort_if_errors();
+    move r
+}
+
+fn next_node_id(sess: parse_sess) -> node_id {
+    let rv = sess.next_id;
+    sess.next_id += 1;
+    // ID 0 is reserved for the crate and doesn't actually exist in the AST
+    assert rv != 0;
+    return rv;
+}
+
+fn new_parser_from_source_str(sess: parse_sess, cfg: ast::crate_cfg,
+                              +name: ~str, +ss: codemap::FileSubstr,
+                              source: @~str) -> Parser {
+    let filemap = sess.cm.new_filemap_w_substr(name, ss, source);
+    let srdr = lexer::new_string_reader(sess.span_diagnostic, filemap,
+                                        sess.interner);
+    return Parser(sess, cfg, srdr as reader);
+}
+
+fn new_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg,
+                        path: &Path) -> Result<Parser, ~str> {
+    match io::read_whole_file_str(path) {
+      result::Ok(move src) => {
+
+          let filemap = sess.cm.new_filemap(path.to_str(), @move src);
+          let srdr = lexer::new_string_reader(sess.span_diagnostic, filemap,
+                                              sess.interner);
+
+          Ok(Parser(sess, cfg, srdr as reader))
+
+      }
+      result::Err(move e) => Err(move e)
+    }
+}
+
+/// Create a new parser for an entire crate, handling errors as appropriate
+/// if the file doesn't exist
+fn new_crate_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg,
+                              path: &Path) -> Parser {
+    match new_parser_from_file(sess, cfg, path) {
+        Ok(move parser) => move parser,
+        Err(move e) => {
+            sess.span_diagnostic.handler().fatal(e)
+        }
+    }
+}
+
+/// Create a new parser based on a span from an existing parser. Handles
+/// error messages correctly when the file does not exist.
+fn new_sub_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg,
+                            path: &Path, sp: span) -> Parser {
+    match new_parser_from_file(sess, cfg, path) {
+        Ok(move parser) => move parser,
+        Err(move e) => {
+            sess.span_diagnostic.span_fatal(sp, e)
+        }
+    }
+}
+
+fn new_parser_from_tts(sess: parse_sess, cfg: ast::crate_cfg,
+                       tts: ~[ast::token_tree]) -> Parser {
+    let trdr = lexer::new_tt_reader(sess.span_diagnostic, sess.interner,
+                                    None, tts);
+    return Parser(sess, cfg, trdr as reader)
+}
diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc
index d5a28a716ec..60036fe7737 100644
--- a/src/libsyntax/syntax.rc
+++ b/src/libsyntax/syntax.rc
@@ -44,7 +44,7 @@ mod util {
     mod interner;
 }
 
-#[merge = "parse/mod.rs"]
+#[path = "parse/mod.rs"]
 mod parse;
 
 mod print {
@@ -118,8 +118,7 @@ mod ext {
     mod source_util;
 
     #[legacy_exports]
-    #[path = "ext/pipes.rs"]
-    #[merge = "ext/pipes/mod.rs"]
+    #[path = "ext/pipes/mod.rs"]
     mod pipes;
 
     #[legacy_exports]