Skip to content

Commit cfb5ae2

Browse files
committed
Auto merge of rust-lang#100748 - SparrowLii:query_depth, r=cjgillot
add `depth_limit` in `QueryVTable` to avoid entering a new tcx in `layout_of` Fixes rust-lang#49735 Updates rust-lang#48685 The `layout_of` query needs to check whether it overflows the depth limit, and the current implementation needs to create a new `ImplicitCtxt` inside `layout_of`. However, `start_query` will already create a new `ImplicitCtxt`, so we can check the depth limit in `start_query`. We can tell whether we need to check the depth limit simply by whether the return value of `to_debug_str` of the query is `layout_of`. But I think adding the `depth_limit` field in `QueryVTable` may be more elegant and more scalable.
2 parents 7480389 + cbc6bd2 commit cfb5ae2

File tree

8 files changed

+83
-54
lines changed

8 files changed

+83
-54
lines changed

compiler/rustc_macros/src/query.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,12 @@ struct QueryModifiers {
106106
/// Generate a dep node based on the dependencies of the query
107107
anon: Option<Ident>,
108108

109-
// Always evaluate the query, ignoring its dependencies
109+
/// Always evaluate the query, ignoring its dependencies
110110
eval_always: Option<Ident>,
111111

112+
/// Whether the query has a call depth limit
113+
depth_limit: Option<Ident>,
114+
112115
/// Use a separate query provider for local and extern crates
113116
separate_provide_extern: Option<Ident>,
114117

@@ -126,6 +129,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
126129
let mut no_hash = None;
127130
let mut anon = None;
128131
let mut eval_always = None;
132+
let mut depth_limit = None;
129133
let mut separate_provide_extern = None;
130134
let mut remap_env_constness = None;
131135

@@ -194,6 +198,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
194198
try_insert!(anon = modifier);
195199
} else if modifier == "eval_always" {
196200
try_insert!(eval_always = modifier);
201+
} else if modifier == "depth_limit" {
202+
try_insert!(depth_limit = modifier);
197203
} else if modifier == "separate_provide_extern" {
198204
try_insert!(separate_provide_extern = modifier);
199205
} else if modifier == "remap_env_constness" {
@@ -215,6 +221,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
215221
no_hash,
216222
anon,
217223
eval_always,
224+
depth_limit,
218225
separate_provide_extern,
219226
remap_env_constness,
220227
})
@@ -365,6 +372,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
365372
if let Some(eval_always) = &modifiers.eval_always {
366373
attributes.push(quote! { (#eval_always) });
367374
};
375+
// Pass on the depth_limit modifier
376+
if let Some(depth_limit) = &modifiers.depth_limit {
377+
attributes.push(quote! { (#depth_limit) });
378+
};
368379
// Pass on the separate_provide_extern modifier
369380
if let Some(separate_provide_extern) = &modifiers.separate_provide_extern {
370381
attributes.push(quote! { (#separate_provide_extern) });

compiler/rustc_middle/src/query/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,7 @@ rustc_queries! {
13091309
query layout_of(
13101310
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
13111311
) -> Result<ty::layout::TyAndLayout<'tcx>, ty::layout::LayoutError<'tcx>> {
1312+
depth_limit
13121313
desc { "computing layout of `{}`", key.value }
13131314
remap_env_constness
13141315
}

compiler/rustc_middle/src/ty/context.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1857,8 +1857,8 @@ pub mod tls {
18571857
/// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query.
18581858
pub diagnostics: Option<&'a Lock<ThinVec<Diagnostic>>>,
18591859

1860-
/// Used to prevent layout from recursing too deeply.
1861-
pub layout_depth: usize,
1860+
/// Used to prevent queries from calling too deeply.
1861+
pub query_depth: usize,
18621862

18631863
/// The current dep graph task. This is used to add dependencies to queries
18641864
/// when executing them.
@@ -1872,7 +1872,7 @@ pub mod tls {
18721872
tcx,
18731873
query: None,
18741874
diagnostics: None,
1875-
layout_depth: 0,
1875+
query_depth: 0,
18761876
task_deps: TaskDepsRef::Ignore,
18771877
}
18781878
}

compiler/rustc_middle/src/ty/layout.rs

+25-36
Original file line numberDiff line numberDiff line change
@@ -229,49 +229,38 @@ fn layout_of<'tcx>(
229229
tcx: TyCtxt<'tcx>,
230230
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
231231
) -> Result<TyAndLayout<'tcx>, LayoutError<'tcx>> {
232-
ty::tls::with_related_context(tcx, move |icx| {
233-
let (param_env, ty) = query.into_parts();
234-
debug!(?ty);
235-
236-
if !tcx.recursion_limit().value_within_limit(icx.layout_depth) {
237-
tcx.sess.fatal(&format!("overflow representing the type `{}`", ty));
232+
let (param_env, ty) = query.into_parts();
233+
debug!(?ty);
234+
235+
let param_env = param_env.with_reveal_all_normalized(tcx);
236+
let unnormalized_ty = ty;
237+
238+
// FIXME: We might want to have two different versions of `layout_of`:
239+
// One that can be called after typecheck has completed and can use
240+
// `normalize_erasing_regions` here and another one that can be called
241+
// before typecheck has completed and uses `try_normalize_erasing_regions`.
242+
let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
243+
Ok(t) => t,
244+
Err(normalization_error) => {
245+
return Err(LayoutError::NormalizationFailure(ty, normalization_error));
238246
}
247+
};
239248

240-
// Update the ImplicitCtxt to increase the layout_depth
241-
let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() };
242-
243-
ty::tls::enter_context(&icx, |_| {
244-
let param_env = param_env.with_reveal_all_normalized(tcx);
245-
let unnormalized_ty = ty;
246-
247-
// FIXME: We might want to have two different versions of `layout_of`:
248-
// One that can be called after typecheck has completed and can use
249-
// `normalize_erasing_regions` here and another one that can be called
250-
// before typecheck has completed and uses `try_normalize_erasing_regions`.
251-
let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
252-
Ok(t) => t,
253-
Err(normalization_error) => {
254-
return Err(LayoutError::NormalizationFailure(ty, normalization_error));
255-
}
256-
};
257-
258-
if ty != unnormalized_ty {
259-
// Ensure this layout is also cached for the normalized type.
260-
return tcx.layout_of(param_env.and(ty));
261-
}
249+
if ty != unnormalized_ty {
250+
// Ensure this layout is also cached for the normalized type.
251+
return tcx.layout_of(param_env.and(ty));
252+
}
262253

263-
let cx = LayoutCx { tcx, param_env };
254+
let cx = LayoutCx { tcx, param_env };
264255

265-
let layout = cx.layout_of_uncached(ty)?;
266-
let layout = TyAndLayout { ty, layout };
256+
let layout = cx.layout_of_uncached(ty)?;
257+
let layout = TyAndLayout { ty, layout };
267258

268-
cx.record_layout_for_printing(layout);
259+
cx.record_layout_for_printing(layout);
269260

270-
sanity_check_layout(&cx, &layout);
261+
sanity_check_layout(&cx, &layout);
271262

272-
Ok(layout)
273-
})
274-
})
263+
Ok(layout)
275264
}
276265

277266
pub struct LayoutCx<'tcx, C> {

compiler/rustc_query_impl/src/plumbing.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,24 @@ impl QueryContext for QueryCtxt<'_> {
9696
fn start_query<R>(
9797
&self,
9898
token: QueryJobId,
99+
depth_limit: bool,
99100
diagnostics: Option<&Lock<ThinVec<Diagnostic>>>,
100101
compute: impl FnOnce() -> R,
101102
) -> R {
102103
// The `TyCtxt` stored in TLS has the same global interner lifetime
103104
// as `self`, so we use `with_related_context` to relate the 'tcx lifetimes
104105
// when accessing the `ImplicitCtxt`.
105106
tls::with_related_context(**self, move |current_icx| {
107+
if depth_limit && !self.recursion_limit().value_within_limit(current_icx.query_depth) {
108+
self.depth_limit_error();
109+
}
110+
106111
// Update the `ImplicitCtxt` to point to our new query job.
107112
let new_icx = ImplicitCtxt {
108113
tcx: **self,
109114
query: Some(token),
110115
diagnostics,
111-
layout_depth: current_icx.layout_depth,
116+
query_depth: current_icx.query_depth + depth_limit as usize,
112117
task_deps: current_icx.task_deps,
113118
};
114119

@@ -210,6 +215,18 @@ macro_rules! is_eval_always {
210215
};
211216
}
212217

218+
macro_rules! depth_limit {
219+
([]) => {{
220+
false
221+
}};
222+
([(depth_limit) $($rest:tt)*]) => {{
223+
true
224+
}};
225+
([$other:tt $($modifiers:tt)*]) => {
226+
depth_limit!([$($modifiers)*])
227+
};
228+
}
229+
213230
macro_rules! hash_result {
214231
([]) => {{
215232
Some(dep_graph::hash_result)
@@ -349,6 +366,7 @@ macro_rules! define_queries {
349366
QueryVTable {
350367
anon: is_anon!([$($modifiers)*]),
351368
eval_always: is_eval_always!([$($modifiers)*]),
369+
depth_limit: depth_limit!([$($modifiers)*]),
352370
dep_kind: dep_graph::DepKind::$name,
353371
hash_result: hash_result!([$($modifiers)*]),
354372
handle_cycle_error: |tcx, mut error| handle_cycle_error!([$($modifiers)*][tcx, error]),

compiler/rustc_query_system/src/query/config.rs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub struct QueryVTable<CTX: QueryContext, K, V> {
2323
pub anon: bool,
2424
pub dep_kind: CTX::DepKind,
2525
pub eval_always: bool,
26+
pub depth_limit: bool,
2627
pub cache_on_disk: bool,
2728

2829
pub compute: fn(CTX::DepContext, K) -> V,

compiler/rustc_query_system/src/query/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub use self::caches::{
1414
mod config;
1515
pub use self::config::{QueryConfig, QueryDescription, QueryVTable};
1616

17-
use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
17+
use crate::dep_graph::{DepContext, DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
1818

1919
use rustc_data_structures::sync::Lock;
2020
use rustc_data_structures::thin_vec::ThinVec;
@@ -119,7 +119,12 @@ pub trait QueryContext: HasDepContext {
119119
fn start_query<R>(
120120
&self,
121121
token: QueryJobId,
122+
depth_limit: bool,
122123
diagnostics: Option<&Lock<ThinVec<Diagnostic>>>,
123124
compute: impl FnOnce() -> R,
124125
) -> R;
126+
127+
fn depth_limit_error(&self) {
128+
self.dep_context().sess().fatal("queries overflow the depth limit!");
129+
}
125130
}

compiler/rustc_query_system/src/query/plumbing.rs

+16-12
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,9 @@ where
381381
// Fast path for when incr. comp. is off.
382382
if !dep_graph.is_fully_enabled() {
383383
let prof_timer = tcx.dep_context().profiler().query_provider();
384-
let result = tcx.start_query(job_id, None, || query.compute(*tcx.dep_context(), key));
384+
let result = tcx.start_query(job_id, query.depth_limit, None, || {
385+
query.compute(*tcx.dep_context(), key)
386+
});
385387
let dep_node_index = dep_graph.next_virtual_depnode_index();
386388
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
387389
return (result, dep_node_index);
@@ -394,7 +396,7 @@ where
394396

395397
// The diagnostics for this query will be promoted to the current session during
396398
// `try_mark_green()`, so we can ignore them here.
397-
if let Some(ret) = tcx.start_query(job_id, None, || {
399+
if let Some(ret) = tcx.start_query(job_id, false, None, || {
398400
try_load_from_disk_and_cache_in_memory(tcx, &key, &dep_node, query)
399401
}) {
400402
return ret;
@@ -404,18 +406,20 @@ where
404406
let prof_timer = tcx.dep_context().profiler().query_provider();
405407
let diagnostics = Lock::new(ThinVec::new());
406408

407-
let (result, dep_node_index) = tcx.start_query(job_id, Some(&diagnostics), || {
408-
if query.anon {
409-
return dep_graph.with_anon_task(*tcx.dep_context(), query.dep_kind, || {
410-
query.compute(*tcx.dep_context(), key)
411-
});
412-
}
409+
let (result, dep_node_index) =
410+
tcx.start_query(job_id, query.depth_limit, Some(&diagnostics), || {
411+
if query.anon {
412+
return dep_graph.with_anon_task(*tcx.dep_context(), query.dep_kind, || {
413+
query.compute(*tcx.dep_context(), key)
414+
});
415+
}
413416

414-
// `to_dep_node` is expensive for some `DepKind`s.
415-
let dep_node = dep_node_opt.unwrap_or_else(|| query.to_dep_node(*tcx.dep_context(), &key));
417+
// `to_dep_node` is expensive for some `DepKind`s.
418+
let dep_node =
419+
dep_node_opt.unwrap_or_else(|| query.to_dep_node(*tcx.dep_context(), &key));
416420

417-
dep_graph.with_task(dep_node, *tcx.dep_context(), key, query.compute, query.hash_result)
418-
});
421+
dep_graph.with_task(dep_node, *tcx.dep_context(), key, query.compute, query.hash_result)
422+
});
419423

420424
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
421425

0 commit comments

Comments
 (0)