Auto merge of #30532 - nikomatsakis:cross-item-dependencies, r=mw

This is roughly the same as my previous PR that created a dependency graph, but that:

1. The dependency graph is only optionally constructed, though this doesn't seem to make much of a difference in terms of overhead (see measurements below).
2. The dependency graph is simpler (I combined a lot of nodes).
3. The dependency graph debugging facilities are much better: you can now use `RUST_DEP_GRAPH_FILTER` to filter the dep graph to just the nodes you are interested in, which is super help.
4. The tests are somewhat more elaborate, including a few known bugs I need to fix in a second pass.

This is potentially a `[breaking-change]` for plugin authors. If you are poking about in tcx state or something like that, you probably want to add `let _ignore = tcx.dep_graph.in_ignore();`, which will cause your reads/writes to be ignored and not affect the dep-graph.

After this, or perhaps as an add-on to this PR in some cases, what I would like to do is the following:

- [x] Write-up a little guide to how to use this system, the debugging options available, and what the possible failure modes are.
- [ ] Introduce read-only and perhaps the `Meta` node
- [x] Replace "memoization tasks" with node from the map itself
- [ ] Fix the shortcomings, obviously! Notably, the HIR map needs to register reads, and there is some state that is not yet tracked. (Maybe as a separate PR.)
- [x] Refactor the dep-graph code so that the actual maintenance of the dep-graph occurs in a parallel thread, and the main thread simply throws things into a shared channel (probably a fixed-size channel). There is no reason for dep-graph construction to be on the main thread. (Maybe as a separate PR.)

Regarding performance: adding this tracking does add some overhead, approximately 2% in my measurements (I was comparing the build times for rustdoc). Interestingly, enabling or disabling tracking doesn't seem to do very much. I want to poke at this some more and gather a bit more data -- in some tests I've seen that 2% go away, but on others it comes back. It's not entirely clear to me if that 2% is truly due to constructing the dep-graph at all.

The next big step after this is write some code to dump the dep-graph to disk and reload it.

r? @michaelwoerister
This commit is contained in:
bors 2016-01-06 18:37:57 +00:00
commit e8c337b5ca
60 changed files with 2640 additions and 423 deletions

View File

@ -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.

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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![]
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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();
}

View File

@ -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(),
});

View File

@ -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> {

View File

@ -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 };

View File

@ -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
{

View File

@ -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> {

View File

@ -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);

View File

@ -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> {

View File

@ -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())
}

View File

@ -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);

View File

@ -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>,

View File

@ -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)
})
})
}
}

View File

@ -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,

View File

@ -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>)")
}

View File

@ -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>> }

View File

@ -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 {
@ -2452,19 +2285,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,
@ -2491,6 +2311,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
}
@ -2513,6 +2337,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
}
@ -2538,6 +2366,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;
@ -2760,6 +2592,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.

View File

@ -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,
}
}

View File

@ -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,

View File

@ -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
}
}
}
}

View File

@ -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 ---");

View File

@ -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
}
}
}

View File

@ -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)]

View File

@ -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
}
}

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -24,6 +24,7 @@ use build;
use graphviz;
use pretty;
use transform::*;
use rustc::dep_graph::DepNode;
use rustc::mir::repr::Mir;
use hair::cx::Cx;
use std::fs::File;
@ -48,7 +49,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
}

View File

@ -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

View File

@ -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),

View File

@ -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)]

View File

@ -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;
}

View File

@ -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()
}

View File

@ -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};
@ -2984,9 +2986,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 {
@ -3140,6 +3149,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,
@ -3192,7 +3203,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);
}
}

View File

@ -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)) => {

View File

@ -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

View File

@ -20,6 +20,7 @@ mod macros;
mod adt;
mod asm;
mod assert_dep_graph;
mod attributes;
mod base;
mod basic_block;

View File

@ -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(()) => {}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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");
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);
// 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);
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

View File

@ -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>(

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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 \

View File

@ -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();
}
}

View File

@ -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) { }
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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() { }