Skip to content

Commit 1077d1e

Browse files
committed
Avoid generating by-move coroutine MIR during parameter deduction
The `mir_keys` query generates additional by-move MIR bodies (that aren't in the HIR) for coroutine-closures. However, computing such bodies (indeed, merely determining whether such a body needs to be generated) before typeck is complete can lead to cycles. In order to compute a function's ABI (which happens amongst other things during CTFE), it may be necessary to look at the function's body in order to deduce certain codegen optimization attributes for its indirectly-passed parameters beyond what can be determined purely from its signature (namely today `ArgAttribute::ReadOnly` and `ArgAttribute::CapturesNone`). Prior to this patch, such parameter deduction is bypassed for functions that have no body (eg in trait or extern declarations) by calling upon the `is_mir_available` query, which in turn calls upon `mir_keys`, and cycles can ensue. This patch breaks such cycles by using instead a new query, `is_mir_available_for_hir`, which itself calls another new query `mir_keys_for_hir` that only considers MIR bodies for items that exist in the HIR (ie without generating the additional by-move MIR bodies for coroutine closures). Clearly theis cannot change overall behaviour, as `is_mir_available` could never have been called with the `DefId` of an as-yet not generated item. In order to minimize duplicated effort, the existing `mir_keys` query reuses the result from the new `mir_keys_for_hir` query, modifying it only if there are coroutine-closures for which by-move MIR bodies need be generated.
1 parent 55bfca7 commit 1077d1e

6 files changed

Lines changed: 84 additions & 22 deletions

File tree

compiler/rustc_data_structures/src/stable_hasher.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::borrow::Cow;
12
use std::hash::{BuildHasher, Hash, Hasher};
23
use std::marker::PhantomData;
34
use std::mem;
@@ -501,6 +502,16 @@ where
501502
}
502503
}
503504

505+
impl<'a, T, CTX> HashStable<CTX> for Cow<'a, T>
506+
where
507+
T: HashStable<CTX> + ?Sized + ToOwned,
508+
{
509+
#[inline]
510+
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
511+
(**self).hash_stable(ctx, hasher);
512+
}
513+
}
514+
504515
impl<T, CTX> HashStable<CTX> for ::std::mem::Discriminant<T> {
505516
#[inline]
506517
fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,7 +1877,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
18771877
&& tcx.sess.opts.optimize != OptLevel::No
18781878
&& tcx.sess.opts.incremental.is_none()
18791879
{
1880-
for &local_def_id in tcx.mir_keys(()) {
1880+
for &local_def_id in &**tcx.mir_keys(()) {
18811881
if let DefKind::AssocFn | DefKind::Fn = tcx.def_kind(local_def_id) {
18821882
record_array!(self.tables.deduced_param_attrs[local_def_id.to_def_id()] <-
18831883
self.tcx.deduced_param_attrs(local_def_id.to_def_id()));
@@ -2284,7 +2284,7 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
22842284
}
22852285

22862286
let reachable_set = tcx.reachable_set(());
2287-
par_for_each_in(tcx.mir_keys(()), |&&def_id| {
2287+
par_for_each_in(&**tcx.mir_keys(()), |&&def_id| {
22882288
if tcx.is_trivial_const(def_id) {
22892289
return;
22902290
}

compiler/rustc_middle/src/queries.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
6363
#![allow(unused_parens)]
6464

65+
use std::borrow::Cow;
6566
use std::ffi::OsStr;
6667
use std::mem;
6768
use std::path::PathBuf;
@@ -623,10 +624,18 @@ rustc_queries! {
623624
desc { |tcx| "building THIR for `{}`", tcx.def_path_str(key) }
624625
}
625626

626-
/// Set of all the `DefId`s in this crate that have MIR associated with
627-
/// them. This includes all the body owners, but also things like struct
628-
/// constructors.
629-
query mir_keys(_: ()) -> &'tcx rustc_data_structures::fx::FxIndexSet<LocalDefId> {
627+
/// Set of all the `DefId`s in this crate that have MIR associated with them owing to their
628+
/// presence in the HIR. This includes all the body owners, but also things like struct
629+
/// constructors. Unlike `mir_keys`, this query will not include the generated by-move MIR
630+
/// bodies of coroutine closures.
631+
query mir_keys_for_hir(_: ()) -> &'tcx rustc_data_structures::fx::FxIndexSet<LocalDefId> {
632+
arena_cache
633+
desc { "getting a list of all mir_keys for hir items" }
634+
}
635+
636+
/// Set of all the `DefId`s in this crate that have MIR associated with them. This includes all
637+
/// the body owners, but also things like struct constructors.
638+
query mir_keys(_: ()) -> &'tcx Cow<'tcx, rustc_data_structures::fx::FxIndexSet<LocalDefId>> {
630639
arena_cache
631640
desc { "getting a list of all mir_keys" }
632641
}
@@ -1588,6 +1597,17 @@ rustc_queries! {
15881597
separate_provide_extern
15891598
}
15901599

1600+
/// Answers whether the HIR item with the given `key` has MIR available. Unlike
1601+
/// `is_mir_available`, this query will not answer `true` for the generated by-move MIR bodies
1602+
/// of coroutine closures.
1603+
query is_mir_available_for_hir(key: DefId) -> bool {
1604+
desc { |tcx| "checking if HIR item has MIR available: `{}`", tcx.def_path_str(key) }
1605+
cache_on_disk_if { key.is_local() }
1606+
separate_provide_extern
1607+
}
1608+
1609+
/// Answers whether MIR is available for the given `key`. Unlike `is_mir_available_for_hir`,
1610+
/// this query also answers `true` for the generated by-move MIR bodies of coroutine closures.
15911611
query is_mir_available(key: DefId) -> bool {
15921612
desc { |tcx| "checking if item has MIR available: `{}`", tcx.def_path_str(key) }
15931613
cache_on_disk_if { key.is_local() }

compiler/rustc_mir_transform/src/deduce_param_attrs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ pub(super) fn deduced_param_attrs<'tcx>(
189189
}
190190

191191
// Don't deduce any attributes for functions that have no MIR.
192-
if !tcx.is_mir_available(def_id) {
192+
if !tcx.is_mir_available_for_hir(def_id) {
193193
return &[];
194194
}
195195

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use tracing::debug;
3535
#[macro_use]
3636
mod pass_manager;
3737

38+
use std::borrow::Cow;
3839
use std::sync::LazyLock;
3940

4041
use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel};
@@ -219,6 +220,7 @@ pub fn provide(providers: &mut Providers) {
219220
shim::provide(&mut providers.queries);
220221
cross_crate_inline::provide(&mut providers.queries);
221222
providers.queries = query::Providers {
223+
mir_keys_for_hir,
222224
mir_keys,
223225
mir_built,
224226
mir_const_qualif,
@@ -228,6 +230,7 @@ pub fn provide(providers: &mut Providers) {
228230
mir_coroutine_witnesses: coroutine::mir_coroutine_witnesses,
229231
optimized_mir,
230232
check_liveness: liveness::check_liveness,
233+
is_mir_available_for_hir,
231234
is_mir_available,
232235
mir_callgraph_cyclic: inline::cycle::mir_callgraph_cyclic,
233236
mir_inliner_callees: inline::cycle::mir_inliner_callees,
@@ -316,30 +319,18 @@ fn take_array<T, const N: usize>(b: &mut Box<[T]>) -> Result<[T; N], Box<[T]>> {
316319
Ok(*b)
317320
}
318321

319-
fn is_mir_available(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
320-
tcx.mir_keys(()).contains(&def_id)
322+
fn is_mir_available_for_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
323+
tcx.mir_keys_for_hir(()).contains(&def_id)
321324
}
322325

323-
/// Finds the full set of `DefId`s within the current crate that have
324-
/// MIR associated with them.
325-
fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> {
326+
fn mir_keys_for_hir(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> {
326327
// All body-owners have MIR associated with them.
327328
let mut set: FxIndexSet<_> = tcx.hir_body_owners().collect();
328329

329330
// Remove the fake bodies for `global_asm!`, since they're not useful
330331
// to be emitted (`--emit=mir`) or encoded (in metadata).
331332
set.retain(|&def_id| !matches!(tcx.def_kind(def_id), DefKind::GlobalAsm));
332333

333-
// Coroutine-closures (e.g. async closures) have an additional by-move MIR
334-
// body that isn't in the HIR.
335-
for body_owner in tcx.hir_body_owners() {
336-
if let DefKind::Closure = tcx.def_kind(body_owner)
337-
&& tcx.needs_coroutine_by_move_body_def_id(body_owner.to_def_id())
338-
{
339-
set.insert(tcx.coroutine_by_move_body_def_id(body_owner).expect_local());
340-
}
341-
}
342-
343334
// tuple struct/variant constructors have MIR, but they don't have a BodyId,
344335
// so we need to build them separately.
345336
for item in tcx.hir_crate_items(()).free_items() {
@@ -355,6 +346,28 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> {
355346
set
356347
}
357348

349+
fn is_mir_available(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
350+
tcx.mir_keys(()).contains(&def_id)
351+
}
352+
353+
/// Finds the full set of `DefId`s within the current crate that have
354+
/// MIR associated with them.
355+
fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> Cow<'_, FxIndexSet<LocalDefId>> {
356+
let mut set = Cow::Borrowed(tcx.mir_keys_for_hir(()));
357+
358+
// Coroutine-closures (e.g. async closures) have an additional by-move MIR
359+
// body that isn't in the HIR.
360+
for body_owner in tcx.hir_body_owners() {
361+
if let DefKind::Closure = tcx.def_kind(body_owner)
362+
&& tcx.needs_coroutine_by_move_body_def_id(body_owner.to_def_id())
363+
{
364+
set.to_mut().insert(tcx.coroutine_by_move_body_def_id(body_owner).expect_local());
365+
}
366+
}
367+
368+
set
369+
}
370+
358371
fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
359372
// N.B., this `borrow()` is guaranteed to be valid (i.e., the value
360373
// cannot yet be stolen), because `mir_promoted()`, which steals
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//! Items whose type depends on CTFE (such as the async closure/coroutine beneath, whose type
2+
//! depends upon evaluating `do_nothing`) should not cause a query cycle owing to the deduction of
3+
//! the function's parameter attributes, which are only required for codegen and not for CTFE.
4+
//!
5+
//! Regression test for https://github.com/rust-lang/rust/issues/151748
6+
//@ compile-flags: -O
7+
//@ edition: 2018
8+
//@ check-pass
9+
10+
fn main() {
11+
let _ = async || {
12+
let COMPLEX_CONSTANT = ();
13+
};
14+
}
15+
16+
const fn do_nothing() {}
17+
18+
const COMPLEX_CONSTANT: () = do_nothing();

0 commit comments

Comments
 (0)