Skip to content

Commit 9b0899e

Browse files
committed
Auto merge of rust-lang#115613 - oli-obk:create_def_forever_red, r=<try>
Make create_def a side effect instead of marking the entire query as always red Before this PR: * query A creates def id D * query A is marked as depending on the always-red node, meaning it will always get re-run * in the next run of rustc: query A is not loaded from the incremental cache, but rerun After this PR: * query A creates def id D * query system registers this a side effect (just like we collect diagnostics to re-emit them without running a query) * in the next run of rustc: query A is loaded from the incremental cache and its side effect is run (thus re-creating def id D without running query A) r? `@cjgillot` TODO: * [ ] need to make feeding queries a side effect, too. At least ones that aren't written to disk. * [ ] need to re-feed the `def_span` query * [ ] many more tests
2 parents db8aca4 + c490a96 commit 9b0899e

File tree

14 files changed

+245
-60
lines changed

14 files changed

+245
-60
lines changed

compiler/rustc_hir/src/definitions.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,11 @@ impl Definitions {
344344
}
345345

346346
/// Adds a definition with a parent definition.
347-
pub fn create_def(&mut self, parent: LocalDefId, data: DefPathData) -> LocalDefId {
347+
pub fn create_def(
348+
&mut self,
349+
parent: LocalDefId,
350+
data: DefPathData,
351+
) -> (LocalDefId, DefPathHash) {
348352
// We can't use `Debug` implementation for `LocalDefId` here, since it tries to acquire a
349353
// reference to `Definitions` and we're already holding a mutable reference.
350354
debug!(
@@ -373,7 +377,7 @@ impl Definitions {
373377
debug!("create_def: after disambiguation, key = {:?}", key);
374378

375379
// Create the definition.
376-
LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) }
380+
(LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) }, def_path_hash)
377381
}
378382

379383
#[inline(always)]

compiler/rustc_hir_analysis/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,15 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
201201
}
202202
});
203203

204+
// Make sure we actually have a value for static items, as they aren't cached in incremental.
205+
// While we could just wait for codegen to invoke this, the definitions freeze below will cause
206+
// that to ICE, because evaluating statics can create more items.
207+
tcx.hir().par_body_owners(|item_def_id| {
208+
if let DefKind::Static { .. } = tcx.def_kind(item_def_id) {
209+
let _ = tcx.eval_static_initializer(item_def_id);
210+
}
211+
});
212+
204213
// Freeze definitions as we don't add new ones at this point. This improves performance by
205214
// allowing lock-free access to them.
206215
tcx.untracked().definitions.freeze();

compiler/rustc_interface/src/callbacks.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
3232
fn track_diagnostic<R>(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
3333
tls::with_context_opt(|icx| {
3434
if let Some(icx) = icx {
35-
if let Some(diagnostics) = icx.diagnostics {
36-
diagnostics.lock().extend(Some(diagnostic.clone()));
35+
if let Some(side_effects) = icx.side_effects {
36+
let diagnostic = diagnostic.clone();
37+
side_effects.lock().diagnostics.push(diagnostic);
3738
}
3839

3940
// Diagnostics are tracked, we can ignore the dependency.

compiler/rustc_macros/src/query.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,17 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
314314
let mut query_description_stream = quote! {};
315315
let mut query_cached_stream = quote! {};
316316
let mut feedable_queries = quote! {};
317+
let mut errors = quote! {};
318+
319+
macro_rules! assert {
320+
( $cond:expr, $span:expr, $( $tt:tt )+ ) => {
321+
if !$cond {
322+
errors.extend(
323+
Error::new($span, format!($($tt)*)).into_compile_error(),
324+
);
325+
}
326+
}
327+
}
317328

318329
for query in queries.0 {
319330
let Query { name, arg, modifiers, .. } = &query;
@@ -369,10 +380,15 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
369380
[#attribute_stream] fn #name(#arg) #result,
370381
});
371382

372-
if modifiers.feedable.is_some() {
373-
assert!(modifiers.anon.is_none(), "Query {name} cannot be both `feedable` and `anon`.");
383+
if let Some(feedable) = &modifiers.feedable {
384+
assert!(
385+
modifiers.anon.is_none(),
386+
feedable.span(),
387+
"Query {name} cannot be both `feedable` and `anon`."
388+
);
374389
assert!(
375390
modifiers.eval_always.is_none(),
391+
feedable.span(),
376392
"Query {name} cannot be both `feedable` and `eval_always`."
377393
);
378394
feedable_queries.extend(quote! {
@@ -407,5 +423,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
407423
use super::*;
408424
#query_cached_stream
409425
}
426+
#errors
410427
})
411428
}

compiler/rustc_middle/src/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1065,7 +1065,6 @@ rustc_queries! {
10651065
"evaluating initializer of static `{}`",
10661066
tcx.def_path_str(key)
10671067
}
1068-
cache_on_disk_if { key.is_local() }
10691068
separate_provide_extern
10701069
feedable
10711070
}
@@ -1711,6 +1710,7 @@ rustc_queries! {
17111710
desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }
17121711
separate_provide_extern
17131712
feedable
1713+
cache_on_disk_if { def_id.is_local() }
17141714
}
17151715

17161716
query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> {

compiler/rustc_middle/src/ty/context.rs

+51-13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
pub mod tls;
66

7+
use rustc_query_system::query::DefIdInfo;
78
pub use rustc_type_ir::lift::Lift;
89

910
use crate::arena::Arena;
@@ -57,7 +58,7 @@ use rustc_hir::lang_items::LangItem;
5758
use rustc_hir::{HirId, Node, TraitCandidate};
5859
use rustc_index::IndexVec;
5960
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
60-
use rustc_query_system::dep_graph::DepNodeIndex;
61+
use rustc_query_system::dep_graph::{DepNodeIndex, TaskDepsRef};
6162
use rustc_query_system::ich::StableHashingContext;
6263
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
6364
use rustc_session::config::CrateType;
@@ -72,7 +73,7 @@ use rustc_target::spec::abi;
7273
use rustc_type_ir::TyKind::*;
7374
use rustc_type_ir::WithCachedTypeInfo;
7475
use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};
75-
use tracing::{debug, instrument};
76+
use tracing::{debug, instrument, trace};
7677

7778
use std::assert_matches::assert_matches;
7879
use std::borrow::Borrow;
@@ -1332,34 +1333,71 @@ impl<'tcx> TyCtxtAt<'tcx> {
13321333

13331334
impl<'tcx> TyCtxt<'tcx> {
13341335
/// `tcx`-dependent operations performed for every created definition.
1336+
#[instrument(level = "trace", skip(self))]
13351337
pub fn create_def(
13361338
self,
13371339
parent: LocalDefId,
13381340
name: Symbol,
13391341
def_kind: DefKind,
13401342
) -> TyCtxtFeed<'tcx, LocalDefId> {
13411343
let data = def_kind.def_path_data(name);
1342-
// The following call has the side effect of modifying the tables inside `definitions`.
1344+
// The following create_def calls have the side effect of modifying the tables inside `definitions`.
13431345
// These very tables are relied on by the incr. comp. engine to decode DepNodes and to
13441346
// decode the on-disk cache.
13451347
//
13461348
// Any LocalDefId which is used within queries, either as key or result, either:
1347-
// - has been created before the construction of the TyCtxt;
1349+
// - has been created before the construction of the TyCtxt,
1350+
// - has been created when marking a query as green (recreating definitions it created in the actual run),
13481351
// - has been created by this call to `create_def`.
1349-
// As a consequence, this LocalDefId is always re-created before it is needed by the incr.
1350-
// comp. engine itself.
13511352
//
13521353
// This call also writes to the value of `source_span` and `expn_that_defined` queries.
13531354
// This is fine because:
13541355
// - those queries are `eval_always` so we won't miss their result changing;
13551356
// - this write will have happened before these queries are called.
1356-
let def_id = self.untracked.definitions.write().create_def(parent, data);
1357-
1358-
// This function modifies `self.definitions` using a side-effect.
1359-
// We need to ensure that these side effects are re-run by the incr. comp. engine.
1360-
// Depending on the forever-red node will tell the graph that the calling query
1361-
// needs to be re-evaluated.
1362-
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
1357+
let def_id = tls::with_context(|icx| {
1358+
match icx.task_deps {
1359+
// Always gets rerun anyway, so nothing to replay
1360+
TaskDepsRef::EvalAlways => {
1361+
let def_id = self.untracked.definitions.write().create_def(parent, data).0;
1362+
trace!(?def_id, "eval always");
1363+
def_id
1364+
}
1365+
// Top-level queries like the resolver get rerun every time anyway
1366+
TaskDepsRef::Ignore => {
1367+
let def_id = self.untracked.definitions.write().create_def(parent, data).0;
1368+
trace!(?def_id, "ignore");
1369+
def_id
1370+
}
1371+
TaskDepsRef::Forbid => bug!(
1372+
"cannot create definition {parent:?}, {name:?}, {def_kind:?} without being able to register task dependencies"
1373+
),
1374+
TaskDepsRef::Allow(_) => {
1375+
let (def_id, hash) =
1376+
self.untracked.definitions.write().create_def(parent, data);
1377+
trace!(?def_id, "record side effects");
1378+
1379+
icx.side_effects.as_ref().unwrap().lock().definitions.push(DefIdInfo {
1380+
parent,
1381+
data,
1382+
hash,
1383+
});
1384+
def_id
1385+
}
1386+
TaskDepsRef::Replay { prev_side_effects, created_def_ids } => {
1387+
trace!(?created_def_ids, "replay side effects");
1388+
trace!("num_defs : {}", prev_side_effects.definitions.len());
1389+
let index = created_def_ids.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1390+
let prev_info = &prev_side_effects.definitions[index];
1391+
let def_id = self.untracked.definitions.read().local_def_path_hash_to_def_id(
1392+
prev_info.hash,
1393+
&"should have already recreated def id in try_mark_green",
1394+
);
1395+
assert_eq!(prev_info.data, data);
1396+
assert_eq!(prev_info.parent, parent);
1397+
def_id
1398+
}
1399+
}
1400+
});
13631401

13641402
let feed = TyCtxtFeed { tcx: self, key: def_id };
13651403
feed.def_kind(def_kind);

compiler/rustc_middle/src/ty/context/tls.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ use super::{GlobalCtxt, TyCtxt};
33
use crate::dep_graph::TaskDepsRef;
44
use crate::query::plumbing::QueryJobId;
55
use rustc_data_structures::sync::{self, Lock};
6-
use rustc_errors::DiagInner;
6+
use rustc_query_system::query::QuerySideEffects;
77
#[cfg(not(parallel_compiler))]
88
use std::cell::Cell;
99
use std::mem;
1010
use std::ptr;
11-
use thin_vec::ThinVec;
1211

1312
/// This is the implicit state of rustc. It contains the current
1413
/// `TyCtxt` and query. It is updated when creating a local interner or
@@ -26,7 +25,7 @@ pub struct ImplicitCtxt<'a, 'tcx> {
2625

2726
/// Where to store diagnostics for the current query job, if any.
2827
/// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query.
29-
pub diagnostics: Option<&'a Lock<ThinVec<DiagInner>>>,
28+
pub side_effects: Option<&'a Lock<QuerySideEffects>>,
3029

3130
/// Used to prevent queries from calling too deeply.
3231
pub query_depth: usize,
@@ -42,7 +41,7 @@ impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> {
4241
ImplicitCtxt {
4342
tcx,
4443
query: None,
45-
diagnostics: None,
44+
side_effects: None,
4645
query_depth: 0,
4746
task_deps: TaskDepsRef::Ignore,
4847
}

compiler/rustc_query_impl/src/plumbing.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::QueryConfigRestored;
66
use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher};
77
use rustc_data_structures::sync::Lock;
88
use rustc_data_structures::unord::UnordMap;
9-
use rustc_errors::DiagInner;
109
use rustc_index::Idx;
1110
use rustc_middle::bug;
1211
use rustc_middle::dep_graph::dep_kinds;
@@ -22,16 +21,15 @@ use rustc_middle::ty::{self, TyCtxt, TyEncoder};
2221
use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext};
2322
use rustc_query_system::ich::StableHashingContext;
2423
use rustc_query_system::query::{
25-
force_query, QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffects,
26-
QueryStackFrame,
24+
force_query, DefIdInfo, QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap,
25+
QuerySideEffects, QueryStackFrame,
2726
};
2827
use rustc_query_system::{LayoutOfDepth, QueryOverflow};
2928
use rustc_serialize::Decodable;
3029
use rustc_serialize::Encodable;
3130
use rustc_session::Limit;
3231
use rustc_span::def_id::LOCAL_CRATE;
3332
use std::num::NonZero;
34-
use thin_vec::ThinVec;
3533

3634
#[derive(Copy, Clone)]
3735
pub struct QueryCtxt<'tcx> {
@@ -125,7 +123,7 @@ impl QueryContext for QueryCtxt<'_> {
125123
self,
126124
token: QueryJobId,
127125
depth_limit: bool,
128-
diagnostics: Option<&Lock<ThinVec<DiagInner>>>,
126+
side_effects: Option<&Lock<QuerySideEffects>>,
129127
compute: impl FnOnce() -> R,
130128
) -> R {
131129
// The `TyCtxt` stored in TLS has the same global interner lifetime
@@ -140,7 +138,7 @@ impl QueryContext for QueryCtxt<'_> {
140138
let new_icx = ImplicitCtxt {
141139
tcx: self.tcx,
142140
query: Some(token),
143-
diagnostics,
141+
side_effects,
144142
query_depth: current_icx.query_depth + depth_limit as usize,
145143
task_deps: current_icx.task_deps,
146144
};
@@ -172,6 +170,21 @@ impl QueryContext for QueryCtxt<'_> {
172170
crate_name: self.crate_name(LOCAL_CRATE),
173171
});
174172
}
173+
174+
#[tracing::instrument(level = "trace", skip(self, side_effects))]
175+
fn apply_side_effects(self, side_effects: QuerySideEffects) {
176+
let dcx = self.dep_context().sess().dcx();
177+
let QuerySideEffects { diagnostics, definitions } = side_effects;
178+
179+
for diagnostic in diagnostics {
180+
dcx.emit_diagnostic(diagnostic);
181+
}
182+
183+
for DefIdInfo { parent, data, hash } in definitions {
184+
let (_def_id, h) = self.tcx.untracked().definitions.write().create_def(parent, data);
185+
debug_assert_eq!(h, hash);
186+
}
187+
}
175188
}
176189

177190
pub(super) fn try_mark_green<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool {

0 commit comments

Comments
 (0)