From bea0a4461cd223de4b63e65d1848647fa2f398a8 Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Thu, 29 Jan 2026 22:05:07 +0000 Subject: [PATCH 1/2] Do not deduce parameter attributes during CTFE `fn_abi_of_instance` can look at function bodies to deduce codegen optimization attributes (namely `ArgAttribute::ReadOnly` and `ArgAttribute::CapturesNone`) of indirectly-passed parameters. This can lead to cycles when performed during typeck, when such attributes are irrelevant. This patch breaks a subquery out from `fn_abi_of_instance`, `fn_abi_of_instance_no_deduced_attrs`, which returns the ABI before such parameter attributes are deduced; and that new subquery is used in CTFE instead. --- .../rustc_const_eval/src/interpret/call.rs | 5 +- .../src/interpret/eval_context.rs | 8 +- .../rustc_const_eval/src/interpret/step.rs | 2 +- compiler/rustc_middle/src/queries.rs | 29 +++- compiler/rustc_middle/src/ty/layout.rs | 53 +++++- compiler/rustc_ty_utils/src/abi.rs | 154 ++++++++++++------ .../ui/coroutine/avoid-query-cycle-in-ctfe.rs | 18 ++ 7 files changed, 200 insertions(+), 69 deletions(-) create mode 100644 tests/ui/coroutine/avoid-query-cycle-in-ctfe.rs diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index c7ddcaa731579..d8dd9ee11f02c 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -355,7 +355,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Compute callee information. // FIXME: for variadic support, do we have to somehow determine callee's extra_args? - let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; + let callee_fn_abi = + self.fn_abi_of_instance_no_deduced_attrs(instance, ty::List::empty())?; if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { throw_unsup_format!("calling a c-variadic function is not supported"); @@ -849,7 +850,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { enter_trace_span!(M, resolve::resolve_drop_in_place, ty = ?place.layout.ty); ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty) }; - let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; + let fn_abi = self.fn_abi_of_instance_no_deduced_attrs(instance, ty::List::empty())?; let arg = self.mplace_to_ref(&place)?; let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?); diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 8c9a79abe48d2..4bb5a801c58e8 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -153,16 +153,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } /// This inherent method takes priority over the trait method with the same name in FnAbiOf, - /// and allows wrapping the actual [FnAbiOf::fn_abi_of_instance] with a tracing span. - /// See [FnAbiOf::fn_abi_of_instance] for the original documentation. + /// and allows wrapping the actual [FnAbiOf::fn_abi_of_instance_no_deduced_attrs] with a tracing span. + /// See [FnAbiOf::fn_abi_of_instance_no_deduced_attrs] for the original documentation. #[inline(always)] - pub fn fn_abi_of_instance( + pub fn fn_abi_of_instance_no_deduced_attrs( &self, instance: ty::Instance<'tcx>, extra_args: &'tcx ty::List>, ) -> >::FnAbiOfResult { let _trace = enter_trace_span!(M, layouting::fn_abi_of_instance, ?instance, ?extra_args); - FnAbiOf::fn_abi_of_instance(self, instance, extra_args) + FnAbiOf::fn_abi_of_instance_no_deduced_attrs(self, instance, extra_args) } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 4aa9030cfe61c..fc27d01b72136 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -476,7 +476,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let instance = self.resolve(def_id, args)?; ( FnVal::Instance(instance), - self.fn_abi_of_instance(instance, extra_args)?, + self.fn_abi_of_instance_no_deduced_attrs(instance, extra_args)?, instance.def.requires_caller_location(*self.tcx), ) } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 32fc7198425b3..c0b47386d81b9 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1793,11 +1793,32 @@ rustc_queries! { desc { "computing call ABI of `{}` function pointers", key.value.0 } } - /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for - /// direct calls to an `fn`. + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI might not include all possible + /// codegen optimization attributes (such as `ReadOnly` or `CapturesNone`), as deducing these + /// requires inspection of function bodies that can lead to cycles when performed during typeck. + /// Post typeck, you should prefer the optimized ABI returned by `fn_abi_of_instance`. + /// + /// NB: the ABI returned by this query must not differ from that returned by + /// `fn_abi_of_instance` in any other way. + /// + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). + query fn_abi_of_instance_no_deduced_attrs( + key: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)> + ) -> Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { + desc { "computing unadjusted call ABI of `{}`", key.value.0 } + } + + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI will include applicable + /// codegen optimization attributes, including `ReadOnly` and `CapturesNone` -- deduction of + /// which requires inspection of function bodies that can lead to cycles when performed during + /// typeck. During typeck, you should therefore use instead the unoptimized ABI returned by + /// `fn_abi_of_instance_no_deduced_attrs`. /// - /// NB: that includes virtual calls, which are represented by "direct calls" - /// to an `InstanceKind::Virtual` instance (of `::fn`). + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). query fn_abi_of_instance( key: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)> ) -> Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d3b4654a8d799..ac76199feff53 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1368,11 +1368,56 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> { ) } - /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for - /// direct calls to an `fn`. + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI might not include all possible + /// codegen optimization attributes (such as `ReadOnly` or `CapturesNone`), as deducing these + /// requires inspection of function bodies that can lead to cycles when performed during typeck. + /// Post typeck, you should prefer the optimized ABI returned by `fn_abi_of_instance`. /// - /// NB: that includes virtual calls, which are represented by "direct calls" - /// to an `InstanceKind::Virtual` instance (of `::fn`). + /// NB: the ABI returned by this query must not differ from that returned by + /// `fn_abi_of_instance` in any other way. + /// + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). + #[inline] + #[tracing::instrument(level = "debug", skip(self))] + fn fn_abi_of_instance_no_deduced_attrs( + &self, + instance: ty::Instance<'tcx>, + extra_args: &'tcx ty::List>, + ) -> Self::FnAbiOfResult { + // FIXME(eddyb) get a better `span` here. + let span = self.layout_tcx_at_span(); + let tcx = self.tcx().at(span); + + MaybeResult::from( + tcx.fn_abi_of_instance_no_deduced_attrs( + self.typing_env().as_query_input((instance, extra_args)), + ) + .map_err(|err| { + // HACK(eddyb) at least for definitions of/calls to `Instance`s, + // we can get some kind of span even if one wasn't provided. + // However, we don't do this early in order to avoid calling + // `def_span` unconditionally (which may have a perf penalty). + let span = if !span.is_dummy() { span } else { tcx.def_span(instance.def_id()) }; + self.handle_fn_abi_err( + *err, + span, + FnAbiRequest::OfInstance { instance, extra_args }, + ) + }), + ) + } + + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI will include applicable + /// codegen optimization attributes, including `ReadOnly` and `CapturesNone` -- deduction of + /// which requires inspection of function bodies that can lead to cycles when performed during + /// typeck. During typeck, you should therefore use instead the unoptimized ABI returned by + /// `fn_abi_of_instance_no_deduced_attrs`. + /// + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). #[inline] #[tracing::instrument(level = "debug", skip(self))] fn fn_abi_of_instance( diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 34c080c4938f5..7c3d5eb8890b0 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -21,7 +21,12 @@ use rustc_target::callconv::{ use tracing::debug; pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { fn_abi_of_fn_ptr, fn_abi_of_instance, ..*providers }; + *providers = Providers { + fn_abi_of_fn_ptr, + fn_abi_of_instance_no_deduced_attrs, + fn_abi_of_instance, + ..*providers + }; } // NOTE(eddyb) this is private to avoid using it from outside of @@ -250,25 +255,71 @@ fn fn_abi_of_fn_ptr<'tcx>( let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query; fn_abi_new_uncached( &LayoutCx::new(tcx, typing_env), - tcx.instantiate_bound_regions_with_erased(sig), - extra_args, + tcx.normalize_erasing_regions(typing_env, tcx.instantiate_bound_regions_with_erased(sig)), + None, None, + false, + extra_args, ) } -fn fn_abi_of_instance<'tcx>( +fn instance_params<'tcx>( tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, -) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { +) -> (LayoutCx<'tcx>, ty::FnSig<'tcx>, Option, Option>, bool, &'tcx [Ty<'tcx>]) { let ty::PseudoCanonicalInput { typing_env, value: (instance, extra_args) } = query; + let cx = LayoutCx::new(tcx, typing_env); + let sig = + tcx.normalize_erasing_regions(typing_env, fn_sig_for_fn_abi(tcx, instance, typing_env)); + let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); + let is_tls_shim_call = matches!(instance.def, ty::InstanceKind::ThreadLocalShim(_)); + ( + cx, + sig, + (!is_virtual_call && !is_tls_shim_call).then(|| instance.def_id()), + instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()), + is_virtual_call, + extra_args, + ) +} + +fn fn_abi_of_instance_no_deduced_attrs<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, +) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { + let (cx, sig, determined_fn_def_id, caller_location, is_virtual_call, extra_args) = + instance_params(tcx, query); fn_abi_new_uncached( - &LayoutCx::new(tcx, typing_env), - fn_sig_for_fn_abi(tcx, instance, typing_env), + &cx, + sig, + caller_location, + determined_fn_def_id, + is_virtual_call, extra_args, - Some(instance), ) } +fn fn_abi_of_instance<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, +) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { + tcx.fn_abi_of_instance_no_deduced_attrs(query).map(|fn_abi| { + let (cx, sig, determined_fn_def_id, ..) = instance_params(tcx, query); + determined_fn_def_id.map_or(fn_abi, |determined_fn_def_id| { + fn_abi_adjust_for_deduced_attrs( + &cx, + fn_abi, + sig.abi, + // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other + // functions from vtable. And for a tls shim, passing the `fn_def_id` would refer to + // the underlying static. Internally, `deduced_param_attrs` attempts to infer attributes + // by visit the function body. + determined_fn_def_id, + ) + }) + }) +} + // Handle safe Rust thin and wide pointers. fn arg_attrs_for_rust_scalar<'tcx>( cx: LayoutCx<'tcx>, @@ -479,27 +530,19 @@ fn fn_abi_sanity_check<'tcx>( fn_arg_sanity_check(cx, fn_abi, spec_abi, &fn_abi.ret); } -#[tracing::instrument(level = "debug", skip(cx, instance))] +#[tracing::instrument( + level = "debug", + skip(cx, caller_location, determined_fn_def_id, is_virtual_call) +)] fn fn_abi_new_uncached<'tcx>( cx: &LayoutCx<'tcx>, sig: ty::FnSig<'tcx>, + caller_location: Option>, + determined_fn_def_id: Option, + is_virtual_call: bool, extra_args: &[Ty<'tcx>], - instance: Option>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let tcx = cx.tcx(); - let (caller_location, determined_fn_def_id, is_virtual_call) = if let Some(instance) = instance - { - let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); - let is_tls_shim_call = matches!(instance.def, ty::InstanceKind::ThreadLocalShim(_)); - ( - instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()), - if is_virtual_call || is_tls_shim_call { None } else { Some(instance.def_id()) }, - is_virtual_call, - ) - } else { - (None, None, false) - }; - let sig = tcx.normalize_erasing_regions(cx.typing_env, sig); let abi_map = AbiMap::from_target(&tcx.sess.target); let conv = abi_map.canonize_abi(sig.abi, sig.c_variadic).unwrap(); @@ -575,16 +618,7 @@ fn fn_abi_new_uncached<'tcx>( sig.abi, ), }; - fn_abi_adjust_for_abi( - cx, - &mut fn_abi, - sig.abi, - // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other - // functions from vtable. And for a tls shim, passing the `fn_def_id` would refer to - // the underlying static. Internally, `deduced_param_attrs` attempts to infer attributes - // by visit the function body. - determined_fn_def_id, - ); + fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi); debug!("fn_abi_new_uncached = {:?}", fn_abi); fn_abi_sanity_check(cx, &fn_abi, sig.abi); Ok(tcx.arena.alloc(fn_abi)) @@ -595,7 +629,6 @@ fn fn_abi_adjust_for_abi<'tcx>( cx: &LayoutCx<'tcx>, fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>, abi: ExternAbi, - fn_def_id: Option, ) { if abi == ExternAbi::Unadjusted { // The "unadjusted" ABI passes aggregates in "direct" mode. That's fragile but needed for @@ -616,30 +649,43 @@ fn fn_abi_adjust_for_abi<'tcx>( for arg in fn_abi.args.iter_mut() { unadjust(arg); } - return; + } else if abi.is_rustic_abi() { + fn_abi.adjust_for_rust_abi(cx); + } else { + fn_abi.adjust_for_foreign_abi(cx, abi); } +} +#[tracing::instrument(level = "trace", skip(cx))] +fn fn_abi_adjust_for_deduced_attrs<'tcx>( + cx: &LayoutCx<'tcx>, + fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, + abi: ExternAbi, + fn_def_id: DefId, +) -> &'tcx FnAbi<'tcx, Ty<'tcx>> { let tcx = cx.tcx(); - - if abi.is_rustic_abi() { - fn_abi.adjust_for_rust_abi(cx); - // Look up the deduced parameter attributes for this function, if we have its def ID and - // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes - // as appropriate. - let deduced = - if tcx.sess.opts.optimize != OptLevel::No && tcx.sess.opts.incremental.is_none() { - fn_def_id.map(|fn_def_id| tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default() - } else { - &[] - }; - if !deduced.is_empty() { - apply_deduced_attributes(cx, deduced, 0, &mut fn_abi.ret); - for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() { - apply_deduced_attributes(cx, deduced, arg_idx + 1, arg); - } - } + // Look up the deduced parameter attributes for this function, if we have its def ID and + // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes + // as appropriate. + let deduced = if abi.is_rustic_abi() + && tcx.sess.opts.optimize != OptLevel::No + && tcx.sess.opts.incremental.is_none() + { + tcx.deduced_param_attrs(fn_def_id) } else { - fn_abi.adjust_for_foreign_abi(cx, abi); + &[] + }; + if deduced.is_empty() { + fn_abi + } else { + let mut fn_abi = fn_abi.clone(); + apply_deduced_attributes(cx, deduced, 0, &mut fn_abi.ret); + for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() { + apply_deduced_attributes(cx, deduced, arg_idx + 1, arg); + } + debug!("fn_abi_adjust_for_deduced_attrs = {:?}", fn_abi); + fn_abi_sanity_check(cx, &fn_abi, abi); + tcx.arena.alloc(fn_abi) } } diff --git a/tests/ui/coroutine/avoid-query-cycle-in-ctfe.rs b/tests/ui/coroutine/avoid-query-cycle-in-ctfe.rs new file mode 100644 index 0000000000000..4ae3d97daa448 --- /dev/null +++ b/tests/ui/coroutine/avoid-query-cycle-in-ctfe.rs @@ -0,0 +1,18 @@ +//! Items whose type depends on CTFE (such as the async closure/coroutine beneath, whose type +//! depends upon evaluating `do_nothing`) should not cause a query cycle owing to the deduction of +//! the function's parameter attributes, which are only required for codegen and not for CTFE. +//! +//! Regression test for https://github.com/rust-lang/rust/issues/151748 +//@ compile-flags: -O +//@ edition: 2018 +//@ check-pass + +fn main() { + let _ = async || { + let COMPLEX_CONSTANT = (); + }; +} + +const fn do_nothing() {} + +const COMPLEX_CONSTANT: () = do_nothing(); From b5f82052ce3953e883fb303d2095c97100b9d3f9 Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Thu, 12 Feb 2026 12:44:12 +0000 Subject: [PATCH 2/2] Only invoke `fn_abi_of_instance_no_deduced_attrs` query in opt builds As a performance optimization, we altogether avoid ever invoking the `fn_abi_of_instance_no_deduced_attrs` query in cases where the build will not deduce parameter attributes. --- compiler/rustc_middle/src/ty/layout.rs | 6 ++++ compiler/rustc_middle/src/ty/mod.rs | 5 +++ compiler/rustc_ty_utils/src/abi.rs | 44 +++++++++++++------------- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index ac76199feff53..084dedf923561 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1386,6 +1386,12 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> { instance: ty::Instance<'tcx>, extra_args: &'tcx ty::List>, ) -> Self::FnAbiOfResult { + // Performance optimization. If this build won't deduce parameter attributes, then we can + // avoid ever calling the `fn_abi_of_instance_no_deduced_attrs` query by delegating here to + // `fn_abi_of_instance` instead. + if !self.tcx().will_deduce_param_attrs() { + return self.fn_abi_of_instance(instance, extra_args); + } // FIXME(eddyb) get a better `span` here. let span = self.layout_tcx_at_span(); let tcx = self.tcx().at(span); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 37871aad4a775..fa52d80c0bb2b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -49,6 +49,7 @@ use rustc_macros::{ }; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; +use rustc_session::config::OptLevel; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol}; @@ -2163,6 +2164,10 @@ impl<'tcx> TyCtxt<'tcx> { !self.associated_types_for_impl_traits_in_associated_fn(trait_item_def_id).is_empty() } + + pub fn will_deduce_param_attrs(self) -> bool { + self.sess.opts.optimize != OptLevel::No && self.sess.opts.incremental.is_none() + } } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 7c3d5eb8890b0..844cfeaffe6bf 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -12,7 +12,6 @@ use rustc_middle::ty::layout::{ FnAbiError, HasTyCtxt, HasTypingEnv, LayoutCx, LayoutOf, TyAndLayout, fn_can_unwind, }; use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt}; -use rustc_session::config::OptLevel; use rustc_span::DUMMY_SP; use rustc_span::def_id::DefId; use rustc_target::callconv::{ @@ -303,21 +302,29 @@ fn fn_abi_of_instance<'tcx>( tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { - tcx.fn_abi_of_instance_no_deduced_attrs(query).map(|fn_abi| { + if tcx.will_deduce_param_attrs() { let (cx, sig, determined_fn_def_id, ..) = instance_params(tcx, query); - determined_fn_def_id.map_or(fn_abi, |determined_fn_def_id| { - fn_abi_adjust_for_deduced_attrs( - &cx, - fn_abi, - sig.abi, - // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other - // functions from vtable. And for a tls shim, passing the `fn_def_id` would refer to - // the underlying static. Internally, `deduced_param_attrs` attempts to infer attributes - // by visit the function body. - determined_fn_def_id, - ) + // The `fn_abi_of_instance_no_deduced_attrs` query may have been called during CTFE, so we + // delegate to it here in order to reuse (and, if necessary, augment) its result. + tcx.fn_abi_of_instance_no_deduced_attrs(query).map(|fn_abi| { + determined_fn_def_id.map_or(fn_abi, |determined_fn_def_id| { + fn_abi_adjust_for_deduced_attrs( + &cx, + fn_abi, + sig.abi, + // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other + // functions from vtable. And for a tls shim, passing the `fn_def_id` would refer to + // the underlying static. Internally, `deduced_param_attrs` attempts to infer attributes + // by visit the function body. + determined_fn_def_id, + ) + }) }) - }) + } else { + // No need ever to invoke the `fn_abi_of_instance_no_deduced_attrs` query, so we delegate + // here instead to its underlying function in order to avoid the query system overhead. + fn_abi_of_instance_no_deduced_attrs(tcx, query) + } } // Handle safe Rust thin and wide pointers. @@ -667,14 +674,7 @@ fn fn_abi_adjust_for_deduced_attrs<'tcx>( // Look up the deduced parameter attributes for this function, if we have its def ID and // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes // as appropriate. - let deduced = if abi.is_rustic_abi() - && tcx.sess.opts.optimize != OptLevel::No - && tcx.sess.opts.incremental.is_none() - { - tcx.deduced_param_attrs(fn_def_id) - } else { - &[] - }; + let deduced = if abi.is_rustic_abi() { tcx.deduced_param_attrs(fn_def_id) } else { &[] }; if deduced.is_empty() { fn_abi } else {