From 78a6b58a42d30bb8393b2d5773ca466c438afac5 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Tue, 26 May 2026 09:57:05 -0400 Subject: [PATCH 1/2] add red test --- tests/coverage/async_closure2.cov-map | 66 ++++++++++++++++++++++++++ tests/coverage/async_closure2.coverage | 33 +++++++++++++ tests/coverage/async_closure2.rs | 23 +++++++++ 3 files changed, 122 insertions(+) create mode 100644 tests/coverage/async_closure2.cov-map create mode 100644 tests/coverage/async_closure2.coverage create mode 100644 tests/coverage/async_closure2.rs diff --git a/tests/coverage/async_closure2.cov-map b/tests/coverage/async_closure2.cov-map new file mode 100644 index 000000000000..70e0a0f37116 --- /dev/null +++ b/tests/coverage/async_closure2.cov-map @@ -0,0 +1,66 @@ +Function name: async_closure2::call_once:: +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 01, 00, 2a] +Number of files: 1 +- file 0 => $DIR/async_closure2.rs +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 12, 1) to (start + 0, 42) +Highest counter ID seen: c0 + +Function name: async_closure2::call_once::::{closure#0} +Raw bytes (21): 0x[01, 01, 01, 05, 09, 03, 01, 0c, 2b, 00, 2c, 01, 01, 05, 00, 0e, 02, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/async_closure2.rs +Number of expressions: 1 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 12, 43) to (start + 0, 44) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 14) +- Code(Expression(0, Sub)) at (prev + 1, 1) to (start + 0, 2) + = (c1 - c2) +Highest counter ID seen: c0 + +Function name: async_closure2::main +Raw bytes (54): 0x[01, 01, 00, 0a, 01, 10, 01, 00, 0e, 01, 01, 09, 00, 16, 01, 04, 05, 00, 17, 01, 00, 18, 00, 21, 01, 00, 22, 00, 2f, 01, 01, 05, 00, 0f, 01, 00, 10, 00, 15, 01, 00, 16, 00, 1a, 01, 00, 1b, 00, 2b, 05, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/async_closure2.rs +Number of expressions: 0 +Number of file 0 mappings: 10 +- Code(Counter(0)) at (prev + 16, 1) to (start + 0, 14) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 22) +- Code(Counter(0)) at (prev + 4, 5) to (start + 0, 23) +- Code(Counter(0)) at (prev + 0, 24) to (start + 0, 33) +- Code(Counter(0)) at (prev + 0, 34) to (start + 0, 47) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15) +- Code(Counter(0)) at (prev + 0, 16) to (start + 0, 21) +- Code(Counter(0)) at (prev + 0, 22) to (start + 0, 26) +- Code(Counter(0)) at (prev + 0, 27) to (start + 0, 43) +- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c1 + +Function name: async_closure2::main::{closure#0} +Raw bytes (14): 0x[01, 01, 00, 02, 01, 11, 22, 00, 23, 01, 03, 05, 00, 06] +Number of files: 1 +- file 0 => $DIR/async_closure2.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 17, 34) to (start + 0, 35) +- Code(Counter(0)) at (prev + 3, 5) to (start + 0, 6) +Highest counter ID seen: c0 + +Function name: async_closure2::main::{closure#0}::{closure#0}::<_> (unused) +Raw bytes (44): 0x[01, 01, 00, 08, 00, 11, 22, 00, 23, 00, 01, 09, 00, 0e, 00, 00, 0f, 00, 18, 00, 00, 1c, 00, 2c, 00, 01, 09, 00, 0e, 00, 00, 0f, 00, 18, 00, 00, 1c, 00, 2c, 00, 01, 05, 00, 06] +Number of files: 1 +- file 0 => $DIR/async_closure2.rs +Number of expressions: 0 +Number of file 0 mappings: 8 +- Code(Zero) at (prev + 17, 34) to (start + 0, 35) +- Code(Zero) at (prev + 1, 9) to (start + 0, 14) +- Code(Zero) at (prev + 0, 15) to (start + 0, 24) +- Code(Zero) at (prev + 0, 28) to (start + 0, 44) +- Code(Zero) at (prev + 1, 9) to (start + 0, 14) +- Code(Zero) at (prev + 0, 15) to (start + 0, 24) +- Code(Zero) at (prev + 0, 28) to (start + 0, 44) +- Code(Zero) at (prev + 1, 5) to (start + 0, 6) +Highest counter ID seen: (none) + diff --git a/tests/coverage/async_closure2.coverage b/tests/coverage/async_closure2.coverage new file mode 100644 index 000000000000..9e1b63d27c84 --- /dev/null +++ b/tests/coverage/async_closure2.coverage @@ -0,0 +1,33 @@ + LL| |// Regression test for . + LL| | + LL| |//@ edition: 2021 + LL| | + LL| |//@ aux-build: executor.rs + LL| |extern crate executor; + LL| | + LL| |use std::sync::atomic::{AtomicUsize, Ordering}; + LL| | + LL| |static STEPS: AtomicUsize = AtomicUsize::new(0); + LL| | + LL| 1|async fn call_once(f: impl AsyncFnOnce()) { + LL| 1| f().await; + LL| 1|} + LL| | + LL| 1|pub fn main() { + LL| 1| let async_closure = async || { + LL| 0| STEPS.fetch_add(1, Ordering::SeqCst); + LL| 0| STEPS.fetch_add(1, Ordering::SeqCst); + LL| 1| }; + ------------------ + | Unexecuted instantiation: async_closure2::main::{closure#0}::{closure#0}::<_> + ------------------ + | async_closure2::main::{closure#0}: + | LL| 1| let async_closure = async || { + | LL| | STEPS.fetch_add(1, Ordering::SeqCst); + | LL| | STEPS.fetch_add(1, Ordering::SeqCst); + | LL| 1| }; + ------------------ + LL| 1| executor::block_on(call_once(async_closure)); + LL| 1| assert_eq!(STEPS.load(Ordering::SeqCst), 2); + LL| 1|} + diff --git a/tests/coverage/async_closure2.rs b/tests/coverage/async_closure2.rs new file mode 100644 index 000000000000..01e268d928d3 --- /dev/null +++ b/tests/coverage/async_closure2.rs @@ -0,0 +1,23 @@ +// Regression test for . + +//@ edition: 2021 + +//@ aux-build: executor.rs +extern crate executor; + +use std::sync::atomic::{AtomicUsize, Ordering}; + +static STEPS: AtomicUsize = AtomicUsize::new(0); + +async fn call_once(f: impl AsyncFnOnce()) { + f().await; +} + +pub fn main() { + let async_closure = async || { + STEPS.fetch_add(1, Ordering::SeqCst); + STEPS.fetch_add(1, Ordering::SeqCst); + }; + executor::block_on(call_once(async_closure)); + assert_eq!(STEPS.load(Ordering::SeqCst), 2); +} From cf3249c0ccc2ef99adab21cb7b31a7a005826bb8 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Tue, 26 May 2026 10:01:36 -0400 Subject: [PATCH 2/2] coverage: Use original HIR info for synthetic by-move coroutine bodies --- .../rustc_mir_transform/src/coverage/hir_info.rs | 13 ++++++++++--- tests/coverage/async_closure2.cov-map | 12 +++++++++--- tests/coverage/async_closure2.coverage | 8 ++++---- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/hir_info.rs b/compiler/rustc_mir_transform/src/coverage/hir_info.rs index 28fdc52b06cb..85cf1970c12c 100644 --- a/compiler/rustc_mir_transform/src/coverage/hir_info.rs +++ b/compiler/rustc_mir_transform/src/coverage/hir_info.rs @@ -1,7 +1,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::LocalDefId; @@ -24,9 +24,16 @@ pub(crate) fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> E // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back // to HIR for it. - // HACK: For synthetic MIR bodies (async closures), use the def id of the HIR body. + // Synthetic by-move coroutine bodies don't have useful HIR of their own. + // Use the original coroutine body instead. These synthetic bodies are + // created with a coroutine type, so we can inspect that type as-is. if tcx.is_synthetic_mir(def_id) { - return extract_hir_info(tcx, tcx.local_parent(def_id)); + let effective_def_id = + match *tcx.type_of(def_id).instantiate_identity().skip_normalization().kind() { + ty::Coroutine(coroutine_def_id, _) => coroutine_def_id.expect_local(), + _ => tcx.local_parent(def_id), + }; + return extract_hir_info(tcx, effective_def_id); } let hir_node = tcx.hir_node_by_def_id(def_id); diff --git a/tests/coverage/async_closure2.cov-map b/tests/coverage/async_closure2.cov-map index 70e0a0f37116..bd58e4db5084 100644 --- a/tests/coverage/async_closure2.cov-map +++ b/tests/coverage/async_closure2.cov-map @@ -39,13 +39,19 @@ Number of file 0 mappings: 10 Highest counter ID seen: c1 Function name: async_closure2::main::{closure#0} -Raw bytes (14): 0x[01, 01, 00, 02, 01, 11, 22, 00, 23, 01, 03, 05, 00, 06] +Raw bytes (44): 0x[01, 01, 00, 08, 01, 11, 22, 00, 23, 01, 01, 09, 00, 0e, 01, 00, 0f, 00, 18, 01, 00, 1c, 00, 2c, 01, 01, 09, 00, 0e, 01, 00, 0f, 00, 18, 01, 00, 1c, 00, 2c, 01, 01, 05, 00, 06] Number of files: 1 - file 0 => $DIR/async_closure2.rs Number of expressions: 0 -Number of file 0 mappings: 2 +Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 17, 34) to (start + 0, 35) -- Code(Counter(0)) at (prev + 3, 5) to (start + 0, 6) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 14) +- Code(Counter(0)) at (prev + 0, 15) to (start + 0, 24) +- Code(Counter(0)) at (prev + 0, 28) to (start + 0, 44) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 14) +- Code(Counter(0)) at (prev + 0, 15) to (start + 0, 24) +- Code(Counter(0)) at (prev + 0, 28) to (start + 0, 44) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6) Highest counter ID seen: c0 Function name: async_closure2::main::{closure#0}::{closure#0}::<_> (unused) diff --git a/tests/coverage/async_closure2.coverage b/tests/coverage/async_closure2.coverage index 9e1b63d27c84..cd79b303fb66 100644 --- a/tests/coverage/async_closure2.coverage +++ b/tests/coverage/async_closure2.coverage @@ -15,16 +15,16 @@ LL| | LL| 1|pub fn main() { LL| 1| let async_closure = async || { - LL| 0| STEPS.fetch_add(1, Ordering::SeqCst); - LL| 0| STEPS.fetch_add(1, Ordering::SeqCst); + LL| 1| STEPS.fetch_add(1, Ordering::SeqCst); + LL| 1| STEPS.fetch_add(1, Ordering::SeqCst); LL| 1| }; ------------------ | Unexecuted instantiation: async_closure2::main::{closure#0}::{closure#0}::<_> ------------------ | async_closure2::main::{closure#0}: | LL| 1| let async_closure = async || { - | LL| | STEPS.fetch_add(1, Ordering::SeqCst); - | LL| | STEPS.fetch_add(1, Ordering::SeqCst); + | LL| 1| STEPS.fetch_add(1, Ordering::SeqCst); + | LL| 1| STEPS.fetch_add(1, Ordering::SeqCst); | LL| 1| }; ------------------ LL| 1| executor::block_on(call_once(async_closure));