Rollup merge of #130201 - compiler-errors:foreign-synthetic-body, r=lcnr

Encode `coroutine_by_move_body_def_id` in crate metadata

We synthesize the MIR for a by-move body for the `FnOnce` implementation of async closures. It can be accessed with the `coroutine_by_move_body_def_id` query. We weren't encoding this query in the metadata though, nor were we properly recording that synthetic MIR in `mir_keys`, so the `optimized_mir` wasn't getting encoded either!

Stacked on top is a fix to consider `DefKind::SyntheticCoroutineBody` to return true in several places I missed. Specifically, we should consider the def-kind in `fn DefKind::is_fn_like()`, since that's what we were using to make sure we ensure `query mir_inliner_callees` before the MIR gets stolen for the body. This led to some CI failures that were caught by miri but which I added a test for.
This commit is contained in:
Matthias Krüger 2024-09-17 17:28:32 +02:00 committed by GitHub
commit ddcb9c132a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 82 additions and 21 deletions

View File

@ -287,7 +287,10 @@ pub fn def_path_data(self, name: Symbol) -> DefPathData {
#[inline] #[inline]
pub fn is_fn_like(self) -> bool { pub fn is_fn_like(self) -> bool {
matches!(self, DefKind::Fn | DefKind::AssocFn | DefKind::Closure) matches!(
self,
DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::SyntheticCoroutineBody
)
} }
/// Whether `query get_codegen_attrs` should be used with this definition. /// Whether `query get_codegen_attrs` should be used with this definition.

View File

@ -290,6 +290,7 @@ fn into_args(self) -> (DefId, SimplifiedType) {
fn_arg_names => { table } fn_arg_names => { table }
coroutine_kind => { table_direct } coroutine_kind => { table_direct }
coroutine_for_closure => { table } coroutine_for_closure => { table }
coroutine_by_move_body_def_id => { table }
eval_static_initializer => { eval_static_initializer => {
Ok(cdata Ok(cdata
.root .root

View File

@ -1488,9 +1488,18 @@ fn encode_def_ids(&mut self) {
if def_kind == DefKind::Closure if def_kind == DefKind::Closure
&& tcx.type_of(def_id).skip_binder().is_coroutine_closure() && tcx.type_of(def_id).skip_binder().is_coroutine_closure()
{ {
let coroutine_for_closure = self.tcx.coroutine_for_closure(def_id);
self.tables self.tables
.coroutine_for_closure .coroutine_for_closure
.set_some(def_id.index, self.tcx.coroutine_for_closure(def_id).into()); .set_some(def_id.index, coroutine_for_closure.into());
// If this async closure has a by-move body, record it too.
if tcx.needs_coroutine_by_move_body_def_id(coroutine_for_closure) {
self.tables.coroutine_by_move_body_def_id.set_some(
coroutine_for_closure.index,
self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure).into(),
);
}
} }
if let DefKind::Static { .. } = def_kind { if let DefKind::Static { .. } = def_kind {
if !self.tcx.is_foreign_item(def_id) { if !self.tcx.is_foreign_item(def_id) {

View File

@ -446,6 +446,7 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
fn_arg_names: Table<DefIndex, LazyArray<Ident>>, fn_arg_names: Table<DefIndex, LazyArray<Ident>>,
coroutine_kind: Table<DefIndex, hir::CoroutineKind>, coroutine_kind: Table<DefIndex, hir::CoroutineKind>,
coroutine_for_closure: Table<DefIndex, RawDefId>, coroutine_for_closure: Table<DefIndex, RawDefId>,
coroutine_by_move_body_def_id: Table<DefIndex, RawDefId>,
eval_static_initializer: Table<DefIndex, LazyValue<mir::interpret::ConstAllocation<'static>>>, eval_static_initializer: Table<DefIndex, LazyValue<mir::interpret::ConstAllocation<'static>>>,
trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>, trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
trait_item_def_id: Table<DefIndex, RawDefId>, trait_item_def_id: Table<DefIndex, RawDefId>,

View File

@ -24,7 +24,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
// This just reproduces the logic from Instance::requires_inline. // This just reproduces the logic from Instance::requires_inline.
match tcx.def_kind(def_id) { match tcx.def_kind(def_id) {
DefKind::Ctor(..) | DefKind::Closure => return true, DefKind::Ctor(..) | DefKind::Closure | DefKind::SyntheticCoroutineBody => return true,
DefKind::Fn | DefKind::AssocFn => {} DefKind::Fn | DefKind::AssocFn => {}
_ => return false, _ => return false,
} }

View File

@ -21,9 +21,8 @@
use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::steal::Steal; use rustc_data_structures::steal::Steal;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::DefKind; use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::mir::{ use rustc_middle::mir::{
AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl, AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl,
@ -224,26 +223,31 @@ fn is_mir_available(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
/// MIR associated with them. /// MIR associated with them.
fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> { fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> {
// All body-owners have MIR associated with them. // All body-owners have MIR associated with them.
let set: FxIndexSet<_> = tcx.hir().body_owners().collect(); let mut set: FxIndexSet<_> = tcx.hir().body_owners().collect();
// Additionally, tuple struct/variant constructors have MIR, but // Coroutine-closures (e.g. async closures) have an additional by-move MIR
// they don't have a BodyId, so we need to build them separately. // body that isn't in the HIR.
struct GatherCtors { for body_owner in tcx.hir().body_owners() {
set: FxIndexSet<LocalDefId>, if let DefKind::Closure = tcx.def_kind(body_owner)
} && tcx.needs_coroutine_by_move_body_def_id(body_owner.to_def_id())
impl<'tcx> Visitor<'tcx> for GatherCtors { {
fn visit_variant_data(&mut self, v: &'tcx hir::VariantData<'tcx>) { set.insert(tcx.coroutine_by_move_body_def_id(body_owner).expect_local());
if let hir::VariantData::Tuple(_, _, def_id) = *v {
self.set.insert(def_id);
}
intravisit::walk_struct_def(self, v)
} }
} }
let mut gather_ctors = GatherCtors { set }; // tuple struct/variant constructors have MIR, but they don't have a BodyId,
tcx.hir().visit_all_item_likes_in_crate(&mut gather_ctors); // so we need to build them separately.
for item in tcx.hir_crate_items(()).free_items() {
if let DefKind::Struct | DefKind::Enum = tcx.def_kind(item.owner_id) {
for variant in tcx.adt_def(item.owner_id).variants() {
if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor {
set.insert(ctor_def_id.expect_local());
}
}
}
}
gather_ctors.set set
} }
fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {

View File

@ -227,7 +227,11 @@ fn compute_symbol_name<'tcx>(
// and we want to be sure to avoid any symbol conflicts here. // and we want to be sure to avoid any symbol conflicts here.
let is_globally_shared_function = matches!( let is_globally_shared_function = matches!(
tcx.def_kind(instance.def_id()), tcx.def_kind(instance.def_id()),
DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Ctor(..) DefKind::Fn
| DefKind::AssocFn
| DefKind::Closure
| DefKind::SyntheticCoroutineBody
| DefKind::Ctor(..)
) && matches!( ) && matches!(
MonoItem::Fn(instance).instantiation_mode(tcx), MonoItem::Fn(instance).instantiation_mode(tcx),
InstantiationMode::GloballyShared { may_conflict: true } InstantiationMode::GloballyShared { may_conflict: true }

View File

@ -12,8 +12,13 @@
struct NoCopy; struct NoCopy;
async fn call_once(f: impl async FnOnce()) {
f().await;
}
fn main() { fn main() {
block_on::block_on(async { block_on::block_on(async {
foreign::closure()().await; foreign::closure()().await;
call_once(foreign::closure()).await;
}); });
} }

View File

@ -0,0 +1,34 @@
//@ edition: 2021
//@ compile-flags: -Zinline-mir
//@ build-pass
// Ensure that we don't hit a Steal ICE because we forgot to ensure
// `mir_inliner_callees` for the synthetic by-move coroutine body since
// its def-id wasn't previously being considered.
#![feature(async_closure, noop_waker)]
use std::future::Future;
use std::pin::pin;
use std::task::*;
pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
let mut fut = pin!(fut);
let ctx = &mut Context::from_waker(Waker::noop());
loop {
match fut.as_mut().poll(ctx) {
Poll::Pending => {}
Poll::Ready(t) => break t,
}
}
}
async fn call_once<T>(f: impl async FnOnce() -> T) -> T {
f().await
}
fn main() {
let c = async || {};
block_on(call_once(c));
}