Skip to content

Commit

Permalink
Properly re-use def path hash in incremental mode
Browse files Browse the repository at this point in the history
Fixes rust-lang#79661

In incremental compilation mode, we update a `DefPathHash -> DefId`
mapping every time we create a `DepNode` for a foreign `DefId`.
This mapping is written out to the on-disk incremental cache, and is
read by the next compilation session to allow us to lazily decode
`DefId`s.

When we decode a `DepNode` from the current incremental cache, we need
to ensure that any previously-recorded `DefPathHash -> DefId` mapping
gets recorded in the new mapping that we write out. However, PR rust-lang#74967
didn't do this in all cases, leading to us being unable to decode a
`DefPathHash` in certain circumstances.

This PR refactors some of the code around `DepNode` deserialization to
prevent this kind of mistake from happening again.
  • Loading branch information
Aaron1011 committed Dec 5, 2020
1 parent 3ff10e7 commit c294640
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 20 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &De
.map(|c| c.is_green())
.unwrap_or(false));

let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap();
let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash));
if queries::$name::cache_on_disk(tcx, &key, None) {
let _ = tcx.$name(key);
}
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_query_system/src/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ use std::hash::Hash;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
pub struct DepNode<K> {
pub kind: K,
// Important - whenever a `DepNode` is constructed, we need to make
// sure to register a `DefPathHash -> DefId` mapping if needed.
// This is currently done in two places:
//
// * When a `DepNode::construct` is called, `arg.to_fingerprint()`
// is responsible for calling `OnDiskCache::store_foreign_def_id_hash`
// if needed
// * When a `DepNode` is loaded from the `PreviousDepGraph`,
// then `PreviousDepGraph::index_to_node` is responsible for calling
// `tcx.register_reused_dep_path_hash`
//
// FIXME: Enforce this by preventing manual construction of `DefNode`
// (e.g. add a `_priv: ()` field)
pub hash: PackedFingerprint,
}

Expand Down
23 changes: 5 additions & 18 deletions compiler/rustc_query_system/src/dep_graph/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
use rustc_data_structures::unlikely;
use rustc_errors::Diagnostic;
use rustc_index::vec::{Idx, IndexVec};
use rustc_span::def_id::DefPathHash;

use parking_lot::{Condvar, Mutex};
use smallvec::{smallvec, SmallVec};
Expand Down Expand Up @@ -555,7 +554,7 @@ impl<K: DepKind> DepGraph<K> {
// We never try to mark eval_always nodes as green
debug_assert!(!dep_node.kind.is_eval_always());

debug_assert_eq!(data.previous.index_to_node(prev_dep_node_index), *dep_node);
data.previous.debug_assert_eq(prev_dep_node_index, *dep_node);

let prev_deps = data.previous.edge_targets_from(prev_dep_node_index);

Expand All @@ -573,7 +572,7 @@ impl<K: DepKind> DepGraph<K> {
"try_mark_previous_green({:?}) --- found dependency {:?} to \
be immediately green",
dep_node,
data.previous.index_to_node(dep_dep_node_index)
data.previous.debug_dep_node(dep_dep_node_index),
);
current_deps.push(node_index);
}
Expand All @@ -586,12 +585,12 @@ impl<K: DepKind> DepGraph<K> {
"try_mark_previous_green({:?}) - END - dependency {:?} was \
immediately red",
dep_node,
data.previous.index_to_node(dep_dep_node_index)
data.previous.debug_dep_node(dep_dep_node_index)
);
return None;
}
None => {
let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index);
let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index, tcx);

// We don't know the state of this dependency. If it isn't
// an eval_always node, let's try to mark it green recursively.
Expand Down Expand Up @@ -700,18 +699,6 @@ impl<K: DepKind> DepGraph<K> {
data.current.intern_node(*dep_node, current_deps, fingerprint)
};

// We have just loaded a deserialized `DepNode` from the previous
// compilation session into the current one. If this was a foreign `DefId`,
// then we stored additional information in the incr comp cache when we
// initially created its fingerprint (see `DepNodeParams::to_fingerprint`)
// We won't be calling `to_fingerprint` again for this `DepNode` (we no longer
// have the original value), so we need to copy over this additional information
// from the old incremental cache into the new cache that we serialize
// and the end of this compilation session.
if dep_node.kind.can_reconstruct_query_key() {
tcx.register_reused_dep_path_hash(DefPathHash(dep_node.hash.into()));
}

// ... emitting any stored diagnostic ...

// FIXME: Store the fact that a node has diagnostics in a bit in the dep graph somewhere
Expand Down Expand Up @@ -814,7 +801,7 @@ impl<K: DepKind> DepGraph<K> {
for prev_index in data.colors.values.indices() {
match data.colors.get(prev_index) {
Some(DepNodeColor::Green(_)) => {
let dep_node = data.previous.index_to_node(prev_index);
let dep_node = data.previous.index_to_node(prev_index, tcx);
tcx.try_load_from_on_disk_cache(&dep_node);
}
None | Some(DepNodeColor::Red) => {
Expand Down
41 changes: 40 additions & 1 deletion compiler/rustc_query_system/src/dep_graph/prev.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex};
use super::{DepKind, DepNode};
use crate::dep_graph::DepContext;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_span::def_id::DefPathHash;

#[derive(Debug, Encodable, Decodable)]
pub struct PreviousDepGraph<K: DepKind> {
Expand Down Expand Up @@ -31,7 +33,44 @@ impl<K: DepKind> PreviousDepGraph<K> {
}

#[inline]
pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode<K> {
pub fn index_to_node<CTX: DepContext<DepKind = K>>(
&self,
dep_node_index: SerializedDepNodeIndex,
tcx: CTX,
) -> DepNode<K> {
let dep_node = self.data.nodes[dep_node_index];
// We have just loaded a deserialized `DepNode` from the previous
// compilation session into the current one. If this was a foreign `DefId`,
// then we stored additional information in the incr comp cache when we
// initially created its fingerprint (see `DepNodeParams::to_fingerprint`)
// We won't be calling `to_fingerprint` again for this `DepNode` (we no longer
// have the original value), so we need to copy over this additional information
// from the old incremental cache into the new cache that we serialize
// and the end of this compilation session.
if dep_node.kind.can_reconstruct_query_key() {
tcx.register_reused_dep_path_hash(DefPathHash(dep_node.hash.into()));
}
dep_node
}

/// When debug assertions are enabled, asserts that the dep node at `dep_node_index` is equal to `dep_node`.
/// This method should be preferred over manually calling `index_to_node`.
/// Calls to `index_to_node` may affect global state, so gating a call
/// to `index_to_node` on debug assertions could cause behavior changes when debug assertions
/// are enabled.
#[inline]
pub fn debug_assert_eq(&self, dep_node_index: SerializedDepNodeIndex, dep_node: DepNode<K>) {
debug_assert_eq!(self.data.nodes[dep_node_index], dep_node);
}

/// Obtains a debug-printable version of the `DepNode`.
/// See `debug_assert_eq` for why this should be preferred over manually
/// calling `dep_node_index`
pub fn debug_dep_node(&self, dep_node_index: SerializedDepNodeIndex) -> impl std::fmt::Debug {
// We're returning the `DepNode` without calling `register_reused_dep_path_hash`,
// but `impl Debug` return type means that it can only be used for debug printing.
// So, there's no risk of calls trying to create new dep nodes that have this
// node as a dependency
self.data.nodes[dep_node_index]
}

Expand Down
6 changes: 6 additions & 0 deletions src/test/incremental/auxiliary/issue-79661.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#![feature(rustc_attrs)]

#[cfg_attr(any(rpass2, rpass3), doc = "Some comment")]
pub struct Foo;

pub struct Wrapper(Foo);
14 changes: 14 additions & 0 deletions src/test/incremental/issue-79661-missing-def-path-hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// aux-build:issue-79661.rs
// revisions: rpass1 rpass2 rpass3

// Regression test for issue #79661
// We were failing to copy over a DefPathHash->DefId mapping
// from the old incremental cache to the new incremental cache
// when we ended up forcing a query. As a result, a subsequent
// unchanged incremental run would crash due to the missing mapping

extern crate issue_79661;
use issue_79661::Wrapper;

pub struct Outer(Wrapper);
fn main() {}

0 comments on commit c294640

Please sign in to comment.