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:
commit
e8c337b5ca
390
src/librustc/dep_graph/README.md
Normal file
390
src/librustc/dep_graph/README.md
Normal 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.
|
||||
|
137
src/librustc/dep_graph/dep_tracking_map.rs
Normal file
137
src/librustc/dep_graph/dep_tracking_map.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
162
src/librustc/dep_graph/edges.rs
Normal file
162
src/librustc/dep_graph/edges.rs
Normal 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)
|
||||
}
|
||||
}
|
196
src/librustc/dep_graph/mod.rs
Normal file
196
src/librustc/dep_graph/mod.rs
Normal 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)
|
||||
}
|
68
src/librustc/dep_graph/query.rs
Normal file
68
src/librustc/dep_graph/query.rs
Normal 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![]
|
||||
}
|
||||
}
|
||||
}
|
47
src/librustc/dep_graph/raii.rs
Normal file
47
src/librustc/dep_graph/raii.rs
Normal 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);
|
||||
}
|
||||
}
|
137
src/librustc/dep_graph/thread.rs
Normal file
137
src/librustc/dep_graph/thread.rs
Normal 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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
});
|
||||
|
@ -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> {
|
||||
|
@ -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 };
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>,
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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>)")
|
||||
}
|
||||
|
44
src/librustc/middle/ty/maps.rs
Normal file
44
src/librustc/middle/ty/maps.rs
Normal 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>> }
|
@ -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.
|
||||
|
226
src/librustc/middle/ty/trait_def.rs
Normal file
226
src/librustc/middle/ty/trait_def.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 ---");
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
47
src/librustc_data_structures/veccell/mod.rs
Normal file
47
src/librustc_data_structures/veccell/mod.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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)]
|
||||
|
@ -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;
|
||||
}
|
||||
|
430
src/librustc_trans/trans/assert_dep_graph.rs
Normal file
430
src/librustc_trans/trans/assert_dep_graph.rs
Normal 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()
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)) => {
|
||||
|
@ -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
|
||||
|
@ -20,6 +20,7 @@ mod macros;
|
||||
|
||||
mod adt;
|
||||
mod asm;
|
||||
mod assert_dep_graph;
|
||||
mod attributes;
|
||||
mod base;
|
||||
mod basic_block;
|
||||
|
@ -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(()) => {}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>(
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
47
src/test/compile-fail/dep-graph-caller-callee.rs
Normal file
47
src/test/compile-fail/dep-graph-caller-callee.rs
Normal 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();
|
||||
}
|
||||
}
|
100
src/test/compile-fail/dep-graph-struct-signature.rs
Normal file
100
src/test/compile-fail/dep-graph-struct-signature.rs
Normal 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) { }
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
54
src/test/compile-fail/dep-graph-trait-impl-two-traits.rs
Normal file
54
src/test/compile-fail/dep-graph-trait-impl-two-traits.rs
Normal 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();
|
||||
}
|
||||
}
|
77
src/test/compile-fail/dep-graph-trait-impl.rs
Normal file
77
src/test/compile-fail/dep-graph-trait-impl.rs
Normal 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();
|
||||
}
|
||||
}
|
22
src/test/compile-fail/dep-graph-unrelated.rs
Normal file
22
src/test/compile-fail/dep-graph-unrelated.rs
Normal 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() { }
|
Loading…
x
Reference in New Issue
Block a user