Rollup merge of - michaelwoerister:awaitee_field, r=tmandry

async: Give predictable name to binding generated from .await expressions.

This name makes it to debuginfo and allows debuggers to identify such bindings and their captured versions in suspended async fns.

This will be useful for async stack traces, as discussed in https://internals.rust-lang.org/t/async-debugging-logical-stack-traces-setting-goals-collecting-examples/15547.

I don't know if this needs some discussion by ````@rust-lang/compiler,```` e.g. about the name of the binding (`__awaitee`) or about the fact that this PR introduces a (soft) guarantee about a compiler generated name. Although, regarding the later, I think the same reasoning applies here as it does for debuginfo in general.

r? ````@tmandry````
This commit is contained in:
Dylan DPC 2022-03-31 00:26:30 +02:00 committed by GitHub
commit 1c3657b20d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 14 deletions
compiler
rustc_ast_lowering/src
rustc_span/src
src/test/codegen

View File

@ -618,9 +618,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// Desugar `<expr>.await` into:
/// ```rust
/// match ::std::future::IntoFuture::into_future(<expr>) {
/// mut pinned => loop {
/// mut __awaitee => loop {
/// match unsafe { ::std::future::Future::poll(
/// <::std::pin::Pin>::new_unchecked(&mut pinned),
/// <::std::pin::Pin>::new_unchecked(&mut __awaitee),
/// ::std::future::get_context(task_context),
/// ) } {
/// ::std::task::Poll::Ready(result) => break result,
@ -657,21 +657,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
let expr = self.lower_expr_mut(expr);
let expr_hir_id = expr.hir_id;
let pinned_ident = Ident::with_dummy_span(sym::pinned);
let (pinned_pat, pinned_pat_hid) =
self.pat_ident_binding_mode(span, pinned_ident, hir::BindingAnnotation::Mutable);
// Note that the name of this binding must not be changed to something else because
// debuggers and debugger extensions expect it to be called `__awaitee`. They use
// this name to identify what is being awaited by a suspended async functions.
let awaitee_ident = Ident::with_dummy_span(sym::__awaitee);
let (awaitee_pat, awaitee_pat_hid) =
self.pat_ident_binding_mode(span, awaitee_ident, hir::BindingAnnotation::Mutable);
let task_context_ident = Ident::with_dummy_span(sym::_task_context);
// unsafe {
// ::std::future::Future::poll(
// ::std::pin::Pin::new_unchecked(&mut pinned),
// ::std::pin::Pin::new_unchecked(&mut __awaitee),
// ::std::future::get_context(task_context),
// )
// }
let poll_expr = {
let pinned = self.expr_ident(span, pinned_ident, pinned_pat_hid);
let ref_mut_pinned = self.expr_mut_addr_of(span, pinned);
let awaitee = self.expr_ident(span, awaitee_ident, awaitee_pat_hid);
let ref_mut_awaitee = self.expr_mut_addr_of(span, awaitee);
let task_context = if let Some(task_context_hid) = self.task_context {
self.expr_ident_mut(span, task_context_ident, task_context_hid)
} else {
@ -681,7 +684,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let new_unchecked = self.expr_call_lang_item_fn_mut(
span,
hir::LangItem::PinNewUnchecked,
arena_vec![self; ref_mut_pinned],
arena_vec![self; ref_mut_awaitee],
Some(expr_hir_id),
);
let get_context = self.expr_call_lang_item_fn_mut(
@ -782,8 +785,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
span: self.lower_span(span),
});
// mut pinned => loop { ... }
let pinned_arm = self.arm(pinned_pat, loop_expr);
// mut __awaitee => loop { ... }
let awaitee_arm = self.arm(awaitee_pat, loop_expr);
// `match ::std::future::IntoFuture::into_future(<expr>) { ... }`
let into_future_span = self.mark_span_with_reason(
@ -799,11 +802,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
// match <into_future_expr> {
// mut pinned => loop { .. }
// mut __awaitee => loop { .. }
// }
hir::ExprKind::Match(
into_future_expr,
arena_vec![self; pinned_arm],
arena_vec![self; awaitee_arm],
hir::MatchSource::AwaitDesugar,
)
}

View File

@ -272,6 +272,7 @@ symbols! {
__D,
__H,
__S,
__awaitee,
__try_var,
_d,
_e,
@ -1019,7 +1020,6 @@ symbols! {
pattern_parentheses,
phantom_data,
pin,
pinned,
platform_intrinsics,
plugin,
plugin_registrar,

View File

@ -0,0 +1,23 @@
// This test makes sure that the generator field capturing the awaitee in a `.await` expression
// is called "__awaitee" in debuginfo. This name must not be changed since debuggers and debugger
// extensions rely on the field having this name.
// ignore-tidy-linelength
// compile-flags: -C debuginfo=2 --edition=2018
async fn foo() {}
async fn async_fn_test() {
foo().await;
}
// NONMSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}",
// MSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$<async_fn_debug_awaitee_field::async_fn_test::async_fn_env$0>",
// 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: "GenFuture<async_fn_debug_awaitee_field::foo::{async_fn_env#0}>",
// MSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "GenFuture<enum$<async_fn_debug_awaitee_field::foo::async_fn_env$0> >",
fn main() {
let _fn = async_fn_test();
}