expand DepNode::TraitSelect to include type ids

To handle the general case, we include a vector of def-ids, so that we
can account for things like `(Foo, Bar)` which references both `Foo` and
`Bar`. This means it is not Copy, so re-jigger some APIs to use
borrowing more intelligently.
This commit is contained in:
Niko Matsakis 2016-05-26 06:11:16 -04:00
parent 5dc6a058b2
commit 63bb0847bd
10 changed files with 91 additions and 79 deletions

View File

@ -10,7 +10,16 @@
use std::fmt::Debug;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
macro_rules! try_opt {
($e:expr) => (
match $e {
Some(r) => r,
None => return None,
}
)
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub enum DepNode<D: Clone + Debug> {
// The `D` type is "how definitions are identified".
// During compilation, it is always `DefId`, but when serializing
@ -116,7 +125,7 @@ pub enum DepNode<D: Clone + Debug> {
// which would yield an overly conservative dep-graph.
TraitItems(D),
ReprHints(D),
TraitSelect(D),
TraitSelect(D, Vec<D>),
}
impl<D: Clone + Debug> DepNode<D> {
@ -212,7 +221,11 @@ impl<D: Clone + Debug> DepNode<D> {
TraitImpls(ref d) => op(d).map(TraitImpls),
TraitItems(ref d) => op(d).map(TraitItems),
ReprHints(ref d) => op(d).map(ReprHints),
TraitSelect(ref d) => op(d).map(TraitSelect),
TraitSelect(ref d, ref type_ds) => {
let d = try_opt!(op(d));
let type_ds = try_opt!(type_ds.iter().map(|d| op(d)).collect());
Some(TraitSelect(d, type_ds))
}
}
}
}

View File

@ -47,26 +47,26 @@ impl<D: Clone + Debug + Hash + Eq> DepGraphQuery<D> {
self.indices.contains_key(&node)
}
pub fn nodes(&self) -> Vec<DepNode<D>> {
pub fn nodes(&self) -> Vec<&DepNode<D>> {
self.graph.all_nodes()
.iter()
.map(|n| n.data.clone())
.map(|n| &n.data)
.collect()
}
pub fn edges(&self) -> Vec<(DepNode<D>,DepNode<D>)> {
pub fn edges(&self) -> Vec<(&DepNode<D>,&DepNode<D>)> {
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()))
.map(|(s, t)| (self.graph.node_data(s),
self.graph.node_data(t)))
.collect()
}
fn reachable_nodes(&self, node: DepNode<D>, direction: Direction) -> Vec<DepNode<D>> {
if let Some(&index) = self.indices.get(&node) {
fn reachable_nodes(&self, node: &DepNode<D>, direction: Direction) -> Vec<&DepNode<D>> {
if let Some(&index) = self.indices.get(node) {
self.graph.depth_traverse(index, direction)
.map(|s| self.graph.node_data(s).clone())
.map(|s| self.graph.node_data(s))
.collect()
} else {
vec![]
@ -75,20 +75,20 @@ impl<D: Clone + Debug + Hash + Eq> DepGraphQuery<D> {
/// All nodes reachable from `node`. In other words, things that
/// will have to be recomputed if `node` changes.
pub fn transitive_successors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
pub fn transitive_successors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
self.reachable_nodes(node, OUTGOING)
}
/// All nodes that can reach `node`.
pub fn transitive_predecessors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
pub fn transitive_predecessors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
self.reachable_nodes(node, INCOMING)
}
/// Just the outgoing edges from `node`.
pub fn immediate_successors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
pub fn immediate_successors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
if let Some(&index) = self.indices.get(&node) {
self.graph.successor_nodes(index)
.map(|s| self.graph.node_data(s).clone())
.map(|s| self.graph.node_data(s))
.collect()
} else {
vec![]

View File

@ -20,14 +20,14 @@ pub struct DepTask<'graph> {
impl<'graph> DepTask<'graph> {
pub fn new(data: &'graph DepGraphThreadData, key: DepNode<DefId>)
-> DepTask<'graph> {
data.enqueue(DepMessage::PushTask(key));
data.enqueue(DepMessage::PushTask(key.clone()));
DepTask { data: data, key: key }
}
}
impl<'graph> Drop for DepTask<'graph> {
fn drop(&mut self) {
self.data.enqueue(DepMessage::PopTask(self.key));
self.data.enqueue(DepMessage::PopTask(self.key.clone()));
}
}

View File

@ -39,7 +39,7 @@ pub fn visit_all_items_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, '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);
let _task = self.tcx.dep_graph.in_task(task_id);
let _task = self.tcx.dep_graph.in_task(task_id.clone());
debug!("Started task {:?}", task_id);
self.tcx.dep_graph.read(DepNode::Hir(item_def_id));
self.visitor.visit_item(i);

View File

@ -946,7 +946,7 @@ impl<'tcx> TraitPredicate<'tcx> {
/// Creates the dep-node for selecting/evaluating this trait reference.
fn dep_node(&self) -> DepNode<DefId> {
DepNode::TraitSelect(self.def_id())
DepNode::TraitSelect(self.def_id(), vec![])
}
pub fn input_types(&self) -> &[Ty<'tcx>] {
@ -1768,9 +1768,8 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
stack: &mut Vec<AdtDefMaster<'tcx>>)
{
let dep_node = DepNode::SizedConstraint(self.did);
if self.sized_constraint.get(dep_node).is_some() {
let dep_node = || DepNode::SizedConstraint(self.did);
if self.sized_constraint.get(dep_node()).is_some() {
return;
}
@ -1780,7 +1779,7 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
//
// Consider the type as Sized in the meanwhile to avoid
// further errors.
self.sized_constraint.fulfill(dep_node, tcx.types.err);
self.sized_constraint.fulfill(dep_node(), tcx.types.err);
return;
}
@ -1803,14 +1802,14 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
_ => tcx.mk_tup(tys)
};
match self.sized_constraint.get(dep_node) {
match self.sized_constraint.get(dep_node()) {
Some(old_ty) => {
debug!("calculate_sized_constraint: {:?} recurred", self);
assert_eq!(old_ty, tcx.types.err)
}
None => {
debug!("calculate_sized_constraint: {:?} => {:?}", self, ty);
self.sized_constraint.fulfill(dep_node, ty)
self.sized_constraint.fulfill(dep_node(), ty)
}
}
}

View File

@ -195,7 +195,7 @@ fn check_paths<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
};
for &(_, source_def_id, source_dep_node) in sources {
for &(_, source_def_id, ref source_dep_node) in sources {
let dependents = query.transitive_successors(source_dep_node);
for &(target_span, ref target_pass, _, ref target_dep_node) in targets {
if !dependents.contains(&target_dep_node) {
@ -239,7 +239,7 @@ fn dump_graph(tcx: TyCtxt) {
{ // 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 {
for &(ref source, ref target) in &edges {
write!(file, "{:?} -> {:?}\n", source, target).unwrap();
}
}
@ -252,34 +252,34 @@ fn dump_graph(tcx: TyCtxt) {
}
}
pub struct GraphvizDepGraph(FnvHashSet<DepNode<DefId>>,
Vec<(DepNode<DefId>, DepNode<DefId>)>);
pub struct GraphvizDepGraph<'q>(FnvHashSet<&'q DepNode<DefId>>,
Vec<(&'q DepNode<DefId>, &'q DepNode<DefId>)>);
impl<'a, 'tcx> dot::GraphWalk<'a> for GraphvizDepGraph {
type Node = DepNode<DefId>;
type Edge = (DepNode<DefId>, DepNode<DefId>);
fn nodes(&self) -> dot::Nodes<DepNode<DefId>> {
impl<'a, 'tcx, 'q> dot::GraphWalk<'a> for GraphvizDepGraph<'q> {
type Node = &'q DepNode<DefId>;
type Edge = (&'q DepNode<DefId>, &'q DepNode<DefId>);
fn nodes(&self) -> dot::Nodes<&'q DepNode<DefId>> {
let nodes: Vec<_> = self.0.iter().cloned().collect();
nodes.into_cow()
}
fn edges(&self) -> dot::Edges<(DepNode<DefId>, DepNode<DefId>)> {
fn edges(&self) -> dot::Edges<(&'q DepNode<DefId>, &'q DepNode<DefId>)> {
self.1[..].into_cow()
}
fn source(&self, edge: &(DepNode<DefId>, DepNode<DefId>)) -> DepNode<DefId> {
fn source(&self, edge: &(&'q DepNode<DefId>, &'q DepNode<DefId>)) -> &'q DepNode<DefId> {
edge.0
}
fn target(&self, edge: &(DepNode<DefId>, DepNode<DefId>)) -> DepNode<DefId> {
fn target(&self, edge: &(&'q DepNode<DefId>, &'q DepNode<DefId>)) -> &'q DepNode<DefId> {
edge.1
}
}
impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
type Node = DepNode<DefId>;
type Edge = (DepNode<DefId>, DepNode<DefId>);
impl<'a, 'tcx, 'q> dot::Labeller<'a> for GraphvizDepGraph<'q> {
type Node = &'q DepNode<DefId>;
type Edge = (&'q DepNode<DefId>, &'q DepNode<DefId>);
fn graph_id(&self) -> dot::Id {
dot::Id::new("DependencyGraph").unwrap()
}
fn node_id(&self, n: &DepNode<DefId>) -> dot::Id {
fn node_id(&self, n: &&'q DepNode<DefId>) -> dot::Id {
let s: String =
format!("{:?}", n).chars()
.map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
@ -287,7 +287,7 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
debug!("n={:?} s={:?}", n, s);
dot::Id::new(s).unwrap()
}
fn node_label(&self, n: &DepNode<DefId>) -> dot::LabelText {
fn node_label(&self, n: &&'q DepNode<DefId>) -> dot::LabelText {
dot::LabelText::label(format!("{:?}", n))
}
}
@ -295,8 +295,8 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
// 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<DefId>, filter: &DepNodeFilter)
-> Option<FnvHashSet<DepNode<DefId>>>
fn node_set<'q>(query: &'q DepGraphQuery<DefId>, filter: &DepNodeFilter)
-> Option<FnvHashSet<&'q DepNode<DefId>>>
{
debug!("node_set(filter={:?})", filter);
@ -307,10 +307,10 @@ fn node_set(query: &DepGraphQuery<DefId>, filter: &DepNodeFilter)
Some(query.nodes().into_iter().filter(|n| filter.test(n)).collect())
}
fn filter_nodes(query: &DepGraphQuery<DefId>,
sources: &Option<FnvHashSet<DepNode<DefId>>>,
targets: &Option<FnvHashSet<DepNode<DefId>>>)
-> FnvHashSet<DepNode<DefId>>
fn filter_nodes<'q>(query: &'q DepGraphQuery<DefId>,
sources: &Option<FnvHashSet<&'q DepNode<DefId>>>,
targets: &Option<FnvHashSet<&'q DepNode<DefId>>>)
-> FnvHashSet<&'q DepNode<DefId>>
{
if let &Some(ref sources) = sources {
if let &Some(ref targets) = targets {
@ -325,21 +325,21 @@ fn filter_nodes(query: &DepGraphQuery<DefId>,
}
}
fn walk_nodes(query: &DepGraphQuery<DefId>,
starts: &FnvHashSet<DepNode<DefId>>,
direction: Direction)
-> FnvHashSet<DepNode<DefId>>
fn walk_nodes<'q>(query: &'q DepGraphQuery<DefId>,
starts: &FnvHashSet<&'q DepNode<DefId>>,
direction: Direction)
-> FnvHashSet<&'q DepNode<DefId>>
{
let mut set = FnvHashSet();
for start in starts {
for &start in starts {
debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
if set.insert(*start) {
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) {
if set.insert(neighbor) {
stack.push(neighbor_index);
}
}
@ -349,10 +349,10 @@ fn walk_nodes(query: &DepGraphQuery<DefId>,
set
}
fn walk_between(query: &DepGraphQuery<DefId>,
sources: &FnvHashSet<DepNode<DefId>>,
targets: &FnvHashSet<DepNode<DefId>>)
-> FnvHashSet<DepNode<DefId>>
fn walk_between<'q>(query: &'q DepGraphQuery<DefId>,
sources: &FnvHashSet<&'q DepNode<DefId>>,
targets: &FnvHashSet<&'q DepNode<DefId>>)
-> FnvHashSet<&'q DepNode<DefId>>
{
// 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
@ -365,16 +365,16 @@ fn walk_between(query: &DepGraphQuery<DefId>,
let mut node_states = vec![State::Undecided; query.graph.len_nodes()];
for &target in targets {
node_states[query.indices[&target].0] = State::Included;
node_states[query.indices[target].0] = State::Included;
}
for source in sources.iter().map(|n| query.indices[n]) {
for source in sources.iter().map(|&n| query.indices[n]) {
recurse(query, &mut node_states, source);
}
return query.nodes()
.into_iter()
.filter(|n| {
.filter(|&n| {
let index = query.indices[n];
node_states[index.0] == State::Included
})
@ -417,12 +417,12 @@ fn walk_between(query: &DepGraphQuery<DefId>,
}
}
fn filter_edges(query: &DepGraphQuery<DefId>,
nodes: &FnvHashSet<DepNode<DefId>>)
-> Vec<(DepNode<DefId>, DepNode<DefId>)>
fn filter_edges<'q>(query: &'q DepGraphQuery<DefId>,
nodes: &FnvHashSet<&'q DepNode<DefId>>)
-> Vec<(&'q DepNode<DefId>, &'q DepNode<DefId>)>
{
query.edges()
.into_iter()
.filter(|&(source, target)| nodes.contains(&source) && nodes.contains(&target))
.filter(|&(source, target)| nodes.contains(source) && nodes.contains(target))
.collect()
}

View File

@ -57,7 +57,7 @@ impl RetracedDefIdDirectory {
self.ids[index.index as usize]
}
pub fn map(&self, node: DepNode<DefPathIndex>) -> Option<DepNode<DefId>> {
pub fn map(&self, node: &DepNode<DefPathIndex>) -> Option<DepNode<DefId>> {
node.map_def(|&index| self.def_id(index))
}
}
@ -91,7 +91,7 @@ impl<'a,'tcx> DefIdDirectoryBuilder<'a,'tcx> {
.clone()
}
pub fn map(&mut self, node: DepNode<DefId>) -> DepNode<DefPathIndex> {
pub fn map(&mut self, node: &DepNode<DefId>) -> DepNode<DefPathIndex> {
node.map_def(|&def_id| Some(self.add(def_id))).unwrap()
}

View File

@ -39,8 +39,8 @@ impl<'a, 'tcx> HashContext<'a, 'tcx> {
}
}
pub fn hash(&mut self, dep_node: DepNode<DefId>) -> Option<u64> {
match dep_node {
pub fn hash(&mut self, dep_node: &DepNode<DefId>) -> Option<u64> {
match *dep_node {
// HIR nodes (which always come from our crate) are an input:
DepNode::Hir(def_id) => {
assert!(def_id.is_local());

View File

@ -114,15 +114,15 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let clean_nodes =
serialized_dep_graph.nodes
.iter()
.filter_map(|&node| retraced.map(node))
.filter_map(|node| retraced.map(node))
.filter(|node| !dirty_nodes.contains(node))
.map(|node| (node, node));
.map(|node| (node.clone(), node));
// Add nodes and edges that are not dirty into our main graph.
let dep_graph = tcx.dep_graph.clone();
for (source, target) in clean_edges.into_iter().chain(clean_nodes) {
let _task = dep_graph.in_task(target);
dep_graph.read(source);
let _task = dep_graph.in_task(target.clone());
dep_graph.read(source.clone());
debug!("decode_dep_graph: clean edge: {:?} -> {:?}", source, target);
}
@ -140,7 +140,7 @@ fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
for hash in hashes {
match hash.node.map_def(|&i| retraced.def_id(i)) {
Some(dep_node) => {
let current_hash = hcx.hash(dep_node).unwrap();
let current_hash = hcx.hash(&dep_node).unwrap();
debug!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}",
dep_node, current_hash, hash.hash);
if current_hash != hash.hash {
@ -171,7 +171,7 @@ fn compute_clean_edges(serialized_edges: &[(SerializedEdge)],
// target) if neither node has been removed. If the source has
// been removed, add target to the list of dirty nodes.
let mut clean_edges = Vec::with_capacity(serialized_edges.len());
for &(serialized_source, serialized_target) in serialized_edges {
for &(ref serialized_source, ref serialized_target) in serialized_edges {
if let Some(target) = retraced.map(serialized_target) {
if let Some(source) = retraced.map(serialized_source) {
clean_edges.push((source, target))

View File

@ -99,7 +99,7 @@ pub fn encode_dep_graph<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
query.nodes()
.into_iter()
.filter_map(|dep_node| {
hcx.hash(dep_node)
hcx.hash(&dep_node)
.map(|hash| {
let node = builder.map(dep_node);
SerializedHash { node: node, hash: hash }
@ -147,7 +147,7 @@ pub fn encode_metadata_hashes<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
let meta_data_def_ids =
query.nodes()
.into_iter()
.filter_map(|dep_node| match dep_node {
.filter_map(|dep_node| match *dep_node {
DepNode::MetaData(def_id) if def_id.is_local() => Some(def_id),
_ => None,
});
@ -165,8 +165,8 @@ pub fn encode_metadata_hashes<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
let dep_node = DepNode::MetaData(def_id);
let mut state = SipHasher::new();
debug!("save: computing metadata hash for {:?}", dep_node);
for node in query.transitive_predecessors(dep_node) {
if let Some(hash) = hcx.hash(node) {
for node in query.transitive_predecessors(&dep_node) {
if let Some(hash) = hcx.hash(&node) {
debug!("save: predecessor {:?} has hash {}", node, hash);
state.write_u64(hash.to_le());
} else {