Skip to content

Commit fa55f66

Browse files
committed
Auto merge of #79721 - Aaron1011:fix/reuse-def-path-hash, r=wesleywiser
Properly re-use def path hash in incremental mode Fixes #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 #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.
2 parents cc03ee6 + c294640 commit fa55f66

File tree

6 files changed

+79
-20
lines changed

6 files changed

+79
-20
lines changed

compiler/rustc_middle/src/ty/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &De
220220
.map(|c| c.is_green())
221221
.unwrap_or(false));
222222

223-
let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap();
223+
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));
224224
if queries::$name::cache_on_disk(tcx, &key, None) {
225225
let _ = tcx.$name(key);
226226
}

compiler/rustc_query_system/src/dep_graph/dep_node.rs

+13
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ use std::hash::Hash;
5353
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
5454
pub struct DepNode<K> {
5555
pub kind: K,
56+
// Important - whenever a `DepNode` is constructed, we need to make
57+
// sure to register a `DefPathHash -> DefId` mapping if needed.
58+
// This is currently done in two places:
59+
//
60+
// * When a `DepNode::construct` is called, `arg.to_fingerprint()`
61+
// is responsible for calling `OnDiskCache::store_foreign_def_id_hash`
62+
// if needed
63+
// * When a `DepNode` is loaded from the `PreviousDepGraph`,
64+
// then `PreviousDepGraph::index_to_node` is responsible for calling
65+
// `tcx.register_reused_dep_path_hash`
66+
//
67+
// FIXME: Enforce this by preventing manual construction of `DefNode`
68+
// (e.g. add a `_priv: ()` field)
5669
pub hash: PackedFingerprint,
5770
}
5871

compiler/rustc_query_system/src/dep_graph/graph.rs

+5-18
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
77
use rustc_data_structures::unlikely;
88
use rustc_errors::Diagnostic;
99
use rustc_index::vec::{Idx, IndexVec};
10-
use rustc_span::def_id::DefPathHash;
1110

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

558-
debug_assert_eq!(data.previous.index_to_node(prev_dep_node_index), *dep_node);
557+
data.previous.debug_assert_eq(prev_dep_node_index, *dep_node);
559558

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

@@ -573,7 +572,7 @@ impl<K: DepKind> DepGraph<K> {
573572
"try_mark_previous_green({:?}) --- found dependency {:?} to \
574573
be immediately green",
575574
dep_node,
576-
data.previous.index_to_node(dep_dep_node_index)
575+
data.previous.debug_dep_node(dep_dep_node_index),
577576
);
578577
current_deps.push(node_index);
579578
}
@@ -586,12 +585,12 @@ impl<K: DepKind> DepGraph<K> {
586585
"try_mark_previous_green({:?}) - END - dependency {:?} was \
587586
immediately red",
588587
dep_node,
589-
data.previous.index_to_node(dep_dep_node_index)
588+
data.previous.debug_dep_node(dep_dep_node_index)
590589
);
591590
return None;
592591
}
593592
None => {
594-
let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index);
593+
let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index, tcx);
595594

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

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

717704
// FIXME: Store the fact that a node has diagnostics in a bit in the dep graph somewhere
@@ -814,7 +801,7 @@ impl<K: DepKind> DepGraph<K> {
814801
for prev_index in data.colors.values.indices() {
815802
match data.colors.get(prev_index) {
816803
Some(DepNodeColor::Green(_)) => {
817-
let dep_node = data.previous.index_to_node(prev_index);
804+
let dep_node = data.previous.index_to_node(prev_index, tcx);
818805
tcx.try_load_from_on_disk_cache(&dep_node);
819806
}
820807
None | Some(DepNodeColor::Red) => {

compiler/rustc_query_system/src/dep_graph/prev.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex};
22
use super::{DepKind, DepNode};
3+
use crate::dep_graph::DepContext;
34
use rustc_data_structures::fingerprint::Fingerprint;
45
use rustc_data_structures::fx::FxHashMap;
6+
use rustc_span::def_id::DefPathHash;
57

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

3335
#[inline]
34-
pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode<K> {
36+
pub fn index_to_node<CTX: DepContext<DepKind = K>>(
37+
&self,
38+
dep_node_index: SerializedDepNodeIndex,
39+
tcx: CTX,
40+
) -> DepNode<K> {
41+
let dep_node = self.data.nodes[dep_node_index];
42+
// We have just loaded a deserialized `DepNode` from the previous
43+
// compilation session into the current one. If this was a foreign `DefId`,
44+
// then we stored additional information in the incr comp cache when we
45+
// initially created its fingerprint (see `DepNodeParams::to_fingerprint`)
46+
// We won't be calling `to_fingerprint` again for this `DepNode` (we no longer
47+
// have the original value), so we need to copy over this additional information
48+
// from the old incremental cache into the new cache that we serialize
49+
// and the end of this compilation session.
50+
if dep_node.kind.can_reconstruct_query_key() {
51+
tcx.register_reused_dep_path_hash(DefPathHash(dep_node.hash.into()));
52+
}
53+
dep_node
54+
}
55+
56+
/// When debug assertions are enabled, asserts that the dep node at `dep_node_index` is equal to `dep_node`.
57+
/// This method should be preferred over manually calling `index_to_node`.
58+
/// Calls to `index_to_node` may affect global state, so gating a call
59+
/// to `index_to_node` on debug assertions could cause behavior changes when debug assertions
60+
/// are enabled.
61+
#[inline]
62+
pub fn debug_assert_eq(&self, dep_node_index: SerializedDepNodeIndex, dep_node: DepNode<K>) {
63+
debug_assert_eq!(self.data.nodes[dep_node_index], dep_node);
64+
}
65+
66+
/// Obtains a debug-printable version of the `DepNode`.
67+
/// See `debug_assert_eq` for why this should be preferred over manually
68+
/// calling `dep_node_index`
69+
pub fn debug_dep_node(&self, dep_node_index: SerializedDepNodeIndex) -> impl std::fmt::Debug {
70+
// We're returning the `DepNode` without calling `register_reused_dep_path_hash`,
71+
// but `impl Debug` return type means that it can only be used for debug printing.
72+
// So, there's no risk of calls trying to create new dep nodes that have this
73+
// node as a dependency
3574
self.data.nodes[dep_node_index]
3675
}
3776

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![feature(rustc_attrs)]
2+
3+
#[cfg_attr(any(rpass2, rpass3), doc = "Some comment")]
4+
pub struct Foo;
5+
6+
pub struct Wrapper(Foo);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// aux-build:issue-79661.rs
2+
// revisions: rpass1 rpass2 rpass3
3+
4+
// Regression test for issue #79661
5+
// We were failing to copy over a DefPathHash->DefId mapping
6+
// from the old incremental cache to the new incremental cache
7+
// when we ended up forcing a query. As a result, a subsequent
8+
// unchanged incremental run would crash due to the missing mapping
9+
10+
extern crate issue_79661;
11+
use issue_79661::Wrapper;
12+
13+
pub struct Outer(Wrapper);
14+
fn main() {}

0 commit comments

Comments
 (0)