From b576abd2cc9f854653607dc1a05b51a215b08fa5 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 22 Feb 2017 12:02:41 -0500
Subject: [PATCH] detect "bootstrap outputs" when serializing the dep-graph

Fixes #39828.
---
 src/librustc_incremental/persist/data.rs      |  5 +++++
 src/librustc_incremental/persist/load.rs      | 12 ++++++++++
 src/librustc_incremental/persist/preds/mod.rs | 21 ++++++++++++++++--
 src/librustc_incremental/persist/save.rs      |  7 +++++-
 .../issue-39828/auxiliary/generic.rs          | 18 +++++++++++++++
 .../incremental/issue-39828/issue-39828.rs    | 22 +++++++++++++++++++
 6 files changed, 82 insertions(+), 3 deletions(-)
 create mode 100644 src/test/incremental/issue-39828/auxiliary/generic.rs
 create mode 100644 src/test/incremental/issue-39828/issue-39828.rs

diff --git a/src/librustc_incremental/persist/data.rs b/src/librustc_incremental/persist/data.rs
index 60f24b71de245..673f1ae10843c 100644
--- a/src/librustc_incremental/persist/data.rs
+++ b/src/librustc_incremental/persist/data.rs
@@ -23,6 +23,11 @@ use super::directory::DefPathIndex;
 pub struct SerializedDepGraph {
     pub edges: Vec<SerializedEdgeSet>,
 
+    /// These are output nodes that have no incoming edges. We track
+    /// these separately so that when we reload all edges, we don't
+    /// lose track of these nodes.
+    pub bootstrap_outputs: Vec<DepNode<DefPathIndex>>,
+
     /// These are hashes of two things:
     /// - the HIR nodes in this crate
     /// - the metadata nodes from dependent crates we use
diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs
index 7724658a9d6fe..03411e01a5798 100644
--- a/src/librustc_incremental/persist/load.rs
+++ b/src/librustc_incremental/persist/load.rs
@@ -184,6 +184,18 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
     }
 
+    // Recreate bootstrap outputs, which are outputs that have no incoming edges (and hence cannot
+    // be dirty).
+    for bootstrap_output in &serialized_dep_graph.bootstrap_outputs {
+        if let Some(n) = retraced.map(bootstrap_output) {
+            if let DepNode::WorkProduct(ref wp) = n {
+                clean_work_products.insert(wp.clone());
+            }
+
+            tcx.dep_graph.with_task(n, || ()); // create the node with no inputs
+        }
+    }
+
     // 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:
     //
diff --git a/src/librustc_incremental/persist/preds/mod.rs b/src/librustc_incremental/persist/preds/mod.rs
index a80620fbde66f..f6a37c7a12265 100644
--- a/src/librustc_incremental/persist/preds/mod.rs
+++ b/src/librustc_incremental/persist/preds/mod.rs
@@ -11,7 +11,7 @@
 use rustc::dep_graph::{DepGraphQuery, DepNode};
 use rustc::hir::def_id::DefId;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::graph::Graph;
+use rustc_data_structures::graph::{Graph, NodeIndex};
 
 use super::hash::*;
 use ich::Fingerprint;
@@ -28,6 +28,14 @@ pub struct Predecessors<'query> {
     // of the graph down.
     pub reduced_graph: Graph<&'query DepNode<DefId>, ()>,
 
+    // These are output nodes that have no incoming edges. We have to
+    // track these specially because, when we load the data back up
+    // again, we want to make sure and recreate these nodes (we want
+    // to recreate the nodes where all incoming edges are clean; but
+    // since we ordinarily just serialize edges, we wind up just
+    // forgetting that bootstrap outputs even exist in that case.)
+    pub bootstrap_outputs: Vec<&'query DepNode<DefId>>,
+
     // For the inputs (hir/foreign-metadata), we include hashes.
     pub hashes: FxHashMap<&'query DepNode<DefId>, Fingerprint>,
 }
@@ -57,7 +65,7 @@ impl<'q> Predecessors<'q> {
 
         // Reduce the graph to the most important nodes.
         let compress::Reduction { graph, input_nodes } =
-            compress::reduce_graph(&query.graph, HashContext::is_hashable, is_output);
+            compress::reduce_graph(&query.graph, HashContext::is_hashable, |n| is_output(n));
 
         let mut hashes = FxHashMap();
         for input_index in input_nodes {
@@ -67,8 +75,17 @@ impl<'q> Predecessors<'q> {
                   .or_insert_with(|| hcx.hash(input).unwrap());
         }
 
+        let bootstrap_outputs: Vec<&'q DepNode<DefId>> =
+            (0 .. graph.len_nodes())
+            .map(NodeIndex)
+            .filter(|&n| graph.incoming_edges(n).next().is_none())
+            .map(|n| *graph.node_data(n))
+            .filter(|n| is_output(n))
+            .collect();
+
         Predecessors {
             reduced_graph: graph,
+            bootstrap_outputs: bootstrap_outputs,
             hashes: hashes,
         }
     }
diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs
index 34bb125ef3f45..dfa6bf6bbb5e7 100644
--- a/src/librustc_incremental/persist/save.rs
+++ b/src/librustc_incremental/persist/save.rs
@@ -204,11 +204,15 @@ pub fn encode_dep_graph(preds: &Predecessors,
     }
 
     // Create the serialized dep-graph.
+    let bootstrap_outputs = preds.bootstrap_outputs.iter()
+                                                   .map(|n| builder.map(n))
+                                                   .collect();
     let edges = edges.into_iter()
                      .map(|(k, v)| SerializedEdgeSet { source: k, targets: v })
                      .collect();
     let graph = SerializedDepGraph {
-        edges: edges,
+        bootstrap_outputs,
+        edges,
         hashes: preds.hashes
             .iter()
             .map(|(&dep_node, &hash)| {
@@ -221,6 +225,7 @@ pub fn encode_dep_graph(preds: &Predecessors,
     };
 
     if tcx.sess.opts.debugging_opts.incremental_info {
+        println!("incremental: {} nodes in reduced dep-graph", preds.reduced_graph.len_nodes());
         println!("incremental: {} edges in serialized dep-graph", graph.edges.len());
         println!("incremental: {} hashes in serialized dep-graph", graph.hashes.len());
     }
diff --git a/src/test/incremental/issue-39828/auxiliary/generic.rs b/src/test/incremental/issue-39828/auxiliary/generic.rs
new file mode 100644
index 0000000000000..a562eab1768f3
--- /dev/null
+++ b/src/test/incremental/issue-39828/auxiliary/generic.rs
@@ -0,0 +1,18 @@
+// 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.
+
+// revisions:rpass1 rpass2
+// compile-flags: -Z query-dep-graph
+
+#![rustc_partition_reused(module="__rustc_fallback_codegen_unit", cfg="rpass2")]
+#![feature(rustc_attrs)]
+
+#![crate_type="rlib"]
+pub fn foo<T>() { }
diff --git a/src/test/incremental/issue-39828/issue-39828.rs b/src/test/incremental/issue-39828/issue-39828.rs
new file mode 100644
index 0000000000000..c729380bd5a31
--- /dev/null
+++ b/src/test/incremental/issue-39828/issue-39828.rs
@@ -0,0 +1,22 @@
+// 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 #39828. If you make use of a module that
+// consists only of generics, no code is generated, just a dummy
+// module. The reduced graph consists of a single node (for that
+// module) with no inputs. Since we only serialize edges, when we
+// reload, we would consider that node dirty since it is not recreated
+// (it is not the target of any edges).
+
+// revisions:rpass1 rpass2
+// aux-build:generic.rs
+
+extern crate generic;
+fn main() { }