Skip to content

Commit

Permalink
Rollup merge of rust-lang#39582 - nikomatsakis:incr-comp-issue-39569,…
Browse files Browse the repository at this point in the history
… r=michaelwoerister

Handle the case where an intermediate node can't be recreated

This solution grows the graph, but this is quite the corner case.

r? @michaelwoerister
  • Loading branch information
frewsxcv authored Feb 8, 2017
2 parents e0eeb6e + 4f5fc4e commit 944933e
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 35 deletions.
119 changes: 84 additions & 35 deletions src/librustc_incremental/persist/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,46 +176,32 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Recreate the edges in the graph that are still clean.
let mut clean_work_products = FxHashSet();
let mut dirty_work_products = FxHashSet(); // incomplete; just used to suppress debug output
let mut extra_edges = vec![];
for (source, targets) in &edge_map {
for target in targets {
// If the target is dirty, skip the edge. If this is an edge
// that targets a work-product, we can print the blame
// information now.
if let Some(blame) = dirty_raw_nodes.get(target) {
if let DepNode::WorkProduct(ref wp) = *target {
if tcx.sess.opts.debugging_opts.incremental_info {
if dirty_work_products.insert(wp.clone()) {
// It'd be nice to pretty-print these paths better than just
// using the `Debug` impls, but wev.
println!("incremental: module {:?} is dirty because {:?} \
changed or was removed",
wp,
blame.map_def(|&index| {
Some(directory.def_path_string(tcx, index))
}).unwrap());
}
}
}
continue;
}

// If the source is dirty, the target will be dirty.
assert!(!dirty_raw_nodes.contains_key(source));

// Retrace the source -> target edges to def-ids and then
// create an edge in the graph. Retracing may yield none if
// some of the data happens to have been removed; this ought
// to be impossible unless it is dirty, so we can unwrap.
let source_node = retraced.map(source).unwrap();
let target_node = retraced.map(target).unwrap();
let _task = tcx.dep_graph.in_task(target_node);
tcx.dep_graph.read(source_node);
if let DepNode::WorkProduct(ref wp) = *target {
clean_work_products.insert(wp.clone());
}
process_edges(tcx, source, target, &edge_map, &directory, &retraced, &dirty_raw_nodes,
&mut clean_work_products, &mut dirty_work_products, &mut extra_edges);
}
}

// Subtle. Sometimes we have intermediate nodes that we can't recreate in the new graph.
// This is pretty unusual but it arises in a scenario like this:
//
// Hir(X) -> Foo(Y) -> Bar
//
// Note that the `Hir(Y)` is not an input to `Foo(Y)` -- this
// almost never happens, but can happen in some obscure
// scenarios. In that case, if `Y` is removed, then we can't
// recreate `Foo(Y)` (the def-id `Y` no longer exists); what we do
// then is to push the edge `Hir(X) -> Bar` onto `extra_edges`
// (along with any other targets of `Foo(Y)`). We will then add
// the edge from `Hir(X)` to `Bar` (or, if `Bar` itself cannot be
// recreated, to the targets of `Bar`).
while let Some((source, target)) = extra_edges.pop() {
process_edges(tcx, source, target, &edge_map, &directory, &retraced, &dirty_raw_nodes,
&mut clean_work_products, &mut dirty_work_products, &mut extra_edges);
}

// Add in work-products that are still clean, and delete those that are
// dirty.
reconcile_work_products(tcx, work_products, &clean_work_products);
Expand Down Expand Up @@ -393,3 +379,66 @@ fn load_prev_metadata_hashes(tcx: TyCtxt,
serialized_hashes.index_map.len());
}

fn process_edges<'a, 'tcx, 'edges>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: &'edges DepNode<DefPathIndex>,
target: &'edges DepNode<DefPathIndex>,
edges: &'edges FxHashMap<DepNode<DefPathIndex>, Vec<DepNode<DefPathIndex>>>,
directory: &DefIdDirectory,
retraced: &RetracedDefIdDirectory,
dirty_raw_nodes: &DirtyNodes,
clean_work_products: &mut FxHashSet<Arc<WorkProductId>>,
dirty_work_products: &mut FxHashSet<Arc<WorkProductId>>,
extra_edges: &mut Vec<(&'edges DepNode<DefPathIndex>, &'edges DepNode<DefPathIndex>)>)
{
// If the target is dirty, skip the edge. If this is an edge
// that targets a work-product, we can print the blame
// information now.
if let Some(blame) = dirty_raw_nodes.get(target) {
if let DepNode::WorkProduct(ref wp) = *target {
if tcx.sess.opts.debugging_opts.incremental_info {
if dirty_work_products.insert(wp.clone()) {
// It'd be nice to pretty-print these paths better than just
// using the `Debug` impls, but wev.
println!("incremental: module {:?} is dirty because {:?} \
changed or was removed",
wp,
blame.map_def(|&index| {
Some(directory.def_path_string(tcx, index))
}).unwrap());
}
}
}
return;
}

// If the source is dirty, the target will be dirty.
assert!(!dirty_raw_nodes.contains_key(source));

// Retrace the source -> target edges to def-ids and then create
// an edge in the graph. Retracing may yield none if some of the
// data happens to have been removed.
if let Some(source_node) = retraced.map(source) {
if let Some(target_node) = retraced.map(target) {
let _task = tcx.dep_graph.in_task(target_node);
tcx.dep_graph.read(source_node);
if let DepNode::WorkProduct(ref wp) = *target {
clean_work_products.insert(wp.clone());
}
} else {
// As discussed in `decode_dep_graph` above, sometimes the
// target cannot be recreated again, in which case we add
// edges to go from `source` to the targets of `target`.
extra_edges.extend(
edges[target].iter().map(|t| (source, t)));
}
} else {
// It's also possible that the source can't be created! But we
// can ignore such cases, because (a) if `source` is a HIR
// node, it would be considered dirty; and (b) in other cases,
// there must be some input to this node that is clean, and so
// we'll re-create the edges over in the case where target is
// undefined.
}
}

38 changes: 38 additions & 0 deletions src/test/incremental/issue-39569.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 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.

// Regression test for a weird corner case in our dep-graph reduction
// code. When we solve `CoerceUnsized<Foo>`, we find no impls, so we
// don't end up with an edge to any HIR nodes, but it still gets
// preserved in the dep graph.

// revisions:rpass1 rpass2
// compile-flags: -Z query-dep-graph

use std::sync::Arc;

#[cfg(rpass1)]
struct Foo { x: usize }

#[cfg(rpass1)]
fn main() {
let x: Arc<Foo> = Arc::new(Foo { x: 22 });
let y: Arc<Foo> = x;
}

#[cfg(rpass2)]
struct FooX { x: usize }

#[cfg(rpass2)]
fn main() {
let x: Arc<FooX> = Arc::new(FooX { x: 22 });
let y: Arc<FooX> = x;
}

0 comments on commit 944933e

Please sign in to comment.