pub async fn implementation coroutine (func::{closure#0}) is monomorphized, when func itself is monomorphized

This commit is contained in:
Andrew Zhogin 2025-07-01 23:54:52 +07:00
parent 922958cffe
commit c2c58cbc65
13 changed files with 245 additions and 15 deletions

View file

@ -1535,7 +1535,20 @@ impl<'v> RootCollector<'_, 'v> {
fn process_nested_body(&mut self, def_id: LocalDefId) {
match self.tcx.def_kind(def_id) {
DefKind::Closure => {
if self.strategy == MonoItemCollectionStrategy::Eager
// for 'pub async fn foo(..)' also trying to monomorphize foo::{closure}
let is_pub_fn_coroutine =
match *self.tcx.type_of(def_id).instantiate_identity().kind() {
ty::Coroutine(cor_id, _args) => {
let tcx = self.tcx;
let parent_id = tcx.parent(cor_id);
tcx.def_kind(parent_id) == DefKind::Fn
&& tcx.asyncness(parent_id).is_async()
&& tcx.visibility(parent_id).is_public()
}
ty::Closure(..) | ty::CoroutineClosure(..) => false,
_ => unreachable!(),
};
if (self.strategy == MonoItemCollectionStrategy::Eager || is_pub_fn_coroutine)
&& !self
.tcx
.generics_of(self.tcx.typeck_root_def_id(def_id.to_def_id()))

View file

@ -18,11 +18,11 @@ pub async fn async_fn_test() {
pub async fn foo() {}
// NONMSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
// MSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
// NONMSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[GEN_SCOPE:![0-9]*]],
// MSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::async_fn_test::async_fn_env$0>",
// NONMSVC: [[GEN_SCOPE:!.*]] = !DINamespace(name: "async_fn_test",
// CHECK: [[SUSPEND_STRUCT:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend0", scope: [[GEN]],
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE:![0-9]*]],
// NONMSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
// MSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE]],

View file

@ -0,0 +1,10 @@
//@ edition: 2024
// When pub async fn is monomorphized, its implementation coroutine is also monomorphized
//@ compile-flags: --crate-type=lib
//~ MONO_ITEM fn async_fn @@
//~ MONO_ITEM fn async_fn::{closure#0} @@
#[unsafe(no_mangle)]
pub async fn async_fn(x: u64) -> bool {
true
}

View file

@ -0,0 +1,89 @@
//@ only-x86_64-unknown-linux-gnu
//@ compile-flags: -C panic=abort -Zinline-mir=no -Copt-level=0 -Zcross-crate-inline-threshold=never -Zmir-opt-level=0 -Cno-prepopulate-passes
//@ no-prefer-dynamic
//@ edition:2024
#![crate_type = "lib"]
trait TestTrait {
fn test_func(&self);
}
struct TestStruct {}
impl TestTrait for TestStruct {
fn test_func(&self) {
println!("TestStruct::test_func");
}
}
#[inline(never)]
pub fn foo() -> impl TestTrait {
TestStruct {}
}
//~ MONO_ITEM fn foo
//~ MONO_ITEM fn <TestStruct as TestTrait>::test_func
trait TestTrait2 {
fn test_func2(&self);
}
struct TestStruct2 {}
impl TestTrait2 for TestStruct2 {
fn test_func2(&self) {
println!("TestStruct2::test_func2");
}
}
#[inline(never)]
pub fn foo2() -> Box<dyn TestTrait2> {
Box::new(TestStruct2 {})
}
//~ MONO_ITEM fn <TestStruct2 as TestTrait2>::test_func2
//~ MONO_ITEM fn alloc::alloc::exchange_malloc
//~ MONO_ITEM fn foo2
//~ MONO_ITEM fn std::alloc::Global::alloc_impl
//~ MONO_ITEM fn std::boxed::Box::<TestStruct2>::new
//~ MONO_ITEM fn std::alloc::Layout::from_size_align_unchecked::precondition_check
//~ MONO_ITEM fn std::ptr::NonNull::<T>::new_unchecked::precondition_check
struct Counter {
count: usize,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
impl Iterator for Counter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 { Some(self.count) } else { None }
}
}
#[inline(never)]
pub fn foo3() -> Box<dyn Iterator<Item = usize>> {
Box::new(Counter::new())
}
//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by
//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::advance_by
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::next
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::nth
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::size_hint
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::try_fold::<std::num::NonZero<usize>, {closure@<Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}}, std::option::Option<std::num::NonZero<usize>>>
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::FromResidual<std::option::Option<std::convert::Infallible>>>::from_residual
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::branch
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::from_output
//~ MONO_ITEM fn foo3
//~ MONO_ITEM fn std::boxed::Box::<Counter>::new
//~ MONO_ITEM fn Counter::new
//~ MONO_ITEM fn core::fmt::rt::<impl std::fmt::Arguments<'_>>::new_const::<1>

View file

@ -103,21 +103,21 @@ Number of file 0 mappings: 3
Highest counter ID seen: (none)
Function name: async::g
Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 16]
Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 12]
Number of files: 1
- file 0 => $DIR/async.rs
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 22)
- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 18)
Highest counter ID seen: c0
Function name: async::g::{closure#0} (unused)
Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 17, 00, 18, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 13, 00, 14, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
Number of files: 1
- file 0 => $DIR/async.rs
Number of expressions: 0
Number of file 0 mappings: 12
- Code(Zero) at (prev + 27, 23) to (start + 0, 24)
- Code(Zero) at (prev + 27, 19) to (start + 0, 20)
- Code(Zero) at (prev + 1, 11) to (start + 0, 12)
- Code(Zero) at (prev + 1, 9) to (start + 0, 10)
- Code(Zero) at (prev + 0, 14) to (start + 0, 23)

View file

@ -24,8 +24,8 @@
LL| |
LL| 0|async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()`
LL| |
LL| 1|pub async fn g(x: u8) {
^0
LL| 1|async fn g(x: u8) {
^0
LL| 0| match x {
LL| 0| y if e().await == y => (),
LL| 0| y if f().await == y => (),

View file

@ -24,7 +24,7 @@ async fn f() -> u8 { 1 }
async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()`
pub async fn g(x: u8) {
async fn g(x: u8) {
match x {
y if e().await == y => (),
y if f().await == y => (),

View file

@ -1,7 +1,9 @@
//@ compile-flags: -Z print-type-sizes --crate-type lib
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib
//@ needs-deterministic-layouts
//@ edition:2021
//@ build-pass
//@ ignore-pass
//@ only-x86_64
async fn wait() {}

View file

@ -48,6 +48,39 @@ print-type-size variant `Returned`: 1024 bytes
print-type-size upvar `.arg`: 1024 bytes
print-type-size variant `Panicked`: 1024 bytes
print-type-size upvar `.arg`: 1024 bytes
print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
print-type-size field `.waker`: 8 bytes
print-type-size field `.local_waker`: 8 bytes
print-type-size field `.ext`: 16 bytes
print-type-size field `._marker`: 0 bytes
print-type-size field `._marker2`: 0 bytes
print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
print-type-size field `.filename`: 16 bytes
print-type-size field `.line`: 4 bytes
print-type-size field `.col`: 4 bytes
print-type-size field `._filename`: 0 bytes
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
print-type-size variant `Some`: 16 bytes
print-type-size field `.0`: 16 bytes
print-type-size variant `None`: 0 bytes
print-type-size field `.0`: 0 bytes
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
print-type-size field `.0`: 16 bytes
print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 16 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of big_fut()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
print-type-size field `._vtable_ptr`: 8 bytes
print-type-size field `._phantom`: 0 bytes
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::mem::ManuallyDrop<bool>`: 1 bytes, alignment: 1 bytes
print-type-size field `.value`: 1 bytes
print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
@ -70,3 +103,7 @@ print-type-size discriminant: 1 bytes
print-type-size variant `Unresumed`: 0 bytes
print-type-size variant `Returned`: 0 bytes
print-type-size variant `Panicked`: 0 bytes
print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes

View file

@ -1,7 +1,9 @@
//@ compile-flags: -Z print-type-sizes --crate-type=lib
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type=lib
//@ needs-deterministic-layouts
//@ edition: 2021
//@ build-pass
//@ ignore-pass
//@ only-x86_64
pub async fn test() {
let _ = a([0u8; 1024]).await;

View file

@ -58,3 +58,45 @@ print-type-size variant `Returned`: 1024 bytes
print-type-size upvar `.t`: 1024 bytes
print-type-size variant `Panicked`: 1024 bytes
print-type-size upvar `.t`: 1024 bytes
print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
print-type-size field `.waker`: 8 bytes
print-type-size field `.local_waker`: 8 bytes
print-type-size field `.ext`: 16 bytes
print-type-size field `._marker`: 0 bytes
print-type-size field `._marker2`: 0 bytes
print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
print-type-size field `.filename`: 16 bytes
print-type-size field `.line`: 4 bytes
print-type-size field `.col`: 4 bytes
print-type-size field `._filename`: 0 bytes
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
print-type-size variant `Some`: 16 bytes
print-type-size field `.0`: 16 bytes
print-type-size variant `None`: 0 bytes
print-type-size field `.0`: 0 bytes
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
print-type-size field `.0`: 16 bytes
print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 16 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of a<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of b<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of c<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
print-type-size field `._vtable_ptr`: 8 bytes
print-type-size field `._phantom`: 0 bytes
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes
print-type-size discriminant: 1 bytes
print-type-size variant `Ready`: 0 bytes
print-type-size field `.0`: 0 bytes
print-type-size variant `Pending`: 0 bytes
print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes

View file

@ -1,7 +1,9 @@
//@ compile-flags: -Z print-type-sizes --crate-type lib
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib
//@ needs-deterministic-layouts
//@ edition:2021
//@ build-pass
//@ ignore-pass
//@ only-x86_64
#![allow(dropping_copy_types)]

View file

@ -16,6 +16,35 @@ print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment
print-type-size variant `MaybeUninit`: 8192 bytes
print-type-size field `.uninit`: 0 bytes
print-type-size field `.value`: 8192 bytes
print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
print-type-size field `.waker`: 8 bytes
print-type-size field `.local_waker`: 8 bytes
print-type-size field `.ext`: 16 bytes
print-type-size field `._marker`: 0 bytes
print-type-size field `._marker2`: 0 bytes
print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
print-type-size field `.filename`: 16 bytes
print-type-size field `.line`: 4 bytes
print-type-size field `.col`: 4 bytes
print-type-size field `._filename`: 0 bytes
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
print-type-size variant `Some`: 16 bytes
print-type-size field `.0`: 16 bytes
print-type-size variant `None`: 0 bytes
print-type-size field `.0`: 0 bytes
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
print-type-size field `.0`: 16 bytes
print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 16 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
print-type-size field `._vtable_ptr`: 8 bytes
print-type-size field `._phantom`: 0 bytes
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
print-type-size field `.value`: 1 bytes
print-type-size type: `std::mem::MaybeUninit<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
@ -32,3 +61,7 @@ print-type-size discriminant: 1 bytes
print-type-size variant `Unresumed`: 0 bytes
print-type-size variant `Returned`: 0 bytes
print-type-size variant `Panicked`: 0 bytes
print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes