From 3e9f80f32549c49ab2772f76ab0f8474001504fb Mon Sep 17 00:00:00 2001 From: xmakro Date: Tue, 2 Jun 2026 20:24:04 -0700 Subject: [PATCH] Evaluate free constants and statics in definition order under the parallel front-end Under the parallel front-end, free constant and static values are forced inside par_hir_body_owners, so worker threads pick up items in an arbitrary order. A const whose initializer refers to another const evaluates it as a nested, depth-limited query, so a thread that starts on a const before its dependencies are cached builds a query stack as deep as the uncached chain. A long enough chain can then spuriously exceed the recursion limit depending on scheduling (issue 146616). When more than one thread is in use, force these values sequentially in definition order before the parallel loop, matching the single-threaded order so each const's dependencies are already cached when it is evaluated. This only changes the order in which the existing set of free statics and free consts is forced; it does not change which items are evaluated, the recursion limit, or single-threaded output. maybe_check_static_with_link_section stays in the parallel loop so it still runs exactly once. Re-enable chained-constants-stackoverflow.rs under the parallel front-end. --- compiler/rustc_hir_analysis/src/lib.rs | 71 ++++++++++++++----- .../consts/chained-constants-stackoverflow.rs | 1 - 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 1e9bc80749881..a40bf26ed23e2 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -90,6 +90,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{Const, Ty, TyCtxt}; use rustc_middle::{middle, ty}; use rustc_session::errors::feature_err; +use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits; @@ -145,6 +146,34 @@ pub fn provide(providers: &mut Providers) { }; } +/// Force the value of a `static` or of a non-generic free `const`, so that a +/// crate that defines an item whose value fails to evaluate does not pass +/// compilation, even when the item is otherwise unused. +/// +/// This only forces the (idempotent, cached) evaluation queries; side-effecting +/// checks such as [`check::maybe_check_static_with_link_section`] are left to the +/// caller so that they run exactly once. +fn eval_const_or_static<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId) { + let def_kind = tcx.def_kind(item_def_id); + match def_kind { + DefKind::Static { .. } => { + tcx.ensure_ok().eval_static_initializer(item_def_id); + } + DefKind::Const { .. } + if !tcx.generics_of(item_def_id).own_requires_monomorphization() + && !tcx.is_type_const(item_def_id) => + { + // FIXME(generic_const_items): Passing empty instead of identity args is fishy but + // seems to be fine for now. Revisit this! + let instance = ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty()); + let cid = GlobalId { instance, promoted: None }; + let typing_env = ty::TypingEnv::fully_monomorphized(); + tcx.ensure_ok().eval_to_const_value_raw(typing_env.as_query_input(cid)); + } + _ => (), + } +} + pub fn check_crate(tcx: TyCtxt<'_>) { let _prof_timer = tcx.sess.timer("type_check_crate"); @@ -163,27 +192,31 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _: R = tcx.ensure_result().crate_inherent_impls_overlap_check(()); }); + // Make sure we evaluate all static items, and all non-generic free const items, even + // if unused. If any of these fail to evaluate, we do not want this crate to pass + // compilation. + // + // Under the parallel front-end, force these values sequentially in definition order + // before the parallel loop below. A constant whose initializer refers to another + // constant evaluates that other constant as a nested, depth-limited query. Evaluating + // in definition order keeps these chains shallow because each constant's dependencies + // are already cached by the time it is evaluated, matching the single-threaded order. + // Without this, worker threads can start evaluating a constant before the constants it + // depends on are cached, building a query stack whose depth depends on scheduling and + // can spuriously exceed the recursion limit. + if matches!(tcx.sess.threads(), Some(threads) if threads > 1) { + for item_def_id in tcx.hir_body_owners() { + eval_const_or_static(tcx, item_def_id); + } + } + tcx.par_hir_body_owners(|item_def_id| { let def_kind = tcx.def_kind(item_def_id); - // Make sure we evaluate all static and (non-associated) const items, even if unused. - // If any of these fail to evaluate, we do not want this crate to pass compilation. - match def_kind { - DefKind::Static { .. } => { - tcx.ensure_ok().eval_static_initializer(item_def_id); - check::maybe_check_static_with_link_section(tcx, item_def_id); - } - DefKind::Const { .. } - if !tcx.generics_of(item_def_id).own_requires_monomorphization() - && !tcx.is_type_const(item_def_id) => - { - // FIXME(generic_const_items): Passing empty instead of identity args is fishy but - // seems to be fine for now. Revisit this! - let instance = ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty()); - let cid = GlobalId { instance, promoted: None }; - let typing_env = ty::TypingEnv::fully_monomorphized(); - tcx.ensure_ok().eval_to_const_value_raw(typing_env.as_query_input(cid)); - } - _ => (), + // In single-threaded mode this is the first and only time these values are forced; + // under the parallel front-end they were already forced (and cached) above. + eval_const_or_static(tcx, item_def_id); + if matches!(def_kind, DefKind::Static { .. }) { + check::maybe_check_static_with_link_section(tcx, item_def_id); } // Skip `AnonConst`s because we feed their `type_of`. // Also skip items for which typeck forwards to parent typeck. diff --git a/tests/ui/consts/chained-constants-stackoverflow.rs b/tests/ui/consts/chained-constants-stackoverflow.rs index 0775ea56bbe00..2c8b0ffe86d6f 100644 --- a/tests/ui/consts/chained-constants-stackoverflow.rs +++ b/tests/ui/consts/chained-constants-stackoverflow.rs @@ -1,5 +1,4 @@ //@ run-pass -//@ ignore-parallel-frontend queries overflow the depth limit // https://github.com/rust-lang/rust/issues/34997 pub const CST_1: u32 = 0;