diff --git a/src/librustc/dep_graph/README.md b/src/librustc/dep_graph/README.md
new file mode 100644
index 0000000000000..21742d9935dc2
--- /dev/null
+++ b/src/librustc/dep_graph/README.md
@@ -0,0 +1,390 @@
+# Dependency graph for incremental compilation
+
+This module contains the infrastructure for managing the incremental
+compilation dependency graph. This README aims to explain how it ought
+to be used. In this document, we'll first explain the overall
+strategy, and then share some tips for handling specific scenarios.
+
+The high-level idea is that we want to instrument the compiler to
+track which parts of the AST and other IR are read/written by what.
+This way, when we come back later, we can look at this graph and
+determine what work needs to be redone.
+
+### The dependency graph
+
+The nodes of the graph are defined by the enum `DepNode`. They represent
+one of three things:
+
+1. HIR nodes (like `Hir(DefId)`) represent the HIR input itself.
+2. Data nodes (like `ItemSignature(DefId)`) represent some computed
+   information about a particular item.
+3. Procedure notes (like `CoherenceCheckImpl(DefId)`) represent some
+   procedure that is executing. Usually this procedure is
+   performing some kind of check for errors. You can think of them as
+   computed values where the value being computed is `()` (and the
+   value may fail to be computed, if an error results).
+
+An edge `N1 -> N2` is added between two nodes if either:
+
+- the value of `N1` is used to compute `N2`;
+- `N1` is read by the procedure `N2`;
+- the procedure `N1` writes the value `N2`.
+
+The latter two conditions are equivalent to the first one if you think
+of procedures as values.
+
+### Basic tracking
+
+There is a very general strategy to ensure that you have a correct, if
+sometimes overconservative, dependency graph. The two main things you have
+to do are (a) identify shared state and (b) identify the current tasks.
+
+### Identifying shared state
+
+Identify "shared state" that will be written by one pass and read by
+another. In particular, we need to identify shared state that will be
+read "across items" -- that is, anything where changes in one item
+could invalidate work done for other items. So, for example:
+
+1. The signature for a function is "shared state".
+2. The computed type of some expression in the body of a function is
+   not shared state, because if it changes it does not itself
+   invalidate other functions (though it may be that it causes new
+   monomorphizations to occur, but that's handled independently).
+   
+Put another way: if the HIR for an item changes, we are going to
+recompile that item for sure. But we need the dep tracking map to tell
+us what *else* we have to recompile. Shared state is anything that is
+used to communicate results from one item to another.
+
+### Identifying the current task
+
+The dep graph always tracks a current task: this is basically the
+`DepNode` that the compiler is computing right now. Typically it would
+be a procedure node, but it can also be a data node (as noted above,
+the two are kind of equivalent).
+
+You set the current task by calling `dep_graph.in_task(node)`. For example:
+
+```rust
+let _task = tcx.dep_graph.in_task(DepNode::Privacy);
+```
+
+Now all the code until `_task` goes out of scope will be considered
+part of the "privacy task".
+
+The tasks are maintained in a stack, so it is perfectly fine to nest
+one task within another. Because pushing a task is considered to be
+computing a value, when you nest a task `N2` inside of a task `N1`, we
+automatically add an edge `N2 -> N1` (since `N1` presumably needed the
+result of `N2` to complete):
+
+```rust
+let _n1 = tcx.dep_graph.in_task(DepNode::N1);
+let _n2 = tcx.dep_graph.in_task(DepNode::N2);
+// this will result in an edge N1 -> n2
+```
+
+### Ignore tasks
+
+Although it is rarely needed, you can also push a special "ignore"
+task:
+
+```rust
+let _ignore = tc.dep_graph.in_ignore();
+```
+
+This will cause all read/write edges to be ignored until it goes out
+of scope or until something else is pushed. For example, we could
+suppress the edge between nested tasks like so:
+
+```rust
+let _n1 = tcx.dep_graph.in_task(DepNode::N1);
+let _ignore = tcx.dep_graph.in_ignore();
+let _n2 = tcx.dep_graph.in_task(DepNode::N2);
+// now no edge is added
+```
+
+### Tracking reads and writes
+
+We need to identify what shared state is read/written by the current
+task as it executes. The most fundamental way of doing that is to invoke
+the `read` and `write` methods on `DepGraph`:
+
+```rust
+// Adds an edge from DepNode::Hir(some_def_id) to the current task
+tcx.dep_graph.read(DepNode::Hir(some_def_id))
+
+// Adds an edge from the current task to DepNode::ItemSignature(some_def_id)
+tcx.dep_graph.write(DepNode::ItemSignature(some_def_id))
+```
+
+However, you should rarely need to invoke those methods directly.
+Instead, the idea is to *encapsulate* shared state into some API that
+will invoke `read` and `write` automatically. The most common way to
+do this is to use a `DepTrackingMap`, described in the next section,
+but any sort of abstraction barrier will do. In general, the strategy
+is that getting access to information implicitly adds an appropriate
+`read`. So, for example, when you use the
+`dep_graph::visit_all_items_in_krate` helper method, it will visit
+each item `X`, start a task `Foo(X)` for that item, and automatically
+add an edge `Hir(X) -> Foo(X)`. This edge is added because the code is
+being given access to the HIR node for `X`, and hence it is expected
+to read from it. Similarly, reading from the `tcache` map for item `X`
+(which is a `DepTrackingMap`, described below) automatically invokes
+`dep_graph.read(ItemSignature(X))`.
+
+To make this strategy work, a certain amount of indirection is
+required. For example, modules in the HIR do not have direct pointers
+to the items that they contain. Rather, they contain node-ids -- one
+can then ask the HIR map for the item with a given node-id. This gives
+us an opportunity to add an appropriate read edge.
+
+#### Explicit calls to read and write when starting a new subtask
+
+One time when you *may* need to call `read` and `write` directly is
+when you push a new task onto the stack, either by calling `in_task`
+as shown above or indirectly, such as with the `memoize` pattern
+described below. In that case, any data that the task has access to
+from the surrounding environment must be explicitly "read". For
+example, in `librustc_typeck`, the collection code visits all items
+and, among other things, starts a subtask producing its signature
+(what follows is simplified pseudocode, of course):
+
+```rust
+fn visit_item(item: &hir::Item) {
+    // Here, current subtask is "Collect(X)", and an edge Hir(X) -> Collect(X)
+    // has automatically been added by `visit_all_items_in_krate`.
+    let sig = signature_of_item(item);
+}
+
+fn signature_of_item(item: &hir::Item) {
+    let def_id = tcx.map.local_def_id(item.id);
+    let task = tcx.dep_graph.in_task(DepNode::ItemSignature(def_id));
+    tcx.dep_graph.read(DepNode::Hir(def_id)); // <-- the interesting line
+    ...
+}
+```
+
+Here you can see that, in `signature_of_item`, we started a subtask
+corresponding to producing the `ItemSignature`. This subtask will read from
+`item` -- but it gained access to `item` implicitly. This means that if it just
+reads from `item`, there would be missing edges in the graph:
+
+    Hir(X) --+ // added by the explicit call to `read`
+      |      |
+      |      +---> ItemSignature(X) -> Collect(X)
+      |                                 ^
+      |                                 |
+      +---------------------------------+ // added by `visit_all_items_in_krate`
+    
+In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only
+present because we called `read` ourselves when entering the `ItemSignature(X)`
+task.
+
+So, the rule of thumb: when entering a new task yourself, register
+reads on any shared state that you inherit. (This actually comes up
+fairly infrequently though: the main place you need caution is around
+memoization.)
+
+#### Dependency tracking map
+
+`DepTrackingMap` is a particularly convenient way to correctly store
+shared state. A `DepTrackingMap` is a special hashmap that will add
+edges automatically when `get` and `insert` are called. The idea is
+that, when you get/insert a value for the key `K`, we will add an edge
+from/to the node `DepNode::Variant(K)` (for some variant specific to
+the map).
+
+Each `DepTrackingMap` is parameterized by a special type `M` that
+implements `DepTrackingMapConfig`; this trait defines the key and value
+types of the map, and also defines a fn for converting from the key to
+a `DepNode` label. You don't usually have to muck about with this by
+hand, there is a macro for creating it. You can see the complete set
+of `DepTrackingMap` definitions in `librustc/middle/ty/maps.rs`.
+
+As an example, let's look at the `adt_defs` map. The `adt_defs` map
+maps from the def-id of a struct/enum to its `AdtDef`. It is defined
+using this macro:
+
+```rust
+dep_map_ty! { AdtDefs: ItemSignature(DefId) -> ty::AdtDefMaster<'tcx> }
+//            ~~~~~~~  ~~~~~~~~~~~~~ ~~~~~     ~~~~~~~~~~~~~~~~~~~~~~
+//               |           |      Key type       Value type
+//               |    DepNode variant
+//      Name of map id type
+```
+
+this indicates that a map id type `AdtDefs` will be created. The key
+of the map will be a `DefId` and value will be
+`ty::AdtDefMaster<'tcx>`. The `DepNode` will be created by
+`DepNode::ItemSignature(K)` for a given key.
+
+Once that is done, you can just use the `DepTrackingMap` like any
+other map:
+
+```rust
+let mut map: DepTrackingMap<M> = DepTrackingMap::new(dep_graph);
+map.insert(key, value); // registers dep_graph.write
+map.get(key; // registers dep_graph.read
+```
+
+#### Memoization
+
+One particularly interesting case is memoization. If you have some
+shared state that you compute in a memoized fashion, the correct thing
+to do is to define a `RefCell<DepTrackingMap>` for it and use the
+`memoize` helper:
+
+```rust
+map.memoize(key, || /* compute value */)
+```
+
+This will create a graph that looks like
+
+    ... -> MapVariant(key) -> CurrentTask
+
+where `MapVariant` is the `DepNode` variant that the map is associated with,
+and `...` are whatever edges the `/* compute value */` closure creates.
+
+In particular, using the memoize helper is much better than writing
+the obvious code yourself:
+
+```
+if let Some(result) = map.get(key) {
+    return result;
+}
+let value = /* compute value */;
+map.insert(key, value);
+```
+
+If you write that code manually, the dependency graph you get will
+include artificial edges that are not necessary. For example, imagine that
+two tasks, A and B, both invoke the manual memoization code, but A happens
+to go first. The resulting graph will be:
+
+    ... -> A -> MapVariant(key) -> B
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~       // caused by A writing to MapVariant(key)
+                ~~~~~~~~~~~~~~~~~~~~  // caused by B reading from MapVariant(key)
+
+This graph is not *wrong*, but it encodes a path from A to B that
+should not exist.  In contrast, using the memoized helper, you get:
+
+    ... -> MapVariant(key) -> A
+                 |
+                 +----------> B
+                 
+which is much cleaner.                 
+
+**Be aware though that the closure is executed with `MapVariant(key)`
+pushed onto the stack as the current task!** That means that you must
+add explicit `read` calls for any shared state that it accesses
+implicitly from its environment. See the section on "explicit calls to
+read and write when starting a new subtask" above for more details.
+
+### How to decide where to introduce a new task
+
+Certainly, you need at least one task on the stack: any attempt to
+`read` or `write` shared state will panic if there is no current
+task. But where does it make sense to introduce subtasks? The basic
+rule is that a subtask makes sense for any discrete unit of work you
+may want to skip in the future. Adding a subtask separates out the
+reads/writes from *that particular subtask* versus the larger
+context. An example: you might have a 'meta' task for all of borrow
+checking, and then subtasks for borrow checking individual fns.  (Seen
+in this light, memoized computations are just a special case where we
+may want to avoid redoing the work even within the context of one
+compilation.)
+
+The other case where you might want a subtask is to help with refining
+the reads/writes for some later bit of work that needs to be memoized.
+For example, we create a subtask for type-checking the body of each
+fn.  However, in the initial version of incr. comp. at least, we do
+not expect to actually *SKIP* type-checking -- we only expect to skip
+trans. However, it's still useful to create subtasks for type-checking
+individual items, because, otherwise, if a fn sig changes, we won't
+know which callers are affected -- in fact, because the graph would be
+so coarse, we'd just have to retrans everything, since we can't
+distinguish which fns used which fn sigs.
+
+### Testing the dependency graph
+
+There are various ways to write tests against the dependency graph.
+The simplest mechanism are the
+`#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]`
+annotations. These are used in compile-fail tests to test whether the
+expected set of paths exist in the dependency graph. As an example,
+see `src/test/compile-fail/dep-graph-caller-callee.rs`.
+
+The idea is that you can annotate a test like:
+
+```rust
+#[rustc_if_this_changed]
+fn foo() { }
+
+#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+fn bar() { foo(); }
+
+#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+fn baz() { }
+```
+
+This will check whether there is a path in the dependency graph from
+`Hir(foo)` to `TypeckItemBody(bar)`. An error is reported for each
+`#[rustc_then_this_would_need]` annotation that indicates whether a
+path exists. `//~ ERROR` annotations can then be used to test if a
+path is found (as demonstrated above).
+
+### Debugging the dependency graph
+
+The compiler is also capable of dumping the dependency graph for your
+debugging pleasure. To do so, pass the `-Z dump-dep-graph` flag. The
+graph will be dumped to `dep_graph.{txt,dot}` in the current
+directory.  You can override the filename with the `RUST_DEP_GRAPH`
+environment variable.
+
+Frequently, though, the full dep graph is quite overwhelming and not
+particularly helpful. Therefore, the compiler also allows you to filter
+the graph. You can filter in three ways:
+
+1. All edges originating in a particular set of nodes (usually a single node).
+2. All edges reaching a particular set of nodes.
+3. All edges that lie between given start and end nodes.
+
+To filter, use the `RUST_DEP_GRAPH_FILTER` environment variable, which should
+look like one of the following:
+
+```
+source_filter     // nodes originating from source_filter
+-> target_filter  // nodes that can reach target_filter
+source_filter -> target_filter // nodes in between source_filter and target_filter
+```
+
+`source_filter` and `target_filter` are a `&`-separated list of strings.
+A node is considered to match a filter if all of those strings appear in its
+label. So, for example:
+
+```
+RUST_DEP_GRAPH_FILTER='-> TypeckItemBody'
+```
+
+would select the predecessors of all `TypeckItemBody` nodes. Usually though you
+want the `TypeckItemBody` node for some particular fn, so you might write:
+
+```
+RUST_DEP_GRAPH_FILTER='-> TypeckItemBody & bar'
+```
+
+This will select only the `TypeckItemBody` nodes for fns with `bar` in their name.
+
+Perhaps you are finding that when you change `foo` you need to re-type-check `bar`,
+but you don't think you should have to. In that case, you might do:
+
+```
+RUST_DEP_GRAPH_FILTER='Hir&foo -> TypeckItemBody & bar'
+```
+
+This will dump out all the nodes that lead from `Hir(foo)` to
+`TypeckItemBody(bar)`, from which you can (hopefully) see the source
+of the erroneous edge.
+
diff --git a/src/librustc/dep_graph/dep_tracking_map.rs b/src/librustc/dep_graph/dep_tracking_map.rs
new file mode 100644
index 0000000000000..c49e64f0f543b
--- /dev/null
+++ b/src/librustc/dep_graph/dep_tracking_map.rs
@@ -0,0 +1,137 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc_data_structures::fnv::FnvHashMap;
+use std::cell::RefCell;
+use std::ops::Index;
+use std::hash::Hash;
+use std::marker::PhantomData;
+use util::common::MemoizationMap;
+
+use super::{DepNode, DepGraph};
+
+/// A DepTrackingMap offers a subset of the `Map` API and ensures that
+/// we make calls to `read` and `write` as appropriate. We key the
+/// maps with a unique type for brevity.
+pub struct DepTrackingMap<M: DepTrackingMapConfig> {
+    phantom: PhantomData<M>,
+    graph: DepGraph,
+    map: FnvHashMap<M::Key, M::Value>,
+}
+
+pub trait DepTrackingMapConfig {
+    type Key: Eq + Hash + Clone;
+    type Value: Clone;
+    fn to_dep_node(key: &Self::Key) -> DepNode;
+}
+
+impl<M: DepTrackingMapConfig> DepTrackingMap<M> {
+    pub fn new(graph: DepGraph) -> DepTrackingMap<M> {
+        DepTrackingMap {
+            phantom: PhantomData,
+            graph: graph,
+            map: FnvHashMap()
+        }
+    }
+
+    /// Registers a (synthetic) read from the key `k`. Usually this
+    /// is invoked automatically by `get`.
+    fn read(&self, k: &M::Key) {
+        let dep_node = M::to_dep_node(k);
+        self.graph.read(dep_node);
+    }
+
+    /// Registers a (synthetic) write to the key `k`. Usually this is
+    /// invoked automatically by `insert`.
+    fn write(&self, k: &M::Key) {
+        let dep_node = M::to_dep_node(k);
+        self.graph.write(dep_node);
+    }
+
+    pub fn get(&self, k: &M::Key) -> Option<&M::Value> {
+        self.read(k);
+        self.map.get(k)
+    }
+
+    pub fn insert(&mut self, k: M::Key, v: M::Value) -> Option<M::Value> {
+        self.write(&k);
+        self.map.insert(k, v)
+    }
+
+    pub fn contains_key(&self, k: &M::Key) -> bool {
+        self.read(k);
+        self.map.contains_key(k)
+    }
+}
+
+impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> {
+    type Key = M::Key;
+    type Value = M::Value;
+
+    /// Memoizes an entry in the dep-tracking-map. If the entry is not
+    /// already present, then `op` will be executed to compute its value.
+    /// The resulting dependency graph looks like this:
+    ///
+    ///     [op] -> Map(key) -> CurrentTask
+    ///
+    /// Here, `[op]` represents whatever nodes `op` reads in the
+    /// course of execution; `Map(key)` represents the node for this
+    /// map; and `CurrentTask` represents the current task when
+    /// `memoize` is invoked.
+    ///
+    /// **Important:* when `op` is invoked, the current task will be
+    /// switched to `Map(key)`. Therefore, if `op` makes use of any
+    /// HIR nodes or shared state accessed through its closure
+    /// environment, it must explicitly register a read of that
+    /// state. As an example, see `type_scheme_of_item` in `collect`,
+    /// which looks something like this:
+    ///
+    /// ```
+    /// fn type_scheme_of_item(..., item: &hir::Item) -> ty::TypeScheme<'tcx> {
+    ///     let item_def_id = ccx.tcx.map.local_def_id(it.id);
+    ///     ccx.tcx.tcache.memoized(item_def_id, || {
+    ///         ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // (*)
+    ///         compute_type_scheme_of_item(ccx, item)
+    ///     });
+    /// }
+    /// ```
+    ///
+    /// The key is the line marked `(*)`: the closure implicitly
+    /// accesses the body of the item `item`, so we register a read
+    /// from `Hir(item_def_id)`.
+    fn memoize<OP>(&self, key: M::Key, op: OP) -> M::Value
+        where OP: FnOnce() -> M::Value
+    {
+        let graph;
+        {
+            let this = self.borrow();
+            if let Some(result) = this.map.get(&key) {
+                this.read(&key);
+                return result.clone();
+            }
+            graph = this.graph.clone();
+        }
+
+        let _task = graph.in_task(M::to_dep_node(&key));
+        let result = op();
+        self.borrow_mut().map.insert(key, result.clone());
+        result
+    }
+}
+
+impl<'k, M: DepTrackingMapConfig> Index<&'k M::Key> for DepTrackingMap<M> {
+    type Output = M::Value;
+
+    #[inline]
+    fn index(&self, k: &'k M::Key) -> &M::Value {
+        self.get(k).unwrap()
+    }
+}
+
diff --git a/src/librustc/dep_graph/edges.rs b/src/librustc/dep_graph/edges.rs
new file mode 100644
index 0000000000000..4b25285c476c4
--- /dev/null
+++ b/src/librustc/dep_graph/edges.rs
@@ -0,0 +1,162 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc_data_structures::fnv::{FnvHashMap, FnvHashSet};
+use super::{DepGraphQuery, DepNode};
+
+pub struct DepGraphEdges {
+    nodes: Vec<DepNode>,
+    indices: FnvHashMap<DepNode, IdIndex>,
+    edges: FnvHashSet<(IdIndex, IdIndex)>,
+    open_nodes: Vec<OpenNode>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+struct IdIndex {
+    index: u32
+}
+
+impl IdIndex {
+    fn new(v: usize) -> IdIndex {
+        assert!((v & 0xFFFF_FFFF) == v);
+        IdIndex { index: v as u32 }
+    }
+
+    fn index(self) -> usize {
+        self.index as usize
+    }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+enum OpenNode {
+    Node(IdIndex),
+    Ignore,
+}
+
+impl DepGraphEdges {
+    pub fn new() -> DepGraphEdges {
+        DepGraphEdges {
+            nodes: vec![],
+            indices: FnvHashMap(),
+            edges: FnvHashSet(),
+            open_nodes: Vec::new()
+        }
+    }
+
+    fn id(&self, index: IdIndex) -> DepNode {
+        self.nodes[index.index()]
+    }
+
+    /// Creates a node for `id` in the graph.
+    fn make_node(&mut self, id: DepNode) -> IdIndex {
+        if let Some(&i) = self.indices.get(&id) {
+            return i;
+        }
+
+        let index = IdIndex::new(self.nodes.len());
+        self.nodes.push(id.clone());
+        self.indices.insert(id, index);
+        index
+    }
+
+    /// Top of the stack of open nodes.
+    fn current_node(&self) -> Option<OpenNode> {
+        self.open_nodes.last().cloned()
+    }
+
+    pub fn push_ignore(&mut self) {
+        self.open_nodes.push(OpenNode::Ignore);
+    }
+
+    pub fn pop_ignore(&mut self) {
+        let popped_node = self.open_nodes.pop().unwrap();
+        assert_eq!(popped_node, OpenNode::Ignore);
+    }
+
+    pub fn push_task(&mut self, key: DepNode) {
+        let top_node = self.current_node();
+
+        let new_node = self.make_node(key);
+        self.open_nodes.push(OpenNode::Node(new_node));
+
+        // if we are in the midst of doing task T, then this new task
+        // N is a subtask of T, so add an edge N -> T.
+        if let Some(top_node) = top_node {
+            self.add_edge_from_open_node(top_node, |t| (new_node, t));
+        }
+    }
+
+    pub fn pop_task(&mut self, key: DepNode) {
+        let popped_node = self.open_nodes.pop().unwrap();
+        assert_eq!(OpenNode::Node(self.indices[&key]), popped_node);
+    }
+
+    /// Indicates that the current task `C` reads `v` by adding an
+    /// edge from `v` to `C`. If there is no current task, panics. If
+    /// you want to suppress this edge, use `ignore`.
+    pub fn read(&mut self, v: DepNode) {
+        let source = self.make_node(v);
+        self.add_edge_from_current_node(|current| (source, current))
+    }
+
+    /// Indicates that the current task `C` writes `v` by adding an
+    /// edge from `C` to `v`. If there is no current task, panics. If
+    /// you want to suppress this edge, use `ignore`.
+    pub fn write(&mut self, v: DepNode) {
+        let target = self.make_node(v);
+        self.add_edge_from_current_node(|current| (current, target))
+    }
+
+    /// Invoke `add_edge_from_open_node` with the top of the stack, or
+    /// panic if stack is empty.
+    fn add_edge_from_current_node<OP>(&mut self,
+                                      op: OP)
+        where OP: FnOnce(IdIndex) -> (IdIndex, IdIndex)
+    {
+        match self.current_node() {
+            Some(open_node) => self.add_edge_from_open_node(open_node, op),
+            None => panic!("no current node, cannot add edge into dependency graph")
+        }
+    }
+
+    /// Adds an edge to or from the `open_node`, assuming `open_node`
+    /// is not `Ignore`. The direction of the edge is determined by
+    /// the closure `op` --- we pass as argument the open node `n`,
+    /// and the closure returns a (source, target) tuple, which should
+    /// include `n` in one spot or another.
+    fn add_edge_from_open_node<OP>(&mut self,
+                                   open_node: OpenNode,
+                                   op: OP)
+        where OP: FnOnce(IdIndex) -> (IdIndex, IdIndex)
+    {
+        let (source, target) = match open_node {
+            OpenNode::Node(n) => op(n),
+            OpenNode::Ignore => { return; }
+        };
+
+        // ignore trivial self edges, which are not very interesting
+        if source == target {
+            return;
+        }
+
+        if self.edges.insert((source, target)) {
+            debug!("adding edge from {:?} to {:?}",
+                   self.id(source),
+                   self.id(target));
+        }
+    }
+
+    pub fn query(&self) -> DepGraphQuery {
+        let edges: Vec<_> = self.edges.iter()
+                                      .map(|&(i, j)| (self.id(i), self.id(j)))
+                                      .collect();
+        DepGraphQuery::new(&self.nodes, &edges)
+    }
+}
diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs
new file mode 100644
index 0000000000000..9bf0a79115e78
--- /dev/null
+++ b/src/librustc/dep_graph/mod.rs
@@ -0,0 +1,196 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use self::thread::{DepGraphThreadData, DepMessage};
+use middle::def_id::DefId;
+use middle::ty;
+use middle::ty::fast_reject::SimplifiedType;
+use rustc_front::hir;
+use rustc_front::intravisit::Visitor;
+use std::rc::Rc;
+
+mod dep_tracking_map;
+mod edges;
+mod query;
+mod raii;
+mod thread;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum DepNode {
+    // Represents the `Krate` as a whole (the `hir::Krate` value) (as
+    // distinct from the krate module). This is basically a hash of
+    // the entire krate, so if you read from `Krate` (e.g., by calling
+    // `tcx.map.krate()`), we will have to assume that any change
+    // means that you need to be recompiled. This is because the
+    // `Krate` value gives you access to all other items. To avoid
+    // this fate, do not call `tcx.map.krate()`; instead, prefer
+    // wrappers like `tcx.visit_all_items_in_krate()`.  If there is no
+    // suitable wrapper, you can use `tcx.dep_graph.ignore()` to gain
+    // access to the krate, but you must remember to add suitable
+    // edges yourself for the individual items that you read.
+    Krate,
+
+    // Represents the HIR node with the given node-id
+    Hir(DefId),
+
+    // Represents different phases in the compiler.
+    CollectItem(DefId),
+    Coherence,
+    CoherenceCheckImpl(DefId),
+    CoherenceOverlapCheck(DefId),
+    CoherenceOverlapCheckSpecial(DefId),
+    CoherenceOrphanCheck(DefId),
+    Variance,
+    WfCheck(DefId),
+    TypeckItemType(DefId),
+    TypeckItemBody(DefId),
+    Dropck,
+    DropckImpl(DefId),
+    CheckConst(DefId),
+    Privacy,
+    IntrinsicCheck(DefId),
+    MatchCheck(DefId),
+    MirMapConstruction(DefId),
+    BorrowCheck(DefId),
+    RvalueCheck(DefId),
+    Reachability,
+    DeadCheck,
+    StabilityCheck,
+    LateLintCheck,
+    IntrinsicUseCheck,
+    TransCrate,
+    TransCrateItem(DefId),
+    TransInlinedItem(DefId),
+    TransWriteMetadata,
+
+    // Nodes representing bits of computed IR in the tcx. Each shared
+    // table in the tcx (or elsewhere) maps to one of these
+    // nodes. Often we map multiple tables to the same node if there
+    // is no point in distinguishing them (e.g., both the type and
+    // predicates for an item wind up in `ItemSignature`). Other
+    // times, such as `ImplItems` vs `TraitItemDefIds`, tables which
+    // might be mergable are kept distinct because the sets of def-ids
+    // to which they apply are disjoint, and hence we might as well
+    // have distinct labels for easier debugging.
+    ImplOrTraitItems(DefId),
+    ItemSignature(DefId),
+    FieldTy(DefId),
+    TraitItemDefIds(DefId),
+    InherentImpls(DefId),
+    ImplItems(DefId),
+
+    // The set of impls for a given trait. Ultimately, it would be
+    // nice to get more fine-grained here (e.g., to include a
+    // simplified type), but we can't do that until we restructure the
+    // HIR to distinguish the *header* of an impl from its body.  This
+    // is because changes to the header may change the self-type of
+    // the impl and hence would require us to be more conservative
+    // than changes in the impl body.
+    TraitImpls(DefId),
+
+    // Nodes representing caches. To properly handle a true cache, we
+    // don't use a DepTrackingMap, but rather we push a task node.
+    // Otherwise the write into the map would be incorrectly
+    // attributed to the first task that happened to fill the cache,
+    // which would yield an overly conservative dep-graph.
+    TraitItems(DefId),
+    ReprHints(DefId),
+    TraitSelect(DefId, Option<SimplifiedType>),
+}
+
+#[derive(Clone)]
+pub struct DepGraph {
+    data: Rc<DepGraphThreadData>
+}
+
+impl DepGraph {
+    pub fn new(enabled: bool) -> DepGraph {
+        DepGraph {
+            data: Rc::new(DepGraphThreadData::new(enabled))
+        }
+    }
+
+    pub fn query(&self) -> DepGraphQuery {
+        self.data.query()
+    }
+
+    pub fn in_ignore<'graph>(&'graph self) -> raii::IgnoreTask<'graph> {
+        raii::IgnoreTask::new(&self.data)
+    }
+
+    pub fn in_task<'graph>(&'graph self, key: DepNode) -> raii::DepTask<'graph> {
+        raii::DepTask::new(&self.data, key)
+    }
+
+    pub fn with_ignore<OP,R>(&self, op: OP) -> R
+        where OP: FnOnce() -> R
+    {
+        let _task = self.in_ignore();
+        op()
+    }
+
+    pub fn with_task<OP,R>(&self, key: DepNode, op: OP) -> R
+        where OP: FnOnce() -> R
+    {
+        let _task = self.in_task(key);
+        op()
+    }
+
+    pub fn read(&self, v: DepNode) {
+        self.data.enqueue(DepMessage::Read(v));
+    }
+
+    pub fn write(&self, v: DepNode) {
+        self.data.enqueue(DepMessage::Write(v));
+    }
+}
+
+pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig};
+
+pub use self::query::DepGraphQuery;
+
+/// Visit all the items in the krate in some order. When visiting a
+/// particular item, first create a dep-node by calling `dep_node_fn`
+/// and push that onto the dep-graph stack of tasks, and also create a
+/// read edge from the corresponding AST node. This is used in
+/// compiler passes to automatically record the item that they are
+/// working on.
+pub fn visit_all_items_in_krate<'tcx,V,F>(tcx: &ty::ctxt<'tcx>,
+                                          mut dep_node_fn: F,
+                                          visitor: &mut V)
+    where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx>
+{
+    struct TrackingVisitor<'visit, 'tcx: 'visit, F: 'visit, V: 'visit> {
+        tcx: &'visit ty::ctxt<'tcx>,
+        dep_node_fn: &'visit mut F,
+        visitor: &'visit mut V
+    }
+
+    impl<'visit, 'tcx, F, V> Visitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V>
+        where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx>
+    {
+        fn visit_item(&mut self, i: &'tcx hir::Item) {
+            let item_def_id = self.tcx.map.local_def_id(i.id);
+            let task_id = (self.dep_node_fn)(item_def_id);
+            debug!("About to start task {:?}", task_id);
+            let _task = self.tcx.dep_graph.in_task(task_id);
+            self.tcx.dep_graph.read(DepNode::Hir(item_def_id));
+            self.visitor.visit_item(i)
+        }
+    }
+
+    let krate = tcx.dep_graph.with_ignore(|| tcx.map.krate());
+    let mut tracking_visitor = TrackingVisitor {
+        tcx: tcx,
+        dep_node_fn: &mut dep_node_fn,
+        visitor: visitor
+    };
+    krate.visit_all_items(&mut tracking_visitor)
+}
diff --git a/src/librustc/dep_graph/query.rs b/src/librustc/dep_graph/query.rs
new file mode 100644
index 0000000000000..74a054acb4fa0
--- /dev/null
+++ b/src/librustc/dep_graph/query.rs
@@ -0,0 +1,68 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::graph::{Graph, NodeIndex};
+
+use super::DepNode;
+
+pub struct DepGraphQuery {
+    pub graph: Graph<DepNode, ()>,
+    pub indices: FnvHashMap<DepNode, NodeIndex>,
+}
+
+impl DepGraphQuery {
+    pub fn new(nodes: &[DepNode], edges: &[(DepNode, DepNode)]) -> DepGraphQuery {
+        let mut graph = Graph::new();
+        let mut indices = FnvHashMap();
+        for node in nodes {
+            indices.insert(node.clone(), graph.next_node_index());
+            graph.add_node(node.clone());
+        }
+
+        for &(ref source, ref target) in edges {
+            let source = indices[source];
+            let target = indices[target];
+            graph.add_edge(source, target, ());
+        }
+
+        DepGraphQuery {
+            graph: graph,
+            indices: indices
+        }
+    }
+
+    pub fn nodes(&self) -> Vec<DepNode> {
+        self.graph.all_nodes()
+                  .iter()
+                  .map(|n| n.data.clone())
+                  .collect()
+    }
+
+    pub fn edges(&self) -> Vec<(DepNode,DepNode)> {
+        self.graph.all_edges()
+                  .iter()
+                  .map(|edge| (edge.source(), edge.target()))
+                  .map(|(s, t)| (self.graph.node_data(s).clone(), self.graph.node_data(t).clone()))
+                  .collect()
+    }
+
+    /// All nodes reachable from `node`. In other words, things that
+    /// will have to be recomputed if `node` changes.
+    pub fn dependents(&self, node: DepNode) -> Vec<DepNode> {
+        if let Some(&index) = self.indices.get(&node) {
+            self.graph.depth_traverse(index)
+                      .map(|dependent_node| self.graph.node_data(dependent_node).clone())
+                      .collect()
+        } else {
+            vec![]
+        }
+    }
+}
diff --git a/src/librustc/dep_graph/raii.rs b/src/librustc/dep_graph/raii.rs
new file mode 100644
index 0000000000000..dd7ff92f9c360
--- /dev/null
+++ b/src/librustc/dep_graph/raii.rs
@@ -0,0 +1,47 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::DepNode;
+use super::thread::{DepGraphThreadData, DepMessage};
+
+pub struct DepTask<'graph> {
+    data: &'graph DepGraphThreadData,
+    key: DepNode,
+}
+
+impl<'graph> DepTask<'graph> {
+    pub fn new(data: &'graph DepGraphThreadData, key: DepNode) -> DepTask<'graph> {
+        data.enqueue(DepMessage::PushTask(key));
+        DepTask { data: data, key: key }
+    }
+}
+
+impl<'graph> Drop for DepTask<'graph> {
+    fn drop(&mut self) {
+        self.data.enqueue(DepMessage::PopTask(self.key));
+    }
+}
+
+pub struct IgnoreTask<'graph> {
+    data: &'graph DepGraphThreadData
+}
+
+impl<'graph> IgnoreTask<'graph> {
+    pub fn new(data: &'graph DepGraphThreadData) -> IgnoreTask<'graph> {
+        data.enqueue(DepMessage::PushIgnore);
+        IgnoreTask { data: data }
+    }
+}
+
+impl<'graph> Drop for IgnoreTask<'graph> {
+    fn drop(&mut self) {
+        self.data.enqueue(DepMessage::PopIgnore);
+    }
+}
diff --git a/src/librustc/dep_graph/thread.rs b/src/librustc/dep_graph/thread.rs
new file mode 100644
index 0000000000000..dbc57605d71ae
--- /dev/null
+++ b/src/librustc/dep_graph/thread.rs
@@ -0,0 +1,137 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Manages the communication between the compiler's main thread and
+//! the thread that constructs the dependency graph. The basic idea is
+//! to use double buffering to lower the cost of producing a message.
+//! In the compiler thread, we accumulate messages in a vector until
+//! the vector is full, or until we want to query the graph, and then
+//! we send that vector over to the depgraph thread. At the same time,
+//! we receive an empty vector from the depgraph thread that we can use
+//! to accumulate more messages. This way we only ever have two vectors
+//! allocated (and both have a fairly large capacity).
+
+use rustc_data_structures::veccell::VecCell;
+use std::sync::mpsc::{self, Sender, Receiver};
+use std::thread;
+
+use super::DepGraphQuery;
+use super::DepNode;
+use super::edges::DepGraphEdges;
+
+pub enum DepMessage {
+    Read(DepNode),
+    Write(DepNode),
+    PushTask(DepNode),
+    PopTask(DepNode),
+    PushIgnore,
+    PopIgnore,
+    Query,
+}
+
+pub struct DepGraphThreadData {
+    enabled: bool,
+
+    // current buffer, where we accumulate messages
+    messages: VecCell<DepMessage>,
+
+    // whence to receive new buffer when full
+    swap_in: Receiver<Vec<DepMessage>>,
+
+    // where to send buffer when full
+    swap_out: Sender<Vec<DepMessage>>,
+
+    // where to receive query results
+    query_in: Receiver<DepGraphQuery>,
+}
+
+const INITIAL_CAPACITY: usize = 2048;
+
+impl DepGraphThreadData {
+    pub fn new(enabled: bool) -> DepGraphThreadData {
+        let (tx1, rx1) = mpsc::channel();
+        let (tx2, rx2) = mpsc::channel();
+        let (txq, rxq) = mpsc::channel();
+        if enabled {
+            thread::spawn(move || main(rx1, tx2, txq));
+        }
+        DepGraphThreadData {
+            enabled: enabled,
+            messages: VecCell::with_capacity(INITIAL_CAPACITY),
+            swap_in: rx2,
+            swap_out: tx1,
+            query_in: rxq,
+        }
+    }
+
+    /// Sends the current batch of messages to the thread. Installs a
+    /// new vector of messages.
+    fn swap(&self) {
+        assert!(self.enabled, "should never swap if not enabled");
+
+        // should be a buffer waiting for us (though of course we may
+        // have to wait for depgraph thread to finish processing the
+        // old messages)
+        let new_messages = self.swap_in.recv().unwrap();
+        assert!(new_messages.is_empty());
+
+        // swap in the empty buffer and extract the full one
+        let old_messages = self.messages.swap(new_messages);
+
+        // send full buffer to depgraph thread to be processed
+        self.swap_out.send(old_messages).unwrap();
+    }
+
+    pub fn query(&self) -> DepGraphQuery {
+        assert!(self.enabled, "cannot query if dep graph construction not enabled");
+        self.enqueue(DepMessage::Query);
+        self.swap();
+        self.query_in.recv().unwrap()
+    }
+
+    /// Enqueue a message to be sent when things are next swapped. (If
+    /// the buffer is full, this may swap.)
+    #[inline]
+    pub fn enqueue(&self, message: DepMessage) {
+        if self.enabled {
+            let len = self.messages.push(message);
+            if len == INITIAL_CAPACITY {
+                self.swap();
+            }
+        }
+    }
+}
+
+/// Definition of the depgraph thread.
+pub fn main(swap_in: Receiver<Vec<DepMessage>>,
+            swap_out: Sender<Vec<DepMessage>>,
+            query_out: Sender<DepGraphQuery>) {
+    let mut edges = DepGraphEdges::new();
+
+    // the compiler thread always expects a fresh buffer to be
+    // waiting, so queue one up
+    swap_out.send(Vec::with_capacity(INITIAL_CAPACITY)).unwrap();
+
+    // process the buffers from compiler thread as we receive them
+    for mut messages in swap_in {
+        for msg in messages.drain(..) {
+            match msg {
+                DepMessage::Read(node) => edges.read(node),
+                DepMessage::Write(node) => edges.write(node),
+                DepMessage::PushTask(node) => edges.push_task(node),
+                DepMessage::PopTask(node) => edges.pop_task(node),
+                DepMessage::PushIgnore => edges.push_ignore(),
+                DepMessage::PopIgnore => edges.pop_ignore(),
+                DepMessage::Query => query_out.send(edges.query()).unwrap(),
+            }
+        }
+        swap_out.send(messages).unwrap();
+    }
+}
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 00d6237b85564..f84d5fbaf81dd 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -87,6 +87,8 @@ pub mod back {
     pub use rustc_back::svh;
 }
 
+pub mod dep_graph;
+
 pub mod front {
     pub mod check_attr;
     pub mod map;
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 464f29a33937f..0ac5160c29e25 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -25,6 +25,7 @@
 //! for all lint attributes.
 use self::TargetLint::*;
 
+use dep_graph::DepNode;
 use middle::privacy::AccessLevels;
 use middle::ty;
 use session::{early_error, Session};
@@ -1071,6 +1072,8 @@ impl LateLintPass for GatherNodeLevels {
 ///
 /// Consumes the `lint_store` field of the `Session`.
 pub fn check_crate(tcx: &ty::ctxt, access_levels: &AccessLevels) {
+    let _task = tcx.dep_graph.in_task(DepNode::LateLintCheck);
+
     let krate = tcx.map.krate();
     let mut cx = LateContext::new(tcx, krate, access_levels);
 
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index c2acd0e4795c9..a9b3043e09095 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -24,6 +24,7 @@
 // - It's not possible to take the address of a static item with unsafe interior. This is enforced
 // by borrowck::gather_loans
 
+use dep_graph::DepNode;
 use middle::ty::cast::{CastKind};
 use middle::const_eval::{self, ConstEvalErr};
 use middle::const_eval::ErrKind::IndexOpFeatureGated;
@@ -842,13 +843,12 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
 }
 
 pub fn check_crate(tcx: &ty::ctxt) {
-    tcx.map.krate().visit_all_items(&mut CheckCrateVisitor {
+    tcx.visit_all_items_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor {
         tcx: tcx,
         mode: Mode::Var,
         qualif: ConstQualif::NOT_CONST,
         rvalue_borrows: NodeMap()
     });
-
     tcx.sess.abort_if_errors();
 }
 
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 8439b439d7649..972f9e2c64d0e 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -12,6 +12,7 @@ pub use self::Constructor::*;
 use self::Usefulness::*;
 use self::WitnessPreference::*;
 
+use dep_graph::DepNode;
 use middle::const_eval::{compare_const_vals, ConstVal};
 use middle::const_eval::{eval_const_expr, eval_const_expr_partial};
 use middle::const_eval::{const_expr_to_pat, lookup_const_by_id};
@@ -155,7 +156,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
 }
 
 pub fn check_crate(tcx: &ty::ctxt) {
-    tcx.map.krate().visit_all_items(&mut MatchCheckCtxt {
+    tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut MatchCheckCtxt {
         tcx: tcx,
         param_env: tcx.empty_parameter_environment(),
     });
diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs
index 35adeae3e617c..8a3e039ac6e53 100644
--- a/src/librustc/middle/check_rvalues.rs
+++ b/src/librustc/middle/check_rvalues.rs
@@ -11,21 +11,21 @@
 // Checks that all rvalues in a crate have statically known size. check_crate
 // is the public starting point.
 
+use dep_graph::DepNode;
 use middle::expr_use_visitor as euv;
 use middle::infer;
 use middle::mem_categorization as mc;
 use middle::ty::ParameterEnvironment;
 use middle::ty;
 
-use syntax::ast;
 use rustc_front::hir;
-use syntax::codemap::Span;
 use rustc_front::intravisit;
+use syntax::ast;
+use syntax::codemap::Span;
 
-pub fn check_crate(tcx: &ty::ctxt,
-                   krate: &hir::Crate) {
+pub fn check_crate(tcx: &ty::ctxt) {
     let mut rvcx = RvalueContext { tcx: tcx };
-    krate.visit_all_items(&mut rvcx);
+    tcx.visit_all_items_in_krate(DepNode::RvalueCheck, &mut rvcx);
 }
 
 struct RvalueContext<'a, 'tcx: 'a> {
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index ec1b447d7111b..1386ef91c70bf 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -12,6 +12,7 @@
 // closely. The idea is that all reachable symbols are live, codes called
 // from live codes are live, and everything else is dead.
 
+use dep_graph::DepNode;
 use front::map as ast_map;
 use rustc_front::hir;
 use rustc_front::intravisit::{self, Visitor};
@@ -590,6 +591,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> {
 }
 
 pub fn check_crate(tcx: &ty::ctxt, access_levels: &privacy::AccessLevels) {
+    let _task = tcx.dep_graph.in_task(DepNode::DeadCheck);
     let krate = tcx.map.krate();
     let live_symbols = find_live(tcx, access_levels, krate);
     let mut visitor = DeadVisitor { tcx: tcx, live_symbols: live_symbols };
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 0273d1a76e9a8..23d1617e5c658 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -274,7 +274,7 @@ enum PassArgs {
 }
 
 impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
-    pub fn new(delegate: &'d mut (Delegate<'tcx>),
+    pub fn new(delegate: &'d mut (Delegate<'tcx>+'d),
                typer: &'t infer::InferCtxt<'a, 'tcx>)
                -> ExprUseVisitor<'d,'t,'a,'tcx> where 'tcx:'a+'d
     {
diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs
index 48d7f44063ec3..f1eed256dd156 100644
--- a/src/librustc/middle/intrinsicck.rs
+++ b/src/librustc/middle/intrinsicck.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use dep_graph::DepNode;
 use middle::def::DefFn;
 use middle::def_id::DefId;
 use middle::subst::{Subst, Substs, EnumeratedItems};
@@ -29,7 +30,7 @@ pub fn check_crate(tcx: &ctxt) {
         dummy_sized_ty: tcx.types.isize,
         dummy_unsized_ty: tcx.mk_slice(tcx.types.isize),
     };
-    tcx.map.krate().visit_all_items(&mut visitor);
+    tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor);
 }
 
 struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs
index d146ad2d8003b..738440adf416d 100644
--- a/src/librustc/middle/reachable.rs
+++ b/src/librustc/middle/reachable.rs
@@ -15,6 +15,7 @@
 // makes all other generics or inline functions that it references
 // reachable as well.
 
+use dep_graph::DepNode;
 use front::map as ast_map;
 use middle::def;
 use middle::def_id::DefId;
@@ -349,6 +350,7 @@ impl<'a, 'v> Visitor<'v> for CollectPrivateImplItemsVisitor<'a> {
 pub fn find_reachable(tcx: &ty::ctxt,
                       access_levels: &privacy::AccessLevels)
                       -> NodeSet {
+    let _task = tcx.dep_graph.in_task(DepNode::Reachability);
 
     let mut reachable_context = ReachableContext::new(tcx);
 
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index f6af680d441eb..8d5c0c98885b2 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -13,6 +13,7 @@
 
 pub use self::StabilityLevel::*;
 
+use dep_graph::DepNode;
 use session::Session;
 use lint;
 use middle::cstore::{CrateStore, LOCAL_CRATE};
@@ -328,6 +329,7 @@ impl<'tcx> Index<'tcx> {
 /// features used.
 pub fn check_unstable_api_usage(tcx: &ty::ctxt)
                                 -> FnvHashMap<InternedString, StabilityLevel> {
+    let _task = tcx.dep_graph.in_task(DepNode::StabilityCheck);
     let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
 
     // Put the active features into a map for quick lookup
@@ -341,8 +343,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt)
     };
     intravisit::walk_crate(&mut checker, tcx.map.krate());
 
-    let used_features = checker.used_features;
-    return used_features;
+    checker.used_features
 }
 
 struct Checker<'a, 'tcx: 'a> {
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index 255680465ca7f..dddd6f8bc85d7 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -15,10 +15,12 @@ pub use self::FulfillmentErrorCode::*;
 pub use self::Vtable::*;
 pub use self::ObligationCauseCode::*;
 
+use dep_graph::DepNode;
 use middle::def_id::DefId;
 use middle::free_region::FreeRegionMap;
 use middle::subst;
 use middle::ty::{self, HasTypeFlags, Ty};
+use middle::ty::fast_reject;
 use middle::ty::fold::TypeFoldable;
 use middle::infer::{self, fixup_err_to_string, InferCtxt};
 
@@ -599,6 +601,18 @@ impl<'tcx> FulfillmentError<'tcx> {
 }
 
 impl<'tcx> TraitObligation<'tcx> {
+    /// Creates the dep-node for selecting/evaluating this trait reference.
+    fn dep_node(&self, tcx: &ty::ctxt<'tcx>) -> DepNode {
+        let simplified_ty =
+            fast_reject::simplify_type(tcx,
+                                       self.predicate.skip_binder().self_ty(), // (*)
+                                       true);
+
+        // (*) skip_binder is ok because `simplify_type` doesn't care about regions
+
+        DepNode::TraitSelect(self.predicate.def_id(), simplified_ty)
+    }
+
     fn self_ty(&self) -> ty::Binder<Ty<'tcx>> {
         ty::Binder(self.predicate.skip_binder().self_ty())
     }
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index b0215675fca81..bd92f9748669f 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -310,6 +310,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         debug!("select({:?})", obligation);
         assert!(!obligation.predicate.has_escaping_regions());
 
+        let dep_node = obligation.dep_node(self.tcx());
+        let _task = self.tcx().dep_graph.in_task(dep_node);
+
         let stack = self.push_stack(TraitObligationStackList::empty(), obligation);
         match try!(self.candidate_from_obligation(&stack)) {
             None => {
@@ -411,7 +414,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// accurate if inference variables are involved.
     pub fn evaluate_obligation_conservatively(&mut self,
                                               obligation: &PredicateObligation<'tcx>)
-                               -> bool
+                                              -> bool
     {
         debug!("evaluate_obligation_conservatively({:?})",
                obligation);
diff --git a/src/librustc/middle/ty/contents.rs b/src/librustc/middle/ty/contents.rs
index afe88f70d9450..619201a4a9feb 100644
--- a/src/librustc/middle/ty/contents.rs
+++ b/src/librustc/middle/ty/contents.rs
@@ -10,7 +10,7 @@
 
 use middle::def_id::{DefId};
 use middle::ty::{self, Ty};
-use util::common::{memoized};
+use util::common::MemoizationMap;
 use util::nodemap::FnvHashMap;
 
 use std::fmt;
@@ -141,9 +141,7 @@ impl fmt::Debug for TypeContents {
 
 impl<'tcx> ty::TyS<'tcx> {
     pub fn type_contents(&'tcx self, cx: &ty::ctxt<'tcx>) -> TypeContents {
-        return memoized(&cx.tc_cache, self, |ty| {
-            tc_ty(cx, ty, &mut FnvHashMap())
-        });
+        return cx.tc_cache.memoize(self, || tc_ty(cx, self, &mut FnvHashMap()));
 
         fn tc_ty<'tcx>(cx: &ty::ctxt<'tcx>,
                        ty: Ty<'tcx>,
diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs
index cee651743ca86..d1504d25288a8 100644
--- a/src/librustc/middle/ty/context.rs
+++ b/src/librustc/middle/ty/context.rs
@@ -13,6 +13,7 @@
 // FIXME: (@jroesch) @eddyb should remove this when he renames ctxt
 #![allow(non_camel_case_types)]
 
+use dep_graph::{DepGraph, DepTrackingMap};
 use front::map as ast_map;
 use session::Session;
 use lint;
@@ -29,10 +30,12 @@ use middle::traits;
 use middle::ty::{self, TraitRef, Ty, TypeAndMut};
 use middle::ty::{TyS, TypeVariants};
 use middle::ty::{AdtDef, ClosureSubsts, ExistentialBounds, Region};
-use middle::ty::{FreevarMap, GenericPredicates};
+use middle::ty::{FreevarMap};
 use middle::ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, TraitTy};
 use middle::ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
 use middle::ty::TypeVariants::*;
+use middle::ty::maps;
+use util::common::MemoizationMap;
 use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet};
 use util::nodemap::FnvHashMap;
 
@@ -224,6 +227,8 @@ pub struct ctxt<'tcx> {
     region_interner: RefCell<FnvHashMap<&'tcx Region, &'tcx Region>>,
     stability_interner: RefCell<FnvHashMap<&'tcx attr::Stability, &'tcx attr::Stability>>,
 
+    pub dep_graph: DepGraph,
+
     /// Common types, pre-interned for your convenience.
     pub types: CommonTypes<'tcx>,
 
@@ -245,21 +250,22 @@ pub struct ctxt<'tcx> {
     pub tables: RefCell<Tables<'tcx>>,
 
     /// Maps from a trait item to the trait item "descriptor"
-    pub impl_or_trait_items: RefCell<DefIdMap<ty::ImplOrTraitItem<'tcx>>>,
+    pub impl_or_trait_items: RefCell<DepTrackingMap<maps::ImplOrTraitItems<'tcx>>>,
 
     /// Maps from a trait def-id to a list of the def-ids of its trait items
-    pub trait_item_def_ids: RefCell<DefIdMap<Rc<Vec<ty::ImplOrTraitItemId>>>>,
+    pub trait_item_def_ids: RefCell<DepTrackingMap<maps::TraitItemDefIds<'tcx>>>,
 
-    /// A cache for the trait_items() routine
-    pub trait_items_cache: RefCell<DefIdMap<Rc<Vec<ty::ImplOrTraitItem<'tcx>>>>>,
+    /// A cache for the trait_items() routine; note that the routine
+    /// itself pushes the `TraitItems` dependency node.
+    trait_items_cache: RefCell<DepTrackingMap<maps::TraitItems<'tcx>>>,
 
-    pub impl_trait_refs: RefCell<DefIdMap<Option<TraitRef<'tcx>>>>,
-    pub trait_defs: RefCell<DefIdMap<&'tcx ty::TraitDef<'tcx>>>,
-    pub adt_defs: RefCell<DefIdMap<ty::AdtDefMaster<'tcx>>>,
+    pub impl_trait_refs: RefCell<DepTrackingMap<maps::ImplTraitRefs<'tcx>>>,
+    pub trait_defs: RefCell<DepTrackingMap<maps::TraitDefs<'tcx>>>,
+    pub adt_defs: RefCell<DepTrackingMap<maps::AdtDefs<'tcx>>>,
 
     /// Maps from the def-id of an item (trait/struct/enum/fn) to its
     /// associated predicates.
-    pub predicates: RefCell<DefIdMap<GenericPredicates<'tcx>>>,
+    pub predicates: RefCell<DepTrackingMap<maps::Predicates<'tcx>>>,
 
     /// Maps from the def-id of a trait to the list of
     /// super-predicates. This is a subset of the full list of
@@ -267,21 +273,40 @@ pub struct ctxt<'tcx> {
     /// evaluate them even during type conversion, often before the
     /// full predicates are available (note that supertraits have
     /// additional acyclicity requirements).
-    pub super_predicates: RefCell<DefIdMap<GenericPredicates<'tcx>>>,
+    pub super_predicates: RefCell<DepTrackingMap<maps::Predicates<'tcx>>>,
 
     pub map: ast_map::Map<'tcx>,
+
+    // Records the free variables refrenced by every closure
+    // expression. Do not track deps for this, just recompute it from
+    // scratch every time.
     pub freevars: RefCell<FreevarMap>,
-    pub tcache: RefCell<DefIdMap<ty::TypeScheme<'tcx>>>,
+
+    // Records the type of every item.
+    pub tcache: RefCell<DepTrackingMap<maps::Tcache<'tcx>>>,
+
+    // Internal cache for metadata decoding. No need to track deps on this.
     pub rcache: RefCell<FnvHashMap<ty::CReaderCacheKey, Ty<'tcx>>>,
+
+    // Cache for the type-contents routine. FIXME -- track deps?
     pub tc_cache: RefCell<FnvHashMap<Ty<'tcx>, ty::contents::TypeContents>>,
+
+    // Cache for various types within a method body and so forth.
+    //
+    // FIXME this should be made local to typeck, but it is currently used by one lint
     pub ast_ty_to_ty_cache: RefCell<NodeMap<Ty<'tcx>>>,
+
+    // FIXME no dep tracking, but we should be able to remove this
     pub ty_param_defs: RefCell<NodeMap<ty::TypeParameterDef<'tcx>>>,
+
+    // FIXME dep tracking -- should be harmless enough
     pub normalized_cache: RefCell<FnvHashMap<Ty<'tcx>, Ty<'tcx>>>,
+
     pub lang_items: middle::lang_items::LanguageItems,
 
     /// Maps from def-id of a type or region parameter to its
     /// (inferred) variance.
-    pub item_variance_map: RefCell<DefIdMap<Rc<ty::ItemVariances>>>,
+    pub item_variance_map: RefCell<DepTrackingMap<maps::ItemVariances<'tcx>>>,
 
     /// True if the variance has been computed yet; false otherwise.
     pub variance_computed: Cell<bool>,
@@ -289,13 +314,13 @@ pub struct ctxt<'tcx> {
     /// Maps a DefId of a type to a list of its inherent impls.
     /// Contains implementations of methods that are inherent to a type.
     /// Methods in these implementations don't need to be exported.
-    pub inherent_impls: RefCell<DefIdMap<Rc<Vec<DefId>>>>,
+    pub inherent_impls: RefCell<DepTrackingMap<maps::InherentImpls<'tcx>>>,
 
     /// Maps a DefId of an impl to a list of its items.
     /// Note that this contains all of the impls that we know about,
     /// including ones in other crates. It's not clear that this is the best
     /// way to do it.
-    pub impl_items: RefCell<DefIdMap<Vec<ty::ImplOrTraitItemId>>>,
+    pub impl_items: RefCell<DepTrackingMap<maps::ImplItems<'tcx>>>,
 
     /// Set of used unsafe nodes (functions or blocks). Unsafe nodes not
     /// present in this set can be warned about.
@@ -309,6 +334,7 @@ pub struct ctxt<'tcx> {
     /// The set of external nominal types whose implementations have been read.
     /// This is used for lazy resolution of methods.
     pub populated_external_types: RefCell<DefIdSet>,
+
     /// The set of external primitive types whose implementations have been read.
     /// FIXME(arielb1): why is this separate from populated_external_types?
     pub populated_external_primitive_impls: RefCell<DefIdSet>,
@@ -344,7 +370,7 @@ pub struct ctxt<'tcx> {
     pub fulfilled_predicates: RefCell<traits::FulfilledPredicates<'tcx>>,
 
     /// Caches the representation hints for struct definitions.
-    pub repr_hint_cache: RefCell<DefIdMap<Rc<Vec<attr::ReprAttr>>>>,
+    repr_hint_cache: RefCell<DepTrackingMap<maps::ReprHints<'tcx>>>,
 
     /// Maps Expr NodeId's to their constant qualification.
     pub const_qualif_map: RefCell<NodeMap<middle::check_const::ConstQualif>>,
@@ -483,7 +509,7 @@ impl<'tcx> ctxt<'tcx> {
     {
         let interner = RefCell::new(FnvHashMap());
         let common_types = CommonTypes::new(&arenas.type_, &interner);
-
+        let dep_graph = DepGraph::new(s.opts.incremental_compilation);
         tls::enter(ctxt {
             arenas: arenas,
             interner: interner,
@@ -491,35 +517,36 @@ impl<'tcx> ctxt<'tcx> {
             bare_fn_interner: RefCell::new(FnvHashMap()),
             region_interner: RefCell::new(FnvHashMap()),
             stability_interner: RefCell::new(FnvHashMap()),
+            dep_graph: dep_graph.clone(),
             types: common_types,
             named_region_map: named_region_map,
             region_maps: region_maps,
             free_region_maps: RefCell::new(FnvHashMap()),
-            item_variance_map: RefCell::new(DefIdMap()),
+            item_variance_map: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             variance_computed: Cell::new(false),
             sess: s,
             def_map: def_map,
             tables: RefCell::new(Tables::empty()),
-            impl_trait_refs: RefCell::new(DefIdMap()),
-            trait_defs: RefCell::new(DefIdMap()),
-            adt_defs: RefCell::new(DefIdMap()),
-            predicates: RefCell::new(DefIdMap()),
-            super_predicates: RefCell::new(DefIdMap()),
+            impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            adt_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            super_predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()),
             map: map,
             freevars: RefCell::new(freevars),
-            tcache: RefCell::new(DefIdMap()),
+            tcache: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             rcache: RefCell::new(FnvHashMap()),
             tc_cache: RefCell::new(FnvHashMap()),
             ast_ty_to_ty_cache: RefCell::new(NodeMap()),
-            impl_or_trait_items: RefCell::new(DefIdMap()),
-            trait_item_def_ids: RefCell::new(DefIdMap()),
-            trait_items_cache: RefCell::new(DefIdMap()),
+            impl_or_trait_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            trait_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            trait_items_cache: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             ty_param_defs: RefCell::new(NodeMap()),
             normalized_cache: RefCell::new(FnvHashMap()),
             lang_items: lang_items,
-            inherent_impls: RefCell::new(DefIdMap()),
-            impl_items: RefCell::new(DefIdMap()),
+            inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            impl_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             used_unsafe: RefCell::new(NodeSet()),
             used_mut_nodes: RefCell::new(NodeSet()),
             populated_external_types: RefCell::new(DefIdSet()),
@@ -531,7 +558,7 @@ impl<'tcx> ctxt<'tcx> {
             stability: RefCell::new(stability),
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
-            repr_hint_cache: RefCell::new(DefIdMap()),
+            repr_hint_cache: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             const_qualif_map: RefCell::new(NodeMap()),
             custom_coerce_unsized_kinds: RefCell::new(DefIdMap()),
             cast_kinds: RefCell::new(NodeMap()),
@@ -1000,4 +1027,26 @@ impl<'tcx> ctxt<'tcx> {
     pub fn mk_param_from_def(&self, def: &ty::TypeParameterDef) -> Ty<'tcx> {
         self.mk_param(def.space, def.index, def.name)
     }
+
+    pub fn trait_items(&self, trait_did: DefId) -> Rc<Vec<ty::ImplOrTraitItem<'tcx>>> {
+        self.trait_items_cache.memoize(trait_did, || {
+            let def_ids = self.trait_item_def_ids(trait_did);
+            Rc::new(def_ids.iter()
+                           .map(|d| self.impl_or_trait_item(d.def_id()))
+                           .collect())
+        })
+    }
+
+    /// Obtain the representation annotation for a struct definition.
+    pub fn lookup_repr_hints(&self, did: DefId) -> Rc<Vec<attr::ReprAttr>> {
+        self.repr_hint_cache.memoize(did, || {
+            Rc::new(if did.is_local() {
+                self.get_attrs(did).iter().flat_map(|meta| {
+                    attr::find_repr_attrs(self.sess.diagnostic(), meta).into_iter()
+                }).collect()
+            } else {
+                self.sess.cstore.repr_attrs(did)
+            })
+        })
+    }
 }
diff --git a/src/librustc/middle/ty/fast_reject.rs b/src/librustc/middle/ty/fast_reject.rs
index 77608f4012845..a06e8a72c44ee 100644
--- a/src/librustc/middle/ty/fast_reject.rs
+++ b/src/librustc/middle/ty/fast_reject.rs
@@ -15,7 +15,7 @@ use syntax::ast;
 use self::SimplifiedType::*;
 
 /// See `simplify_type
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum SimplifiedType {
     BoolSimplifiedType,
     CharSimplifiedType,
diff --git a/src/librustc/middle/ty/ivar.rs b/src/librustc/middle/ty/ivar.rs
index 73d567d0acf40..ffc12aa5aea19 100644
--- a/src/librustc/middle/ty/ivar.rs
+++ b/src/librustc/middle/ty/ivar.rs
@@ -8,7 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use dep_graph::DepNode;
 use middle::ty::{Ty, TyS};
+use middle::ty::tls;
 
 use rustc_data_structures::ivar;
 
@@ -27,6 +29,10 @@ use core::nonzero::NonZero;
 ///     (B) no aliases to this value with a 'tcx longer than this
 ///         value's 'lt exist
 ///
+/// Dependency tracking: each ivar does not know what node in the
+/// dependency graph it is associated with, so when you get/fulfill
+/// you must supply a `DepNode` id. This should always be the same id!
+///
 /// NonZero is used rather than Unique because Unique isn't Copy.
 pub struct TyIVar<'tcx, 'lt: 'tcx>(ivar::Ivar<NonZero<*const TyS<'static>>>,
                                    PhantomData<fn(TyS<'lt>)->TyS<'tcx>>);
@@ -40,19 +46,28 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> {
     }
 
     #[inline]
-    pub fn get(&self) -> Option<Ty<'tcx>> {
+    pub fn get(&self, dep_node: DepNode) -> Option<Ty<'tcx>> {
+        tls::with(|tcx| tcx.dep_graph.read(dep_node));
+        self.untracked_get()
+    }
+
+    #[inline]
+    fn untracked_get(&self) -> Option<Ty<'tcx>> {
         match self.0.get() {
             None => None,
             // valid because of invariant (A)
             Some(v) => Some(unsafe { &*(*v as *const TyS<'tcx>) })
         }
     }
+
     #[inline]
-    pub fn unwrap(&self) -> Ty<'tcx> {
-        self.get().unwrap()
+    pub fn unwrap(&self, dep_node: DepNode) -> Ty<'tcx> {
+        self.get(dep_node).unwrap()
     }
 
-    pub fn fulfill(&self, value: Ty<'lt>) {
+    pub fn fulfill(&self, dep_node: DepNode, value: Ty<'lt>) {
+        tls::with(|tcx| tcx.dep_graph.write(dep_node));
+
         // Invariant (A) is fulfilled, because by (B), every alias
         // of this has a 'tcx longer than 'lt.
         let value: *const TyS<'lt> = value;
@@ -64,7 +79,7 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> {
 
 impl<'tcx, 'lt> fmt::Debug for TyIVar<'tcx, 'lt> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self.get() {
+        match self.untracked_get() {
             Some(val) => write!(f, "TyIVar({:?})", val),
             None => f.write_str("TyIVar(<unfulfilled>)")
         }
diff --git a/src/librustc/middle/ty/maps.rs b/src/librustc/middle/ty/maps.rs
new file mode 100644
index 0000000000000..7d5276f379ffe
--- /dev/null
+++ b/src/librustc/middle/ty/maps.rs
@@ -0,0 +1,44 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use dep_graph::{DepNode, DepTrackingMapConfig};
+use middle::def_id::DefId;
+use middle::ty;
+use std::marker::PhantomData;
+use std::rc::Rc;
+use syntax::attr;
+
+macro_rules! dep_map_ty {
+    ($ty_name:ident : $node_name:ident ($key:ty) -> $value:ty) => {
+        pub struct $ty_name<'tcx> {
+            data: PhantomData<&'tcx ()>
+        }
+
+        impl<'tcx> DepTrackingMapConfig for $ty_name<'tcx> {
+            type Key = $key;
+            type Value = $value;
+            fn to_dep_node(key: &$key) -> DepNode { DepNode::$node_name(*key) }
+        }
+    }
+}
+
+dep_map_ty! { ImplOrTraitItems: ImplOrTraitItems(DefId) -> ty::ImplOrTraitItem<'tcx> }
+dep_map_ty! { Tcache: ItemSignature(DefId) -> ty::TypeScheme<'tcx> }
+dep_map_ty! { Predicates: ItemSignature(DefId) -> ty::GenericPredicates<'tcx> }
+dep_map_ty! { SuperPredicates: ItemSignature(DefId) -> ty::GenericPredicates<'tcx> }
+dep_map_ty! { TraitItemDefIds: TraitItemDefIds(DefId) -> Rc<Vec<ty::ImplOrTraitItemId>> }
+dep_map_ty! { ImplTraitRefs: ItemSignature(DefId) -> Option<ty::TraitRef<'tcx>> }
+dep_map_ty! { TraitDefs: ItemSignature(DefId) -> &'tcx ty::TraitDef<'tcx> }
+dep_map_ty! { AdtDefs: ItemSignature(DefId) -> ty::AdtDefMaster<'tcx> }
+dep_map_ty! { ItemVariances: ItemSignature(DefId) -> Rc<ty::ItemVariances> }
+dep_map_ty! { InherentImpls: InherentImpls(DefId) -> Rc<Vec<DefId>> }
+dep_map_ty! { ImplItems: ImplItems(DefId) -> Vec<ty::ImplOrTraitItemId> }
+dep_map_ty! { TraitItems: TraitItems(DefId) -> Rc<Vec<ty::ImplOrTraitItem<'tcx>>> }
+dep_map_ty! { ReprHints: ReprHints(DefId) -> Rc<Vec<attr::ReprAttr>> }
diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs
index 5daa9bcd0d1ae..10682655e63b8 100644
--- a/src/librustc/middle/ty/mod.rs
+++ b/src/librustc/middle/ty/mod.rs
@@ -18,6 +18,7 @@ pub use self::ImplOrTraitItem::*;
 pub use self::IntVarValue::*;
 pub use self::LvaluePreference::*;
 
+use dep_graph::{self, DepNode};
 use front::map as ast_map;
 use front::map::LinkedPath;
 use middle;
@@ -31,13 +32,13 @@ use middle::traits;
 use middle::ty;
 use middle::ty::fold::TypeFolder;
 use middle::ty::walk::TypeWalker;
-use util::common::memoized;
-use util::nodemap::{NodeMap, NodeSet, DefIdMap};
+use util::common::MemoizationMap;
+use util::nodemap::{NodeMap, NodeSet};
 use util::nodemap::FnvHashMap;
 
 use serialize::{Encodable, Encoder, Decodable, Decoder};
 use std::borrow::{Borrow, Cow};
-use std::cell::{Cell, RefCell};
+use std::cell::Cell;
 use std::hash::{Hash, Hasher};
 use std::iter;
 use std::rc::Rc;
@@ -51,6 +52,7 @@ use syntax::parse::token::{InternedString, special_idents};
 
 use rustc_front::hir;
 use rustc_front::hir::{ItemImpl, ItemTrait};
+use rustc_front::intravisit::Visitor;
 
 pub use self::sty::{Binder, DebruijnIndex};
 pub use self::sty::{BuiltinBound, BuiltinBounds, ExistentialBounds};
@@ -75,14 +77,18 @@ pub use self::contents::TypeContents;
 pub use self::context::{ctxt, tls};
 pub use self::context::{CtxtArenas, Lift, Tables};
 
+pub use self::trait_def::{TraitDef, TraitFlags};
+
 pub mod adjustment;
 pub mod cast;
 pub mod error;
 pub mod fast_reject;
 pub mod fold;
 pub mod _match;
+pub mod maps;
 pub mod outlives;
 pub mod relate;
+pub mod trait_def;
 pub mod walk;
 pub mod wf;
 pub mod util;
@@ -1317,161 +1323,6 @@ pub struct TypeScheme<'tcx> {
     pub ty: Ty<'tcx>,
 }
 
-bitflags! {
-    flags TraitFlags: u32 {
-        const NO_TRAIT_FLAGS        = 0,
-        const HAS_DEFAULT_IMPL      = 1 << 0,
-        const IS_OBJECT_SAFE        = 1 << 1,
-        const OBJECT_SAFETY_VALID   = 1 << 2,
-        const IMPLS_VALID           = 1 << 3,
-    }
-}
-
-/// As `TypeScheme` but for a trait ref.
-pub struct TraitDef<'tcx> {
-    pub unsafety: hir::Unsafety,
-
-    /// If `true`, then this trait had the `#[rustc_paren_sugar]`
-    /// attribute, indicating that it should be used with `Foo()`
-    /// sugar. This is a temporary thing -- eventually any trait wil
-    /// be usable with the sugar (or without it).
-    pub paren_sugar: bool,
-
-    /// Generic type definitions. Note that `Self` is listed in here
-    /// as having a single bound, the trait itself (e.g., in the trait
-    /// `Eq`, there is a single bound `Self : Eq`). This is so that
-    /// default methods get to assume that the `Self` parameters
-    /// implements the trait.
-    pub generics: Generics<'tcx>,
-
-    pub trait_ref: TraitRef<'tcx>,
-
-    /// A list of the associated types defined in this trait. Useful
-    /// for resolving `X::Foo` type markers.
-    pub associated_type_names: Vec<Name>,
-
-    // Impls of this trait. To allow for quicker lookup, the impls are indexed
-    // by a simplified version of their Self type: impls with a simplifiable
-    // Self are stored in nonblanket_impls keyed by it, while all other impls
-    // are stored in blanket_impls.
-
-    /// Impls of the trait.
-    pub nonblanket_impls: RefCell<
-        FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>
-    >,
-
-    /// Blanket impls associated with the trait.
-    pub blanket_impls: RefCell<Vec<DefId>>,
-
-    /// Various flags
-    pub flags: Cell<TraitFlags>
-}
-
-impl<'tcx> TraitDef<'tcx> {
-    // returns None if not yet calculated
-    pub fn object_safety(&self) -> Option<bool> {
-        if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) {
-            Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE))
-        } else {
-            None
-        }
-    }
-
-    pub fn set_object_safety(&self, is_safe: bool) {
-        assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true));
-        self.flags.set(
-            self.flags.get() | if is_safe {
-                TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE
-            } else {
-                TraitFlags::OBJECT_SAFETY_VALID
-            }
-        );
-    }
-
-    /// Records a trait-to-implementation mapping.
-    pub fn record_impl(&self,
-                       tcx: &ctxt<'tcx>,
-                       impl_def_id: DefId,
-                       impl_trait_ref: TraitRef<'tcx>) {
-        debug!("TraitDef::record_impl for {:?}, from {:?}",
-               self, impl_trait_ref);
-
-        // We don't want to borrow_mut after we already populated all impls,
-        // so check if an impl is present with an immutable borrow first.
-        if let Some(sty) = fast_reject::simplify_type(tcx,
-                                                      impl_trait_ref.self_ty(), false) {
-            if let Some(is) = self.nonblanket_impls.borrow().get(&sty) {
-                if is.contains(&impl_def_id) {
-                    return // duplicate - skip
-                }
-            }
-
-            self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id)
-        } else {
-            if self.blanket_impls.borrow().contains(&impl_def_id) {
-                return // duplicate - skip
-            }
-            self.blanket_impls.borrow_mut().push(impl_def_id)
-        }
-    }
-
-
-    pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &ctxt<'tcx>, mut f: F)  {
-        tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
-
-        for &impl_def_id in self.blanket_impls.borrow().iter() {
-            f(impl_def_id);
-        }
-
-        for v in self.nonblanket_impls.borrow().values() {
-            for &impl_def_id in v {
-                f(impl_def_id);
-            }
-        }
-    }
-
-    /// Iterate over every impl that could possibly match the
-    /// self-type `self_ty`.
-    pub fn for_each_relevant_impl<F: FnMut(DefId)>(&self,
-                                                   tcx: &ctxt<'tcx>,
-                                                   self_ty: Ty<'tcx>,
-                                                   mut f: F)
-    {
-        tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
-
-        for &impl_def_id in self.blanket_impls.borrow().iter() {
-            f(impl_def_id);
-        }
-
-        // simplify_type(.., false) basically replaces type parameters and
-        // projections with infer-variables. This is, of course, done on
-        // the impl trait-ref when it is instantiated, but not on the
-        // predicate trait-ref which is passed here.
-        //
-        // for example, if we match `S: Copy` against an impl like
-        // `impl<T:Copy> Copy for Option<T>`, we replace the type variable
-        // in `Option<T>` with an infer variable, to `Option<_>` (this
-        // doesn't actually change fast_reject output), but we don't
-        // replace `S` with anything - this impl of course can't be
-        // selected, and as there are hundreds of similar impls,
-        // considering them would significantly harm performance.
-        if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) {
-            if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) {
-                for &impl_def_id in impls {
-                    f(impl_def_id);
-                }
-            }
-        } else {
-            for v in self.nonblanket_impls.borrow().values() {
-                for &impl_def_id in v {
-                    f(impl_def_id);
-                }
-            }
-        }
-    }
-
-}
-
 bitflags! {
     flags AdtFlags: u32 {
         const NO_ADT_FLAGS        = 0,
@@ -1513,6 +1364,8 @@ pub struct FieldDefData<'tcx, 'container: 'tcx> {
     pub vis: hir::Visibility,
     /// TyIVar is used here to allow for variance (see the doc at
     /// AdtDefData).
+    ///
+    /// Note: direct accesses to `ty` must also add dep edges.
     ty: ivar::TyIVar<'tcx, 'container>
 }
 
@@ -1803,11 +1656,11 @@ impl<'tcx, 'container> FieldDefData<'tcx, 'container> {
     }
 
     pub fn unsubst_ty(&self) -> Ty<'tcx> {
-        self.ty.unwrap()
+        self.ty.unwrap(DepNode::FieldTy(self.did))
     }
 
     pub fn fulfill_ty(&self, ty: Ty<'container>) {
-        self.ty.fulfill(ty);
+        self.ty.fulfill(DepNode::FieldTy(self.did), ty);
     }
 }
 
@@ -1930,24 +1783,20 @@ impl LvaluePreference {
 /// into the map by the `typeck::collect` phase.  If the def-id is external,
 /// then we have to go consult the crate loading code (and cache the result for
 /// the future).
-fn lookup_locally_or_in_crate_store<V, F>(descr: &str,
+fn lookup_locally_or_in_crate_store<M, F>(descr: &str,
                                           def_id: DefId,
-                                          map: &RefCell<DefIdMap<V>>,
-                                          load_external: F) -> V where
-    V: Clone,
-    F: FnOnce() -> V,
+                                          map: &M,
+                                          load_external: F)
+                                          -> M::Value where
+    M: MemoizationMap<Key=DefId>,
+    F: FnOnce() -> M::Value,
 {
-    match map.borrow().get(&def_id).cloned() {
-        Some(v) => { return v; }
-        None => { }
-    }
-
-    if def_id.is_local() {
-        panic!("No def'n found for {:?} in tcx.{}", def_id, descr);
-    }
-    let v = load_external();
-    map.borrow_mut().insert(def_id, v.clone());
-    v
+    map.memoize(def_id, || {
+        if def_id.is_local() {
+            panic!("No def'n found for {:?} in tcx.{}", def_id, descr);
+        }
+        load_external()
+    })
 }
 
 impl BorrowKind {
@@ -2223,22 +2072,6 @@ impl<'tcx> ctxt<'tcx> {
         }
     }
 
-    pub fn trait_items(&self, trait_did: DefId) -> Rc<Vec<ImplOrTraitItem<'tcx>>> {
-        let mut trait_items = self.trait_items_cache.borrow_mut();
-        match trait_items.get(&trait_did).cloned() {
-            Some(trait_items) => trait_items,
-            None => {
-                let def_ids = self.trait_item_def_ids(trait_did);
-                let items: Rc<Vec<ImplOrTraitItem>> =
-                    Rc::new(def_ids.iter()
-                                   .map(|d| self.impl_or_trait_item(d.def_id()))
-                                   .collect());
-                trait_items.insert(trait_did, items.clone());
-                items
-            }
-        }
-    }
-
     pub fn trait_impl_polarity(&self, id: DefId) -> Option<hir::ImplPolarity> {
         if let Some(id) = self.map.as_local_node_id(id) {
             match self.map.find(id) {
@@ -2256,7 +2089,7 @@ impl<'tcx> ctxt<'tcx> {
     }
 
     pub fn custom_coerce_unsized_kind(&self, did: DefId) -> adjustment::CustomCoerceUnsized {
-        memoized(&self.custom_coerce_unsized_kinds, did, |did: DefId| {
+        self.custom_coerce_unsized_kinds.memoize(did, || {
             let (kind, src) = if did.krate != LOCAL_CRATE {
                 (self.sess.cstore.custom_coerce_unsized_kind(did), "external")
             } else {
@@ -2419,19 +2252,6 @@ impl<'tcx> ctxt<'tcx> {
             || self.lookup_repr_hints(did).contains(&attr::ReprSimd)
     }
 
-    /// Obtain the representation annotation for a struct definition.
-    pub fn lookup_repr_hints(&self, did: DefId) -> Rc<Vec<attr::ReprAttr>> {
-        memoized(&self.repr_hint_cache, did, |did: DefId| {
-            Rc::new(if did.is_local() {
-                self.get_attrs(did).iter().flat_map(|meta| {
-                    attr::find_repr_attrs(self.sess.diagnostic(), meta).into_iter()
-                }).collect()
-            } else {
-                self.sess.cstore.repr_attrs(did)
-            })
-        })
-    }
-
     pub fn item_variances(&self, item_id: DefId) -> Rc<ItemVariances> {
         lookup_locally_or_in_crate_store(
             "item_variance_map", item_id, &self.item_variance_map,
@@ -2458,6 +2278,10 @@ impl<'tcx> ctxt<'tcx> {
             return
         }
 
+        // The primitive is not local, hence we are reading this out
+        // of metadata.
+        let _ignore = self.dep_graph.in_ignore();
+
         if self.populated_external_primitive_impls.borrow().contains(&primitive_def_id) {
             return
         }
@@ -2480,6 +2304,10 @@ impl<'tcx> ctxt<'tcx> {
             return
         }
 
+        // The type is not local, hence we are reading this out of
+        // metadata and don't need to track edges.
+        let _ignore = self.dep_graph.in_ignore();
+
         if self.populated_external_types.borrow().contains(&type_id) {
             return
         }
@@ -2505,6 +2333,10 @@ impl<'tcx> ctxt<'tcx> {
             return
         }
 
+        // The type is not local, hence we are reading this out of
+        // metadata and don't need to track edges.
+        let _ignore = self.dep_graph.in_ignore();
+
         let def = self.lookup_trait_def(trait_id);
         if def.flags.get().intersects(TraitFlags::IMPLS_VALID) {
             return;
@@ -2727,6 +2559,15 @@ impl<'tcx> ctxt<'tcx> {
     pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture> {
         Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone())
     }
+
+
+    pub fn visit_all_items_in_krate<V,F>(&self,
+                                         dep_node_fn: F,
+                                         visitor: &mut V)
+        where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx>
+    {
+        dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor);
+    }
 }
 
 /// The category of explicit self.
diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs
new file mode 100644
index 0000000000000..db001ce2c446c
--- /dev/null
+++ b/src/librustc/middle/ty/trait_def.rs
@@ -0,0 +1,226 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use dep_graph::DepNode;
+use middle::def_id::DefId;
+use middle::ty;
+use middle::ty::fast_reject;
+use middle::ty::Ty;
+use std::borrow::{Borrow};
+use std::cell::{Cell, Ref, RefCell};
+use syntax::ast::Name;
+use rustc_front::hir;
+use util::nodemap::FnvHashMap;
+
+/// As `TypeScheme` but for a trait ref.
+pub struct TraitDef<'tcx> {
+    pub unsafety: hir::Unsafety,
+
+    /// If `true`, then this trait had the `#[rustc_paren_sugar]`
+    /// attribute, indicating that it should be used with `Foo()`
+    /// sugar. This is a temporary thing -- eventually any trait wil
+    /// be usable with the sugar (or without it).
+    pub paren_sugar: bool,
+
+    /// Generic type definitions. Note that `Self` is listed in here
+    /// as having a single bound, the trait itself (e.g., in the trait
+    /// `Eq`, there is a single bound `Self : Eq`). This is so that
+    /// default methods get to assume that the `Self` parameters
+    /// implements the trait.
+    pub generics: ty::Generics<'tcx>,
+
+    pub trait_ref: ty::TraitRef<'tcx>,
+
+    /// A list of the associated types defined in this trait. Useful
+    /// for resolving `X::Foo` type markers.
+    pub associated_type_names: Vec<Name>,
+
+    // Impls of this trait. To allow for quicker lookup, the impls are indexed
+    // by a simplified version of their Self type: impls with a simplifiable
+    // Self are stored in nonblanket_impls keyed by it, while all other impls
+    // are stored in blanket_impls.
+    //
+    // These lists are tracked by `DepNode::TraitImpls`; we don't use
+    // a DepTrackingMap but instead have the `TraitDef` insert the
+    // required reads/writes.
+
+    /// Impls of the trait.
+    nonblanket_impls: RefCell<
+        FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>
+    >,
+
+    /// Blanket impls associated with the trait.
+    blanket_impls: RefCell<Vec<DefId>>,
+
+    /// Various flags
+    pub flags: Cell<TraitFlags>
+}
+
+impl<'tcx> TraitDef<'tcx> {
+    pub fn new(unsafety: hir::Unsafety,
+               paren_sugar: bool,
+               generics: ty::Generics<'tcx>,
+               trait_ref: ty::TraitRef<'tcx>,
+               associated_type_names: Vec<Name>)
+               -> TraitDef<'tcx> {
+        TraitDef {
+            paren_sugar: paren_sugar,
+            unsafety: unsafety,
+            generics: generics,
+            trait_ref: trait_ref,
+            associated_type_names: associated_type_names,
+            nonblanket_impls: RefCell::new(FnvHashMap()),
+            blanket_impls: RefCell::new(vec![]),
+            flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
+        }
+    }
+
+    pub fn def_id(&self) -> DefId {
+        self.trait_ref.def_id
+    }
+
+    // returns None if not yet calculated
+    pub fn object_safety(&self) -> Option<bool> {
+        if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) {
+            Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE))
+        } else {
+            None
+        }
+    }
+
+    pub fn set_object_safety(&self, is_safe: bool) {
+        assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true));
+        self.flags.set(
+            self.flags.get() | if is_safe {
+                TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE
+            } else {
+                TraitFlags::OBJECT_SAFETY_VALID
+            }
+        );
+    }
+
+    fn write_trait_impls(&self, tcx: &ty::ctxt<'tcx>) {
+        tcx.dep_graph.write(DepNode::TraitImpls(self.trait_ref.def_id));
+    }
+
+    fn read_trait_impls(&self, tcx: &ty::ctxt<'tcx>) {
+        tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id));
+    }
+
+    /// Records a trait-to-implementation mapping.
+    pub fn record_impl(&self,
+                       tcx: &ty::ctxt<'tcx>,
+                       impl_def_id: DefId,
+                       impl_trait_ref: ty::TraitRef<'tcx>) {
+        debug!("TraitDef::record_impl for {:?}, from {:?}",
+               self, impl_trait_ref);
+
+        // Record the write into the impl set, but only for local
+        // impls: external impls are handled differently.
+        if impl_def_id.is_local() {
+            self.write_trait_impls(tcx);
+        }
+
+        // We don't want to borrow_mut after we already populated all impls,
+        // so check if an impl is present with an immutable borrow first.
+        if let Some(sty) = fast_reject::simplify_type(tcx,
+                                                      impl_trait_ref.self_ty(), false) {
+            if let Some(is) = self.nonblanket_impls.borrow().get(&sty) {
+                if is.contains(&impl_def_id) {
+                    return // duplicate - skip
+                }
+            }
+
+            self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id)
+        } else {
+            if self.blanket_impls.borrow().contains(&impl_def_id) {
+                return // duplicate - skip
+            }
+            self.blanket_impls.borrow_mut().push(impl_def_id)
+        }
+    }
+
+    pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &ty::ctxt<'tcx>, mut f: F)  {
+        self.read_trait_impls(tcx);
+
+        tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
+
+        for &impl_def_id in self.blanket_impls.borrow().iter() {
+            f(impl_def_id);
+        }
+
+        for v in self.nonblanket_impls.borrow().values() {
+            for &impl_def_id in v {
+                f(impl_def_id);
+            }
+        }
+    }
+
+    /// Iterate over every impl that could possibly match the
+    /// self-type `self_ty`.
+    pub fn for_each_relevant_impl<F: FnMut(DefId)>(&self,
+                                                   tcx: &ty::ctxt<'tcx>,
+                                                   self_ty: Ty<'tcx>,
+                                                   mut f: F)
+    {
+        self.read_trait_impls(tcx);
+
+        tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
+
+        for &impl_def_id in self.blanket_impls.borrow().iter() {
+            f(impl_def_id);
+        }
+
+        // simplify_type(.., false) basically replaces type parameters and
+        // projections with infer-variables. This is, of course, done on
+        // the impl trait-ref when it is instantiated, but not on the
+        // predicate trait-ref which is passed here.
+        //
+        // for example, if we match `S: Copy` against an impl like
+        // `impl<T:Copy> Copy for Option<T>`, we replace the type variable
+        // in `Option<T>` with an infer variable, to `Option<_>` (this
+        // doesn't actually change fast_reject output), but we don't
+        // replace `S` with anything - this impl of course can't be
+        // selected, and as there are hundreds of similar impls,
+        // considering them would significantly harm performance.
+        if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) {
+            if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) {
+                for &impl_def_id in impls {
+                    f(impl_def_id);
+                }
+            }
+        } else {
+            for v in self.nonblanket_impls.borrow().values() {
+                for &impl_def_id in v {
+                    f(impl_def_id);
+                }
+            }
+        }
+    }
+
+    pub fn borrow_impl_lists<'s>(&'s self, tcx: &ty::ctxt<'tcx>)
+                                 -> (Ref<'s, Vec<DefId>>,
+                                     Ref<'s, FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>>) {
+        self.read_trait_impls(tcx);
+        (self.blanket_impls.borrow(), self.nonblanket_impls.borrow())
+    }
+
+}
+
+bitflags! {
+    flags TraitFlags: u32 {
+        const NO_TRAIT_FLAGS        = 0,
+        const HAS_DEFAULT_IMPL      = 1 << 0,
+        const IS_OBJECT_SAFE        = 1 << 1,
+        const OBJECT_SAFETY_VALID   = 1 << 2,
+        const IMPLS_VALID           = 1 << 3,
+    }
+}
+
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 745be426676f6..0134bcdf1757b 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -125,6 +125,8 @@ pub struct Options {
     pub parse_only: bool,
     pub no_trans: bool,
     pub treat_err_as_bug: bool,
+    pub incremental_compilation: bool,
+    pub dump_dep_graph: bool,
     pub no_analysis: bool,
     pub debugging_opts: DebuggingOptions,
     pub prints: Vec<PrintRequest>,
@@ -234,6 +236,8 @@ pub fn basic_options() -> Options {
         parse_only: false,
         no_trans: false,
         treat_err_as_bug: false,
+        incremental_compilation: false,
+        dump_dep_graph: false,
         no_analysis: false,
         debugging_opts: basic_debugging_options(),
         prints: Vec::new(),
@@ -606,6 +610,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "run all passes except translation; no output"),
     treat_err_as_bug: bool = (false, parse_bool,
           "treat all errors that occur as bugs"),
+    incr_comp: bool = (false, parse_bool,
+          "enable incremental compilation (experimental)"),
+    dump_dep_graph: bool = (false, parse_bool,
+          "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"),
     no_analysis: bool = (false, parse_bool,
           "parse and expand the source, but run no analysis"),
     extra_plugins: Vec<String> = (Vec::new(), parse_list,
@@ -932,6 +940,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let parse_only = debugging_opts.parse_only;
     let no_trans = debugging_opts.no_trans;
     let treat_err_as_bug = debugging_opts.treat_err_as_bug;
+    let incremental_compilation = debugging_opts.incr_comp;
+    let dump_dep_graph = debugging_opts.dump_dep_graph;
     let no_analysis = debugging_opts.no_analysis;
 
     if debugging_opts.debug_llvm {
@@ -1106,6 +1116,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         parse_only: parse_only,
         no_trans: no_trans,
         treat_err_as_bug: treat_err_as_bug,
+        incremental_compilation: incremental_compilation || dump_dep_graph,
+        dump_dep_graph: dump_dep_graph,
         no_analysis: no_analysis,
         debugging_opts: debugging_opts,
         prints: prints,
diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs
index b5d259d9ac999..2481cab78b4d6 100644
--- a/src/librustc/util/common.rs
+++ b/src/librustc/util/common.rs
@@ -201,46 +201,38 @@ pub fn block_query<P>(b: &hir::Block, p: P) -> bool where P: FnMut(&hir::Expr) -
     return v.flag;
 }
 
-/// Memoizes a one-argument closure using the given RefCell containing
-/// a type implementing MutableMap to serve as a cache.
-///
-/// In the future the signature of this function is expected to be:
-/// ```
-/// pub fn memoized<T: Clone, U: Clone, M: MutableMap<T, U>>(
-///    cache: &RefCell<M>,
-///    f: &|T| -> U
-/// ) -> impl |T| -> U {
-/// ```
-/// but currently it is not possible.
-///
-/// # Examples
-/// ```
-/// struct Context {
-///    cache: RefCell<HashMap<usize, usize>>
-/// }
-///
-/// fn factorial(ctxt: &Context, n: usize) -> usize {
-///     memoized(&ctxt.cache, n, |n| match n {
-///         0 | 1 => n,
-///         _ => factorial(ctxt, n - 2) + factorial(ctxt, n - 1)
-///     })
-/// }
-/// ```
-#[inline(always)]
-pub fn memoized<T, U, S, F>(cache: &RefCell<HashMap<T, U, S>>, arg: T, f: F) -> U
-    where T: Clone + Hash + Eq,
-          U: Clone,
-          S: HashState,
-          F: FnOnce(T) -> U,
+pub trait MemoizationMap {
+    type Key: Clone;
+    type Value: Clone;
+
+    /// If `key` is present in the map, return the valuee,
+    /// otherwise invoke `op` and store the value in the map.
+    ///
+    /// NB: if the receiver is a `DepTrackingMap`, special care is
+    /// needed in the `op` to ensure that the correct edges are
+    /// added into the dep graph. See the `DepTrackingMap` impl for
+    /// more details!
+    fn memoize<OP>(&self, key: Self::Key, op: OP) -> Self::Value
+        where OP: FnOnce() -> Self::Value;
+}
+
+impl<K, V, S> MemoizationMap for RefCell<HashMap<K,V,S>>
+    where K: Hash+Eq+Clone, V: Clone, S: HashState
 {
-    let key = arg.clone();
-    let result = cache.borrow().get(&key).cloned();
-    match result {
-        Some(result) => result,
-        None => {
-            let result = f(arg);
-            cache.borrow_mut().insert(key, result.clone());
-            result
+    type Key = K;
+    type Value = V;
+
+    fn memoize<OP>(&self, key: K, op: OP) -> V
+        where OP: FnOnce() -> V
+    {
+        let result = self.borrow().get(&key).cloned();
+        match result {
+            Some(result) => result,
+            None => {
+                let result = op();
+                self.borrow_mut().insert(key, result.clone());
+                result
+            }
         }
     }
 }
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 0a2586755cef4..631149e69d77e 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -20,6 +20,7 @@ pub use self::MovedValueUseKind::*;
 
 use self::InteriorKind::*;
 
+use rustc::dep_graph::DepNode;
 use rustc::front::map as hir_map;
 use rustc::front::map::blocks::FnParts;
 use rustc::middle::cfg;
@@ -109,7 +110,7 @@ pub fn check_crate(tcx: &ty::ctxt) {
         }
     };
 
-    tcx.map.krate().visit_all_items(&mut bccx);
+    tcx.visit_all_items_in_krate(DepNode::BorrowCheck, &mut bccx);
 
     if tcx.sess.borrowck_stats() {
         println!("--- borrowck stats ---");
diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs
index 4a3810c822b4d..1ea09490aed2f 100644
--- a/src/librustc_data_structures/graph/mod.rs
+++ b/src/librustc_data_structures/graph/mod.rs
@@ -77,16 +77,16 @@ impl<E: Debug> Debug for Edge<E> {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub struct NodeIndex(pub usize);
 
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub struct EdgeIndex(pub usize);
 
 pub const INVALID_EDGE_INDEX: EdgeIndex = EdgeIndex(usize::MAX);
 
 // Use a private field here to guarantee no more instances are created:
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub struct Direction { repr: usize }
 
 pub const OUTGOING: Direction = Direction { repr: 0 };
@@ -410,4 +410,12 @@ impl<E> Edge<E> {
     pub fn target(&self) -> NodeIndex {
         self.target
     }
+
+    pub fn source_or_target(&self, direction: Direction) -> NodeIndex {
+        if direction == OUTGOING {
+            self.target
+        } else {
+            self.source
+        }
+    }
 }
diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs
index 3bba7d651ad71..ef64d7dde091c 100644
--- a/src/librustc_data_structures/lib.rs
+++ b/src/librustc_data_structures/lib.rs
@@ -40,6 +40,7 @@ pub mod transitive_relation;
 pub mod unify;
 pub mod fnv;
 pub mod tuple_slice;
+pub mod veccell;
 
 // See comments in src/librustc/lib.rs
 #[doc(hidden)]
diff --git a/src/librustc_data_structures/veccell/mod.rs b/src/librustc_data_structures/veccell/mod.rs
new file mode 100644
index 0000000000000..008642d9d6567
--- /dev/null
+++ b/src/librustc_data_structures/veccell/mod.rs
@@ -0,0 +1,47 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cell::UnsafeCell;
+use std::mem;
+
+pub struct VecCell<T> {
+    data: UnsafeCell<Vec<T>>
+}
+
+impl<T> VecCell<T> {
+    pub fn with_capacity(capacity: usize) -> VecCell<T>{
+        VecCell { data: UnsafeCell::new(Vec::with_capacity(capacity)) }
+    }
+
+    #[inline]
+    pub fn push(&self, data: T) -> usize {
+        // The logic here, and in `swap` below, is that the `push`
+        // method on the vector will not recursively access this
+        // `VecCell`. Therefore, we can temporarily obtain mutable
+        // access, secure in the knowledge that even if aliases exist
+        // -- indeed, even if aliases are reachable from within the
+        // vector -- they will not be used for the duration of this
+        // particular fn call. (Note that we also are relying on the
+        // fact that `VecCell` is not `Sync`.)
+        unsafe {
+            let v = self.data.get();
+            (*v).push(data);
+            (*v).len()
+        }
+    }
+
+    pub fn swap(&self, mut data: Vec<T>) -> Vec<T> {
+        unsafe {
+            let v = self.data.get();
+            mem::swap(&mut *v, &mut data);
+        }
+        data
+    }
+}
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 27740b8fc5cf9..d172bfb441358 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -801,7 +801,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
                                    time(time_passes,
                                         "rvalue checking",
-                                        || middle::check_rvalues::check_crate(tcx, krate));
+                                        || middle::check_rvalues::check_crate(tcx));
 
                                    // Avoid overwhelming user with errors if type checking failed.
                                    // I'm not sure how helpful this is, to be honest, but it avoids
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index 47b89e3be5d43..ba5ecc22e7474 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -204,6 +204,7 @@ impl PpSourceMode {
                                                         let annotation = TypedAnnotation {
                                                             tcx: tcx,
                                                         };
+                                                        let _ignore = tcx.dep_graph.in_ignore();
                                                         f(&annotation,
                                                           payload,
                                                           &ast_map.forest.krate)
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 54cccf087ebb6..0b48cad36ba8f 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -38,7 +38,7 @@ use middle::ty::{self, RegionEscape, Ty};
 use rustc::mir;
 use rustc::mir::visit::MutVisitor;
 
-use std::cell::{Cell, RefCell};
+use std::cell::Cell;
 use std::io::prelude::*;
 use std::io;
 use std::rc::Rc;
@@ -353,16 +353,11 @@ pub fn get_trait_def<'tcx>(cdata: Cmd,
     let associated_type_names = parse_associated_type_names(item_doc);
     let paren_sugar = parse_paren_sugar(item_doc);
 
-    ty::TraitDef {
-        paren_sugar: paren_sugar,
-        unsafety: unsafety,
-        generics: generics,
-        trait_ref: item_trait_ref(item_doc, tcx, cdata),
-        associated_type_names: associated_type_names,
-        nonblanket_impls: RefCell::new(FnvHashMap()),
-        blanket_impls: RefCell::new(vec![]),
-        flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
-    }
+    ty::TraitDef::new(unsafety,
+                      paren_sugar,
+                      generics,
+                      item_trait_ref(item_doc, tcx, cdata),
+                      associated_type_names)
 }
 
 pub fn get_adt_def<'tcx>(intr: &IdentInterner,
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs
index 5c9399ebdadf7..08174272a9bc5 100644
--- a/src/librustc_mir/mir_map.rs
+++ b/src/librustc_mir/mir_map.rs
@@ -23,6 +23,7 @@ extern crate rustc_front;
 use build;
 use graphviz;
 use transform::*;
+use rustc::dep_graph::DepNode;
 use rustc::mir::repr::Mir;
 use hair::cx::Cx;
 use std::fs::File;
@@ -47,7 +48,7 @@ pub fn build_mir_for_crate<'tcx>(tcx: &ty::ctxt<'tcx>) -> MirMap<'tcx> {
             tcx: tcx,
             map: &mut map,
         };
-        tcx.map.krate().visit_all_items(&mut dump);
+        tcx.visit_all_items_in_krate(DepNode::MirMapConstruction, &mut dump);
     }
     map
 }
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 1cc2482a39e27..955e68be0b006 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -35,6 +35,7 @@ use std::mem::replace;
 use rustc_front::hir;
 use rustc_front::intravisit::{self, Visitor};
 
+use rustc::dep_graph::DepNode;
 use rustc::lint;
 use rustc::middle::def;
 use rustc::middle::def_id::DefId;
@@ -1674,6 +1675,8 @@ pub fn check_crate(tcx: &ty::ctxt,
                    export_map: &def::ExportMap,
                    external_exports: ExternalExports)
                    -> AccessLevels {
+    let _task = tcx.dep_graph.in_task(DepNode::Privacy);
+
     let krate = tcx.map.krate();
 
     // Sanity check to make sure that all privacy usage and controls are
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 8446db65a4ccb..e1edbf4a1276d 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -182,8 +182,10 @@ pub fn find_crate_name(sess: Option<&Session>,
     "rust_out".to_string()
 }
 
-pub fn build_link_meta(sess: &Session, krate: &hir::Crate,
-                       name: &str) -> LinkMeta {
+pub fn build_link_meta(sess: &Session,
+                       krate: &hir::Crate,
+                       name: &str)
+                       -> LinkMeta {
     let r = LinkMeta {
         crate_name: name.to_owned(),
         crate_hash: Svh::calculate(&sess.opts.cg.metadata, krate),
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index a17e0a4ccd78d..0892cf1b5d33e 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -27,6 +27,7 @@
 #![feature(const_fn)]
 #![feature(custom_attribute)]
 #![allow(unused_attributes)]
+#![feature(into_cow)]
 #![feature(iter_arith)]
 #![feature(libc)]
 #![feature(path_relative_from)]
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 501ab566f1c5a..e1343c73acfa9 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -716,6 +716,8 @@ pub fn process_crate<'l, 'tcx>(tcx: &'l ty::ctxt<'tcx>,
                                analysis: &ty::CrateAnalysis,
                                cratename: &str,
                                odir: Option<&Path>) {
+    let _ignore = tcx.dep_graph.in_ignore();
+
     if generated_code(krate.span) {
         return;
     }
diff --git a/src/librustc_trans/trans/assert_dep_graph.rs b/src/librustc_trans/trans/assert_dep_graph.rs
new file mode 100644
index 0000000000000..924700f0ae591
--- /dev/null
+++ b/src/librustc_trans/trans/assert_dep_graph.rs
@@ -0,0 +1,430 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This pass is only used for the UNIT TESTS and DEBUGGING NEEDS
+//! around dependency graph construction. It serves two purposes; it
+//! will dump graphs in graphviz form to disk, and it searches for
+//! `#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]`
+//! annotations. These annotations can be used to test whether paths
+//! exist in the graph. We report errors on each
+//! `rustc_if_this_changed` annotation. If a path exists in all
+//! cases, then we would report "all path(s) exist". Otherwise, we
+//! report: "no path to `foo`" for each case where no path exists.
+//! `compile-fail` tests can then be used to check when paths exist or
+//! do not.
+//!
+//! The full form of the `rustc_if_this_changed` annotation is
+//! `#[rustc_if_this_changed(id)]`. The `"id"` is optional and
+//! defaults to `"id"` if omitted.
+//!
+//! Example:
+//!
+//! ```
+//! #[rustc_if_this_changed]
+//! fn foo() { }
+//!
+//! #[rustc_then_this_would_need("trans")] //~ ERROR no path from `foo`
+//! fn bar() { }
+//!
+//! #[rustc_then_this_would_need("trans")] //~ ERROR OK
+//! fn baz() { foo(); }
+//! ```
+
+use graphviz as dot;
+use rustc::dep_graph::{DepGraphQuery, DepNode};
+use rustc::middle::def_id::DefId;
+use rustc::middle::ty;
+use rustc_data_structures::fnv::{FnvHashMap, FnvHashSet};
+use rustc_data_structures::graph::{Direction, INCOMING, OUTGOING, NodeIndex};
+use rustc_front::hir;
+use rustc_front::intravisit::Visitor;
+use std::borrow::IntoCow;
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use syntax::ast;
+use syntax::attr::AttrMetaMethods;
+use syntax::codemap::Span;
+use syntax::parse::token::InternedString;
+
+const IF_THIS_CHANGED: &'static str = "rustc_if_this_changed";
+const THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need";
+const ID: &'static str = "id";
+
+pub fn assert_dep_graph(tcx: &ty::ctxt) {
+    let _ignore = tcx.dep_graph.in_ignore();
+
+    if tcx.sess.opts.dump_dep_graph {
+        dump_graph(tcx);
+    }
+
+    // Find annotations supplied by user (if any).
+    let (if_this_changed, then_this_would_need) = {
+        let mut visitor = IfThisChanged { tcx: tcx,
+                                          if_this_changed: FnvHashMap(),
+                                          then_this_would_need: FnvHashMap() };
+        tcx.map.krate().visit_all_items(&mut visitor);
+        (visitor.if_this_changed, visitor.then_this_would_need)
+    };
+
+    // Check paths.
+    check_paths(tcx, &if_this_changed, &then_this_would_need);
+}
+
+type SourceHashMap = FnvHashMap<InternedString,
+                                FnvHashSet<(Span, DefId, DepNode)>>;
+type TargetHashMap = FnvHashMap<InternedString,
+                                FnvHashSet<(Span, InternedString, ast::NodeId, DepNode)>>;
+
+struct IfThisChanged<'a, 'tcx:'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+    if_this_changed: SourceHashMap,
+    then_this_would_need: TargetHashMap,
+}
+
+impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
+    fn process_attrs(&mut self, node_id: ast::NodeId, def_id: DefId) {
+        for attr in self.tcx.get_attrs(def_id).iter() {
+            if attr.check_name(IF_THIS_CHANGED) {
+                let mut id = None;
+                for meta_item in attr.meta_item_list().unwrap_or_default() {
+                    match meta_item.node {
+                        ast::MetaWord(ref s) if id.is_none() => id = Some(s.clone()),
+                        _ => {
+                            self.tcx.sess.span_err(
+                                meta_item.span,
+                                &format!("unexpected meta-item {:?}", meta_item.node));
+                        }
+                    }
+                }
+                let id = id.unwrap_or(InternedString::new(ID));
+                self.if_this_changed.entry(id)
+                                    .or_insert(FnvHashSet())
+                                    .insert((attr.span, def_id, DepNode::Hir(def_id)));
+            } else if attr.check_name(THEN_THIS_WOULD_NEED) {
+                let mut dep_node_interned = None;
+                let mut id = None;
+                for meta_item in attr.meta_item_list().unwrap_or_default() {
+                    match meta_item.node {
+                        ast::MetaWord(ref s) if dep_node_interned.is_none() =>
+                            dep_node_interned = Some(s.clone()),
+                        ast::MetaWord(ref s) if id.is_none() =>
+                            id = Some(s.clone()),
+                        _ => {
+                            self.tcx.sess.span_err(
+                                meta_item.span,
+                                &format!("unexpected meta-item {:?}", meta_item.node));
+                        }
+                    }
+                }
+                let dep_node_str = dep_node_interned.as_ref().map(|s| &**s);
+                macro_rules! match_depnode_name {
+                    ($input:expr, $def_id:expr, match { $($variant:ident,)* } else $y:expr) => {
+                        match $input {
+                            $(Some(stringify!($variant)) => DepNode::$variant($def_id),)*
+                            _ => $y
+                        }
+                    }
+                }
+                let dep_node = match_depnode_name! {
+                    dep_node_str, def_id, match {
+                        CollectItem,
+                        BorrowCheck,
+                        TransCrateItem,
+                        TypeckItemType,
+                        TypeckItemBody,
+                        ImplOrTraitItems,
+                        ItemSignature,
+                        FieldTy,
+                        TraitItemDefIds,
+                        InherentImpls,
+                        ImplItems,
+                        TraitImpls,
+                        ReprHints,
+                    } else {
+                        self.tcx.sess.span_fatal(
+                            attr.span,
+                            &format!("unrecognized DepNode variant {:?}", dep_node_str));
+                    }
+                };
+                let id = id.unwrap_or(InternedString::new(ID));
+                self.then_this_would_need
+                    .entry(id)
+                    .or_insert(FnvHashSet())
+                    .insert((attr.span, dep_node_interned.clone().unwrap(), node_id, dep_node));
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for IfThisChanged<'a, 'tcx> {
+    fn visit_item(&mut self, item: &'tcx hir::Item) {
+        let def_id = self.tcx.map.local_def_id(item.id);
+        self.process_attrs(item.id, def_id);
+    }
+}
+
+fn check_paths(tcx: &ty::ctxt,
+               if_this_changed: &SourceHashMap,
+               then_this_would_need: &TargetHashMap)
+{
+    // Return early here so as not to construct the query, which is not cheap.
+    if if_this_changed.is_empty() {
+        return;
+    }
+    let query = tcx.dep_graph.query();
+    for (id, sources) in if_this_changed {
+        let targets = match then_this_would_need.get(id) {
+            Some(targets) => targets,
+            None => {
+                for &(source_span, _, _) in sources.iter().take(1) {
+                    tcx.sess.span_err(
+                        source_span,
+                        &format!("no targets for id `{}`", id));
+                }
+                continue;
+            }
+        };
+
+        for &(_, source_def_id, source_dep_node) in sources {
+            let dependents = query.dependents(source_dep_node);
+            for &(target_span, ref target_pass, _, ref target_dep_node) in targets {
+                if !dependents.contains(&target_dep_node) {
+                    tcx.sess.span_err(
+                        target_span,
+                        &format!("no path from `{}` to `{}`",
+                                 tcx.item_path_str(source_def_id),
+                                 target_pass));
+                } else {
+                    tcx.sess.span_err(
+                        target_span,
+                        &format!("OK"));
+                }
+            }
+        }
+    }
+}
+
+fn dump_graph(tcx: &ty::ctxt) {
+    let path: String = env::var("RUST_DEP_GRAPH").unwrap_or_else(|_| format!("dep_graph"));
+    let query = tcx.dep_graph.query();
+
+    let nodes = match env::var("RUST_DEP_GRAPH_FILTER") {
+        Ok(string) => {
+            // Expect one of: "-> target", "source -> target", or "source ->".
+            let parts: Vec<_> = string.split("->").collect();
+            if parts.len() > 2 {
+                panic!("Invalid RUST_DEP_GRAPH_FILTER: expected '[source] -> [target]'");
+            }
+            let sources = node_set(&query, &parts[0]);
+            let targets = node_set(&query, &parts[1]);
+            filter_nodes(&query, &sources, &targets)
+        }
+        Err(_) => {
+            query.nodes()
+                 .into_iter()
+                 .collect()
+        }
+    };
+    let edges = filter_edges(&query, &nodes);
+
+    { // dump a .txt file with just the edges:
+        let txt_path = format!("{}.txt", path);
+        let mut file = File::create(&txt_path).unwrap();
+        for &(source, target) in &edges {
+            write!(file, "{:?} -> {:?}\n", source, target).unwrap();
+        }
+    }
+
+    { // dump a .dot file in graphviz format:
+        let dot_path = format!("{}.dot", path);
+        let mut v = Vec::new();
+        dot::render(&GraphvizDepGraph(nodes, edges), &mut v).unwrap();
+        File::create(&dot_path).and_then(|mut f| f.write_all(&v)).unwrap();
+    }
+}
+
+pub struct GraphvizDepGraph(FnvHashSet<DepNode>, Vec<(DepNode, DepNode)>);
+
+impl<'a, 'tcx> dot::GraphWalk<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph {
+    fn nodes(&self) -> dot::Nodes<DepNode> {
+        let nodes: Vec<_> = self.0.iter().cloned().collect();
+        nodes.into_cow()
+    }
+    fn edges(&self) -> dot::Edges<(DepNode, DepNode)> {
+        self.1[..].into_cow()
+    }
+    fn source(&self, edge: &(DepNode, DepNode)) -> DepNode {
+        edge.0
+    }
+    fn target(&self, edge: &(DepNode, DepNode)) -> DepNode {
+        edge.1
+    }
+}
+
+impl<'a, 'tcx> dot::Labeller<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph {
+    fn graph_id(&self) -> dot::Id {
+        dot::Id::new("DependencyGraph").unwrap()
+    }
+    fn node_id(&self, n: &DepNode) -> dot::Id {
+        let s: String =
+            format!("{:?}", n).chars()
+                              .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
+                              .collect();
+        debug!("n={:?} s={:?}", n, s);
+        dot::Id::new(s).unwrap()
+    }
+    fn node_label(&self, n: &DepNode) -> dot::LabelText {
+        dot::LabelText::label(format!("{:?}", n))
+    }
+}
+
+// Given an optional filter like `"x,y,z"`, returns either `None` (no
+// filter) or the set of nodes whose labels contain all of those
+// substrings.
+fn node_set(query: &DepGraphQuery, filter: &str) -> Option<FnvHashSet<DepNode>> {
+    debug!("node_set(filter={:?})", filter);
+
+    if filter.trim().is_empty() {
+        return None;
+    }
+
+    let filters: Vec<&str> = filter.split("&").map(|s| s.trim()).collect();
+
+    debug!("node_set: filters={:?}", filters);
+
+    Some(query.nodes()
+         .into_iter()
+         .filter(|n| {
+             let s = format!("{:?}", n);
+             filters.iter().all(|f| s.contains(f))
+         })
+        .collect())
+}
+
+fn filter_nodes(query: &DepGraphQuery,
+                sources: &Option<FnvHashSet<DepNode>>,
+                targets: &Option<FnvHashSet<DepNode>>)
+                -> FnvHashSet<DepNode>
+{
+    if let &Some(ref sources) = sources {
+        if let &Some(ref targets) = targets {
+            walk_between(query, sources, targets)
+        } else {
+            walk_nodes(query, sources, OUTGOING)
+        }
+    } else if let &Some(ref targets) = targets {
+        walk_nodes(query, targets, INCOMING)
+    } else {
+        query.nodes().into_iter().collect()
+    }
+}
+
+fn walk_nodes(query: &DepGraphQuery,
+              starts: &FnvHashSet<DepNode>,
+              direction: Direction)
+              -> FnvHashSet<DepNode>
+{
+    let mut set = FnvHashSet();
+    for start in starts {
+        debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
+        if set.insert(*start) {
+            let mut stack = vec![query.indices[start]];
+            while let Some(index) = stack.pop() {
+                for (_, edge) in query.graph.adjacent_edges(index, direction) {
+                    let neighbor_index = edge.source_or_target(direction);
+                    let neighbor = query.graph.node_data(neighbor_index);
+                    if set.insert(*neighbor) {
+                        stack.push(neighbor_index);
+                    }
+                }
+            }
+        }
+    }
+    set
+}
+
+fn walk_between(query: &DepGraphQuery,
+                sources: &FnvHashSet<DepNode>,
+                targets: &FnvHashSet<DepNode>)
+                -> FnvHashSet<DepNode>
+{
+    // This is a bit tricky. We want to include a node only if it is:
+    // (a) reachable from a source and (b) will reach a target. And we
+    // have to be careful about cycles etc.  Luckily efficiency is not
+    // a big concern!
+
+    #[derive(Copy, Clone, PartialEq)]
+    enum State { Undecided, Deciding, Included, Excluded }
+
+    let mut node_states = vec![State::Undecided; query.graph.len_nodes()];
+
+    for &target in targets {
+        node_states[query.indices[&target].0] = State::Included;
+    }
+
+    for source in sources.iter().map(|n| query.indices[n]) {
+        recurse(query, &mut node_states, source);
+    }
+
+    return query.nodes()
+                .into_iter()
+                .filter(|n| {
+                    let index = query.indices[n];
+                    node_states[index.0] == State::Included
+                })
+                .collect();
+
+    fn recurse(query: &DepGraphQuery,
+               node_states: &mut [State],
+               node: NodeIndex)
+               -> bool
+    {
+        match node_states[node.0] {
+            // known to reach a target
+            State::Included => return true,
+
+            // known not to reach a target
+            State::Excluded => return false,
+
+            // backedge, not yet known, say false
+            State::Deciding => return false,
+
+            State::Undecided => { }
+        }
+
+        node_states[node.0] = State::Deciding;
+
+        for neighbor_index in query.graph.successor_nodes(node) {
+            if recurse(query, node_states, neighbor_index) {
+                node_states[node.0] = State::Included;
+            }
+        }
+
+        // if we didn't find a path to target, then set to excluded
+        if node_states[node.0] == State::Deciding {
+            node_states[node.0] = State::Excluded;
+            false
+        } else {
+            assert!(node_states[node.0] == State::Included);
+            true
+        }
+    }
+}
+
+fn filter_edges(query: &DepGraphQuery,
+                nodes: &FnvHashSet<DepNode>)
+                -> Vec<(DepNode, DepNode)>
+{
+    query.edges()
+         .into_iter()
+         .filter(|&(source, target)| nodes.contains(&source) && nodes.contains(&target))
+         .collect()
+}
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 4197f80cb5ea3..810803762a853 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -43,6 +43,7 @@ use middle::weak_lang_items;
 use middle::pat_util::simple_name;
 use middle::subst::Substs;
 use middle::ty::{self, Ty, HasTypeFlags};
+use rustc::dep_graph::DepNode;
 use rustc::front::map as hir_map;
 use rustc::util::common::time;
 use rustc_mir::mir_map::MirMap;
@@ -50,6 +51,7 @@ use session::config::{self, NoDebugInfo, FullDebugInfo};
 use session::Session;
 use trans::_match;
 use trans::adt;
+use trans::assert_dep_graph;
 use trans::attributes;
 use trans::build::*;
 use trans::builder::{Builder, noname};
@@ -2978,9 +2980,16 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>,
                          mir_map: &MirMap<'tcx>,
                          analysis: ty::CrateAnalysis)
                          -> CrateTranslation {
-    let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis;
+    let _task = tcx.dep_graph.in_task(DepNode::TransCrate);
+
+    // Be careful with this krate: obviously it gives access to the
+    // entire contents of the krate. So if you push any subtasks of
+    // `TransCrate`, you need to be careful to register "reads" of the
+    // particular items that will be processed.
     let krate = tcx.map.krate();
 
+    let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis;
+
     let check_overflow = if let Some(v) = tcx.sess.opts.debugging_opts.force_overflow_checks {
         v
     } else {
@@ -3134,6 +3143,8 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>,
     };
     let no_builtins = attr::contains_name(&krate.attrs, "no_builtins");
 
+    assert_dep_graph::assert_dep_graph(tcx);
+
     CrateTranslation {
         modules: modules,
         metadata_module: metadata_module,
@@ -3186,7 +3197,16 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TransItemsWithinModVisitor<'a, 'tcx> {
                 // skip modules, they will be uncovered by the TransModVisitor
             }
             _ => {
-                trans_item(self.ccx, i);
+                let def_id = self.ccx.tcx().map.local_def_id(i.id);
+                let tcx = self.ccx.tcx();
+
+                // Create a subtask for trans'ing a particular item. We are
+                // giving `trans_item` access to this item, so also record a read.
+                tcx.dep_graph.with_task(DepNode::TransCrateItem(def_id), || {
+                    tcx.dep_graph.read(DepNode::Hir(def_id));
+                    trans_item(self.ccx, i);
+                });
+
                 intravisit::walk_item(self, i);
             }
         }
diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs
index 29965755eac76..89399043c964e 100644
--- a/src/librustc_trans/trans/inline.rs
+++ b/src/librustc_trans/trans/inline.rs
@@ -15,12 +15,13 @@ use middle::subst::Substs;
 use trans::base::{push_ctxt, trans_item, get_item_val, trans_fn};
 use trans::common::*;
 
+use rustc::dep_graph::DepNode;
 use rustc_front::hir;
 
-fn instantiate_inline(ccx: &CrateContext, fn_id: DefId)
-    -> Option<DefId> {
+fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
     debug!("instantiate_inline({:?})", fn_id);
     let _icx = push_ctxt("instantiate_inline");
+    let _task = ccx.tcx().dep_graph.in_task(DepNode::TransInlinedItem(fn_id));
 
     match ccx.external().borrow().get(&fn_id) {
         Some(&Some(node_id)) => {
diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs
index 66f53deffa9f6..d8a3cc50ff4ee 100644
--- a/src/librustc_trans/trans/intrinsic.rs
+++ b/src/librustc_trans/trans/intrinsic.rs
@@ -37,6 +37,7 @@ use trans::machine;
 use trans::type_::Type;
 use middle::ty::{self, Ty, HasTypeFlags};
 use middle::subst::Substs;
+use rustc::dep_graph::DepNode;
 use rustc_front::hir;
 use syntax::abi::{self, RustIntrinsic};
 use syntax::ast;
@@ -101,6 +102,7 @@ pub fn span_transmute_size_error(a: &Session, b: Span, msg: &str) {
 /// Performs late verification that intrinsics are used correctly. At present,
 /// the only intrinsic that needs such verification is `transmute`.
 pub fn check_intrinsics(ccx: &CrateContext) {
+    let _task = ccx.tcx().dep_graph.in_task(DepNode::IntrinsicUseCheck);
     let mut last_failing_id = None;
     for transmute_restriction in ccx.tcx().transmute_restrictions.borrow().iter() {
         // Sometimes, a single call to transmute will push multiple
diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs
index b102e96af20e2..1fbc0d5c01529 100644
--- a/src/librustc_trans/trans/mod.rs
+++ b/src/librustc_trans/trans/mod.rs
@@ -20,6 +20,7 @@ mod macros;
 
 mod adt;
 mod asm;
+mod assert_dep_graph;
 mod attributes;
 mod base;
 mod basic_block;
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index ca2db8c3deffb..14adc84f701f8 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -82,6 +82,7 @@ use self::TupleArgumentsFlag::*;
 
 use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode};
 use check::_match::pat_ctxt;
+use dep_graph::DepNode;
 use fmt_macros::{Parser, Piece, Position};
 use middle::astconv_util::prohibit_type_params;
 use middle::cstore::LOCAL_CRATE;
@@ -384,34 +385,33 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> {
 
 pub fn check_wf_new(ccx: &CrateCtxt) {
     ccx.tcx.sess.abort_if_new_errors(|| {
-        let krate = ccx.tcx.map.krate();
         let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(ccx);
-        krate.visit_all_items(&mut visit);
+        ccx.tcx.visit_all_items_in_krate(DepNode::WfCheck, &mut visit);
     });
 }
 
 pub fn check_item_types(ccx: &CrateCtxt) {
     ccx.tcx.sess.abort_if_new_errors(|| {
-        let krate = ccx.tcx.map.krate();
         let mut visit = CheckItemTypesVisitor { ccx: ccx };
-        krate.visit_all_items(&mut visit);
+        ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemType, &mut visit);
     });
 }
 
 pub fn check_item_bodies(ccx: &CrateCtxt) {
     ccx.tcx.sess.abort_if_new_errors(|| {
-        let krate = ccx.tcx.map.krate();
         let mut visit = CheckItemBodiesVisitor { ccx: ccx };
-        krate.visit_all_items(&mut visit);
+        ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemBody, &mut visit);
     });
 }
 
 pub fn check_drop_impls(ccx: &CrateCtxt) {
     ccx.tcx.sess.abort_if_new_errors(|| {
+        let _task = ccx.tcx.dep_graph.in_task(DepNode::Dropck);
         let drop_trait = match ccx.tcx.lang_items.drop_trait() {
             Some(id) => ccx.tcx.lookup_trait_def(id), None => { return }
         };
         drop_trait.for_each_impl(ccx.tcx, |drop_impl_did| {
+            let _task = ccx.tcx.dep_graph.in_task(DepNode::DropckImpl(drop_impl_did));
             if drop_impl_did.is_local() {
                 match dropck::check_drop_impl(ccx.tcx, drop_impl_did) {
                     Ok(()) => {}
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 02be74a590661..07c920829d978 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -39,9 +39,10 @@ use std::rc::Rc;
 use syntax::codemap::Span;
 use syntax::parse::token;
 use util::nodemap::{DefIdMap, FnvHashMap};
+use rustc::dep_graph::DepNode;
 use rustc::front::map as hir_map;
 use rustc_front::intravisit;
-use rustc_front::hir::{Item, ItemImpl,Crate};
+use rustc_front::hir::{Item, ItemImpl};
 use rustc_front::hir;
 
 mod orphan;
@@ -104,11 +105,13 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CoherenceCheckVisitor<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
-    fn check(&self, krate: &Crate) {
+    fn check(&self) {
         // Check implementations and traits. This populates the tables
         // containing the inherent methods and extension methods. It also
         // builds up the trait inheritance table.
-        krate.visit_all_items(&mut CoherenceCheckVisitor { cc: self });
+        self.crate_context.tcx.visit_all_items_in_krate(
+            DepNode::CoherenceCheckImpl,
+            &mut CoherenceCheckVisitor { cc: self });
 
         // Copy over the inherent impls we gathered up during the walk into
         // the tcx.
@@ -513,11 +516,13 @@ fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id:
 }
 
 pub fn check_coherence(crate_context: &CrateCtxt) {
+    let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence);
+    let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true);
     CoherenceChecker {
         crate_context: crate_context,
-        inference_context: new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true),
+        inference_context: infcx,
         inherent_impls: RefCell::new(FnvHashMap()),
-    }.check(crate_context.tcx.map.krate());
+    }.check();
     unsafety::check(crate_context.tcx);
     orphan::check(crate_context.tcx);
     overlap::check(crate_context.tcx);
diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs
index 76be04bb1741a..69eb7f51f3785 100644
--- a/src/librustc_typeck/coherence/orphan.rs
+++ b/src/librustc_typeck/coherence/orphan.rs
@@ -17,12 +17,13 @@ use middle::traits;
 use middle::ty;
 use syntax::ast;
 use syntax::codemap::Span;
+use rustc::dep_graph::DepNode;
 use rustc_front::intravisit;
 use rustc_front::hir;
 
 pub fn check(tcx: &ty::ctxt) {
     let mut orphan = OrphanChecker { tcx: tcx };
-    tcx.map.krate().visit_all_items(&mut orphan);
+    tcx.visit_all_items_in_krate(DepNode::CoherenceOrphanCheck, &mut orphan);
 }
 
 struct OrphanChecker<'cx, 'tcx:'cx> {
@@ -234,10 +235,10 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
                     }
                     Err(traits::OrphanCheckErr::UncoveredTy(param_ty)) => {
                         span_err!(self.tcx.sess, item.span, E0210,
-                                "type parameter `{}` must be used as the type parameter for \
-                                 some local type (e.g. `MyStruct<T>`); only traits defined in \
-                                 the current crate can be implemented for a type parameter",
-                                param_ty);
+                                  "type parameter `{}` must be used as the type parameter for \
+                                   some local type (e.g. `MyStruct<T>`); only traits defined in \
+                                   the current crate can be implemented for a type parameter",
+                                  param_ty);
                         return;
                     }
                 }
diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs
index beb409f7f0f23..71c6fc1fd08ec 100644
--- a/src/librustc_typeck/coherence/overlap.rs
+++ b/src/librustc_typeck/coherence/overlap.rs
@@ -18,53 +18,53 @@ use middle::ty;
 use middle::infer;
 use syntax::ast;
 use syntax::codemap::Span;
+use rustc::dep_graph::DepNode;
 use rustc_front::hir;
 use rustc_front::intravisit;
-use util::nodemap::DefIdMap;
+use util::nodemap::{DefIdMap, DefIdSet};
 
 pub fn check(tcx: &ty::ctxt) {
-    let mut overlap = OverlapChecker { tcx: tcx, default_impls: DefIdMap() };
-    overlap.check_for_overlapping_impls();
+    let mut overlap = OverlapChecker { tcx: tcx,
+                                       traits_checked: DefIdSet(),
+                                       default_impls: DefIdMap() };
 
     // this secondary walk specifically checks for some other cases,
     // like defaulted traits, for which additional overlap rules exist
-    tcx.map.krate().visit_all_items(&mut overlap);
+    tcx.visit_all_items_in_krate(DepNode::CoherenceOverlapCheckSpecial, &mut overlap);
 }
 
 struct OverlapChecker<'cx, 'tcx:'cx> {
     tcx: &'cx ty::ctxt<'tcx>,
 
+    // The set of traits where we have checked for overlap.  This is
+    // used to avoid checking the same trait twice.
+    //
+    // NB. It's ok to skip tracking this set because we fully
+    // encapsulate it, and we always create a task
+    // (`CoherenceOverlapCheck`) corresponding to each entry.
+    traits_checked: DefIdSet,
+
     // maps from a trait def-id to an impl id
     default_impls: DefIdMap<ast::NodeId>,
 }
 
 impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
-    fn check_for_overlapping_impls(&self) {
-        debug!("check_for_overlapping_impls");
-
-        // Collect this into a vector to avoid holding the
-        // refcell-lock during the
-        // check_for_overlapping_impls_of_trait() check, since that
-        // check can populate this table further with impls from other
-        // crates.
-        let trait_defs: Vec<_> = self.tcx.trait_defs.borrow().values().cloned().collect();
-
-        for trait_def in trait_defs {
-            self.tcx.populate_implementations_for_trait_if_necessary(trait_def.trait_ref.def_id);
-            self.check_for_overlapping_impls_of_trait(trait_def);
+    fn check_for_overlapping_impls_of_trait(&mut self, trait_def_id: DefId) {
+        debug!("check_for_overlapping_impls_of_trait(trait_def_id={:?})",
+               trait_def_id);
+
+        let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id));
+        if !self.traits_checked.insert(trait_def_id) {
+            return;
         }
-    }
 
-    fn check_for_overlapping_impls_of_trait(&self,
-                                            trait_def: &'tcx ty::TraitDef<'tcx>)
-    {
-        debug!("check_for_overlapping_impls_of_trait(trait_def={:?})",
-               trait_def);
+        let trait_def = self.tcx.lookup_trait_def(trait_def_id);
+        self.tcx.populate_implementations_for_trait_if_necessary(
+            trait_def.trait_ref.def_id);
 
         // We should already know all impls of this trait, so these
         // borrows are safe.
-        let blanket_impls = trait_def.blanket_impls.borrow();
-        let nonblanket_impls = trait_def.nonblanket_impls.borrow();
+        let (blanket_impls, nonblanket_impls) = trait_def.borrow_impl_lists(self.tcx);
 
         // Conflicts can only occur between a blanket impl and another impl,
         // or between 2 non-blanket impls of the same kind.
@@ -175,12 +175,20 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
 impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
     fn visit_item(&mut self, item: &'v hir::Item) {
         match item.node {
-            hir::ItemDefaultImpl(_, _) => {
+            hir::ItemTrait(..) => {
+                let trait_def_id = self.tcx.map.local_def_id(item.id);
+                self.check_for_overlapping_impls_of_trait(trait_def_id);
+            }
+
+            hir::ItemDefaultImpl(..) => {
                 // look for another default impl; note that due to the
                 // general orphan/coherence rules, it must always be
                 // in this crate.
                 let impl_def_id = self.tcx.map.local_def_id(item.id);
                 let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
+
+                self.check_for_overlapping_impls_of_trait(trait_ref.def_id);
+
                 let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id);
                 match prev_default_impl {
                     Some(prev_id) => {
@@ -195,6 +203,7 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
                 let impl_def_id = self.tcx.map.local_def_id(item.id);
                 let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
                 let trait_def_id = trait_ref.def_id;
+                self.check_for_overlapping_impls_of_trait(trait_def_id);
                 match trait_ref.self_ty().sty {
                     ty::TyTrait(ref data) => {
                         // This is something like impl Trait1 for Trait2. Illegal
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index eaaa2c773791e..5a9b899175837 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -34,13 +34,12 @@ lazilly and on demand, and include logic that checks for cycles.
 Demand is driven by calls to `AstConv::get_item_type_scheme` or
 `AstConv::lookup_trait_def`.
 
-Currently, we "convert" types and traits in three phases (note that
+Currently, we "convert" types and traits in two phases (note that
 conversion only affects the types of items / enum variants / methods;
 it does not e.g. compute the types of individual expressions):
 
 0. Intrinsics
-1. Trait definitions
-2. Type definitions
+1. Trait/Type definitions
 
 Conversion itself is done by simply walking each of the items in turn
 and invoking an appropriate function (e.g., `trait_def_of_item` or
@@ -56,11 +55,6 @@ There are some shortcomings in this design:
 - Because the type scheme includes defaults, cycles through type
   parameter defaults are illegal even if those defaults are never
   employed. This is not necessarily a bug.
-- The phasing of trait definitions before type definitions does not
-  seem to be necessary, sufficient, or particularly helpful, given that
-  processing a trait definition can trigger processing a type def and
-  vice versa. However, if I remove it, I get ICEs, so some more work is
-  needed in that area. -nmatsakis
 
 */
 
@@ -79,12 +73,13 @@ use middle::ty::{VariantKind};
 use middle::ty::fold::{TypeFolder};
 use middle::ty::util::IntTypeExt;
 use rscope::*;
+use rustc::dep_graph::DepNode;
 use rustc::front::map as hir_map;
-use util::common::{ErrorReported, memoized};
+use util::common::{ErrorReported, MemoizationMap};
 use util::nodemap::{FnvHashMap, FnvHashSet};
 use write_ty_to_tcx;
 
-use std::cell::{Cell, RefCell};
+use std::cell::RefCell;
 use std::collections::HashSet;
 use std::rc::Rc;
 
@@ -104,9 +99,6 @@ use rustc_front::print::pprust;
 pub fn collect_item_types(tcx: &ty::ctxt) {
     let ccx = &CrateCtxt { tcx: tcx, stack: RefCell::new(Vec::new()) };
 
-    let mut visitor = CollectTraitDefVisitor{ ccx: ccx };
-    ccx.tcx.map.krate().visit_all_items(&mut visitor);
-
     let mut visitor = CollectItemTypesVisitor{ ccx: ccx };
     ccx.tcx.map.krate().visit_all_items(&mut visitor);
 }
@@ -146,41 +138,17 @@ enum AstConvRequest {
 }
 
 ///////////////////////////////////////////////////////////////////////////
-// First phase: just collect *trait definitions* -- basically, the set
-// of type parameters and supertraits. This is information we need to
-// know later when parsing field defs.
-
-struct CollectTraitDefVisitor<'a, 'tcx: 'a> {
-    ccx: &'a CrateCtxt<'a, 'tcx>
-}
-
-impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CollectTraitDefVisitor<'a, 'tcx> {
-    fn visit_item(&mut self, i: &hir::Item) {
-        match i.node {
-            hir::ItemTrait(..) => {
-                // computing the trait def also fills in the table
-                let _ = trait_def_of_item(self.ccx, i);
-            }
-            _ => { }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Second phase: collection proper.
 
 struct CollectItemTypesVisitor<'a, 'tcx: 'a> {
     ccx: &'a CrateCtxt<'a, 'tcx>
 }
 
 impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CollectItemTypesVisitor<'a, 'tcx> {
-    fn visit_item(&mut self, i: &hir::Item) {
-        convert_item(self.ccx, i);
-        intravisit::walk_item(self, i);
-    }
-    fn visit_foreign_item(&mut self, i: &hir::ForeignItem) {
-        convert_foreign_item(self.ccx, i);
-        intravisit::walk_foreign_item(self, i);
+    fn visit_item(&mut self, item: &hir::Item) {
+        let tcx = self.ccx.tcx;
+        let item_def_id = tcx.map.local_def_id(item.id);
+        let _task = tcx.dep_graph.in_task(DepNode::CollectItem(item_def_id));
+        convert_item(self.ccx, item);
     }
 }
 
@@ -703,8 +671,12 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
     debug!("convert: item {} with id {}", it.name, it.id);
     match it.node {
         // These don't define types.
-        hir::ItemExternCrate(_) | hir::ItemUse(_) |
-        hir::ItemForeignMod(_) | hir::ItemMod(_) => {
+        hir::ItemExternCrate(_) | hir::ItemUse(_) | hir::ItemMod(_) => {
+        }
+        hir::ItemForeignMod(ref foreign_mod) => {
+            for item in &foreign_mod.items {
+                convert_foreign_item(ccx, item);
+            }
         }
         hir::ItemEnum(ref enum_definition, _) => {
             let (scheme, predicates) = convert_typed_item(ccx, it);
@@ -1283,16 +1255,11 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         substs: substs,
     };
 
-    let trait_def = ty::TraitDef {
-        paren_sugar: paren_sugar,
-        unsafety: unsafety,
-        generics: ty_generics,
-        trait_ref: trait_ref,
-        associated_type_names: associated_type_names,
-        nonblanket_impls: RefCell::new(FnvHashMap()),
-        blanket_impls: RefCell::new(vec![]),
-        flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
-    };
+    let trait_def = ty::TraitDef::new(unsafety,
+                                      paren_sugar,
+                                      ty_generics,
+                                      trait_ref,
+                                      associated_type_names);
 
     return tcx.intern_trait_def(trait_def);
 
@@ -1452,12 +1419,17 @@ fn type_scheme_of_def_id<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
 }
 
 fn type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
-                                it: &hir::Item)
+                                item: &hir::Item)
                                 -> ty::TypeScheme<'tcx>
 {
-    memoized(&ccx.tcx.tcache,
-             ccx.tcx.map.local_def_id(it.id),
-             |_| compute_type_scheme_of_item(ccx, it))
+    let item_def_id = ccx.tcx.map.local_def_id(item.id);
+    ccx.tcx.tcache.memoize(item_def_id, || {
+        // NB. Since the `memoized` function enters a new task, and we
+        // are giving this task access to the item `item`, we must
+        // register a read.
+        ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id));
+        compute_type_scheme_of_item(ccx, item)
+    })
 }
 
 fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
@@ -1571,13 +1543,18 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
 fn type_scheme_of_foreign_item<'a, 'tcx>(
     ccx: &CrateCtxt<'a, 'tcx>,
-    it: &hir::ForeignItem,
+    item: &hir::ForeignItem,
     abi: abi::Abi)
     -> ty::TypeScheme<'tcx>
 {
-    memoized(&ccx.tcx.tcache,
-             ccx.tcx.map.local_def_id(it.id),
-             |_| compute_type_scheme_of_foreign_item(ccx, it, abi))
+    let item_def_id = ccx.tcx.map.local_def_id(item.id);
+    ccx.tcx.tcache.memoize(item_def_id, || {
+        // NB. Since the `memoized` function enters a new task, and we
+        // are giving this task access to the item `item`, we must
+        // register a read.
+        ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id));
+        compute_type_scheme_of_foreign_item(ccx, item, abi)
+    })
 }
 
 fn compute_type_scheme_of_foreign_item<'a, 'tcx>(
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index bf890f3e50709..580d200eb73a7 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -92,6 +92,7 @@ extern crate rustc_platform_intrinsics as intrinsics;
 extern crate rustc_front;
 extern crate rustc_back;
 
+pub use rustc::dep_graph;
 pub use rustc::front;
 pub use rustc::lint;
 pub use rustc::middle;
diff --git a/src/librustc_typeck/variance.rs b/src/librustc_typeck/variance.rs
index 8c7967c707807..ce0e9e14035f5 100644
--- a/src/librustc_typeck/variance.rs
+++ b/src/librustc_typeck/variance.rs
@@ -266,6 +266,7 @@ use self::ParamKind::*;
 
 use arena;
 use arena::TypedArena;
+use dep_graph::DepNode;
 use middle::def_id::DefId;
 use middle::resolve_lifetime as rl;
 use middle::subst;
@@ -280,6 +281,7 @@ use rustc_front::intravisit::Visitor;
 use util::nodemap::NodeMap;
 
 pub fn infer_variance(tcx: &ty::ctxt) {
+    let _task = tcx.dep_graph.in_task(DepNode::Variance);
     let krate = tcx.map.krate();
     let mut arena = arena::TypedArena::new();
     let terms_cx = determine_parameters_to_be_inferred(tcx, &mut arena, krate);
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index d7190a4bea974..d57d1bcd92da5 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -154,6 +154,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
                                         &name,
                                         resolve::MakeGlobMap::No,
                                         |tcx, _, analysis| {
+        let _ignore = tcx.dep_graph.in_ignore();
         let ty::CrateAnalysis { access_levels, .. } = analysis;
 
         // Convert from a NodeId set to a DefId set since we don't always have easy access
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index a23dc3b8871fa..c281571305b8b 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -326,6 +326,14 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
                                        "the `#[rustc_error]` attribute \
                                         is just used for rustc unit tests \
                                         and will never be stable")),
+    ("rustc_if_this_changed", Whitelisted, Gated("rustc_attrs",
+                                       "the `#[rustc_if_this_changed]` attribute \
+                                        is just used for rustc unit tests \
+                                        and will never be stable")),
+    ("rustc_then_this_would_need", Whitelisted, Gated("rustc_attrs",
+                                       "the `#[rustc_if_this_changed]` attribute \
+                                        is just used for rustc unit tests \
+                                        and will never be stable")),
     ("rustc_move_fragments", Normal, Gated("rustc_attrs",
                                            "the `#[rustc_move_fragments]` attribute \
                                             is just used for rustc unit tests \
diff --git a/src/test/compile-fail/dep-graph-caller-callee.rs b/src/test/compile-fail/dep-graph-caller-callee.rs
new file mode 100644
index 0000000000000..acd6091cbdd48
--- /dev/null
+++ b/src/test/compile-fail/dep-graph-caller-callee.rs
@@ -0,0 +1,47 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that immediate callers have to change when callee changes, but
+// not callers' callers.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+fn main() { }
+
+mod x {
+    #[rustc_if_this_changed]
+    pub fn x() { }
+}
+
+mod y {
+    use x;
+
+    // These dependencies SHOULD exist:
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
+    pub fn y() {
+        x::x();
+    }
+}
+
+mod z {
+    use y;
+
+    // These are expected to yield errors, because changes to `x`
+    // affect the BODY of `y`, but not its signature.
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
+    pub fn z() {
+        y::y();
+    }
+}
diff --git a/src/test/compile-fail/dep-graph-struct-signature.rs b/src/test/compile-fail/dep-graph-struct-signature.rs
new file mode 100644
index 0000000000000..5cfb748b6f459
--- /dev/null
+++ b/src/test/compile-fail/dep-graph-struct-signature.rs
@@ -0,0 +1,100 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test cases where a changing struct appears in the signature of fns
+// and methods.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+
+fn main() { }
+
+#[rustc_if_this_changed]
+struct WillChange {
+    x: u32,
+    y: u32
+}
+
+struct WontChange {
+    x: u32,
+    y: u32
+}
+
+// these are valid dependencies
+mod signatures {
+    use WillChange;
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    trait Bar {
+        fn do_something(x: WillChange);
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    fn some_fn(x: WillChange) { }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    fn new_foo(x: u32, y: u32) -> WillChange {
+        WillChange { x: x, y: y }
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    impl WillChange {
+        fn new(x: u32, y: u32) -> WillChange { loop { } }
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    impl WillChange {
+        fn method(&self, x: u32) { }
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    struct WillChanges {
+        x: WillChange,
+        y: WillChange
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    fn indirect(x: WillChanges) { }
+}
+
+// these are invalid dependencies, though sometimes we create edges
+// anyway.
+mod invalid_signatures {
+    use WontChange;
+
+    // FIXME due to the variance pass having overly conservative edges,
+    // we incorrectly think changes are needed here
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    trait A {
+        fn do_something_else_twice(x: WontChange);
+    }
+
+    // FIXME due to the variance pass having overly conservative edges,
+    // we incorrectly think changes are needed here
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    fn b(x: WontChange) { }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path from `WillChange`
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR no path from `WillChange`
+    fn c(x: u32) { }
+}
+
diff --git a/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs b/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs
new file mode 100644
index 0000000000000..57e83586d8d37
--- /dev/null
+++ b/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs
@@ -0,0 +1,54 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that adding an impl to a trait `Foo` DOES affect functions
+// that only use `Bar` if they have methods in common.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+fn main() { }
+
+pub trait Foo: Sized {
+    fn method(self) { }
+}
+
+pub trait Bar: Sized {
+    fn method(self) { }
+}
+
+mod x {
+    use {Foo, Bar};
+
+    #[rustc_if_this_changed]
+    impl Foo for u32 { }
+
+    impl Bar for char { }
+}
+
+mod y {
+    use {Foo, Bar};
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+    pub fn with_char() {
+        char::method('a');
+    }
+}
+
+mod z {
+    use y;
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    pub fn z() {
+        y::with_char();
+    }
+}
diff --git a/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs b/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs
new file mode 100644
index 0000000000000..ba54a05620945
--- /dev/null
+++ b/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs
@@ -0,0 +1,54 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that adding an impl to a trait `Foo` does not affect functions
+// that only use `Bar`, so long as they do not have methods in common.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(warnings)]
+
+fn main() { }
+
+pub trait Foo: Sized {
+    fn foo(self) { }
+}
+
+pub trait Bar: Sized {
+    fn bar(self) { }
+}
+
+mod x {
+    use {Foo, Bar};
+
+    #[rustc_if_this_changed]
+    impl Foo for char { }
+
+    impl Bar for char { }
+}
+
+mod y {
+    use {Foo, Bar};
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    pub fn call_bar() {
+        char::bar('a');
+    }
+}
+
+mod z {
+    use y;
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    pub fn z() {
+        y::call_bar();
+    }
+}
diff --git a/src/test/compile-fail/dep-graph-trait-impl.rs b/src/test/compile-fail/dep-graph-trait-impl.rs
new file mode 100644
index 0000000000000..83e924fe06d7d
--- /dev/null
+++ b/src/test/compile-fail/dep-graph-trait-impl.rs
@@ -0,0 +1,77 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that when a trait impl changes, fns whose body uses that trait
+// must also be recompiled.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(warnings)]
+
+fn main() { }
+
+pub trait Foo: Sized {
+    fn method(self) { }
+}
+
+mod x {
+    use Foo;
+
+    #[rustc_if_this_changed]
+    impl Foo for char { }
+
+    impl Foo for u32 { }
+}
+
+mod y {
+    use Foo;
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
+    pub fn with_char() {
+        char::method('a');
+    }
+
+    // FIXME(#30741) tcx fulfillment cache not tracked
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
+    pub fn take_foo_with_char() {
+        take_foo::<char>('a');
+    }
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
+    pub fn with_u32() {
+        u32::method(22);
+    }
+
+    // FIXME(#30741) tcx fulfillment cache not tracked
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
+    pub fn take_foo_with_u32() {
+        take_foo::<u32>(22);
+    }
+
+    pub fn take_foo<T:Foo>(t: T) { }
+}
+
+mod z {
+    use y;
+
+    // These are expected to yield errors, because changes to `x`
+    // affect the BODY of `y`, but not its signature.
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
+    pub fn z() {
+        y::with_char();
+        y::with_u32();
+    }
+}
diff --git a/src/test/compile-fail/dep-graph-unrelated.rs b/src/test/compile-fail/dep-graph-unrelated.rs
new file mode 100644
index 0000000000000..8feec12a2f76c
--- /dev/null
+++ b/src/test/compile-fail/dep-graph-unrelated.rs
@@ -0,0 +1,22 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that two unrelated functions have no trans dependency.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+#[rustc_if_this_changed]
+fn main() { }
+
+#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path from `main`
+fn bar() { }