Skip to content

Commit c4561fc

Browse files
authoredNov 10, 2022
Rollup merge of rust-lang#104219 - bryangarza:async-track-caller-dup, r=eholk
Support `#[track_caller]` on async fns Adds `#[track_caller]` to the generator that is created when we desugar the async fn. Fixes rust-lang#78840 Open questions: - What is the performance impact of adding `#[track_caller]` to every `GenFuture`'s `poll(...)` function, even if it's unused (i.e., the parent span does not set `#[track_caller]`)? We might need to set it only conditionally, if the indirection causes overhead we don't want.
2 parents 3cc4389 + 509b947 commit c4561fc

File tree

4 files changed

+108
-7
lines changed

4 files changed

+108
-7
lines changed
 

‎compiler/rustc_ast_lowering/src/expr.rs

+31-6
Original file line numberDiff line numberDiff line change
@@ -642,15 +642,40 @@ impl<'hir> LoweringContext<'_, 'hir> {
642642

643643
hir::ExprKind::Closure(c)
644644
};
645-
let generator = hir::Expr {
646-
hir_id: self.lower_node_id(closure_node_id),
647-
kind: generator_kind,
648-
span: self.lower_span(span),
645+
let parent_has_track_caller = self
646+
.attrs
647+
.values()
648+
.find(|attrs| attrs.into_iter().find(|attr| attr.has_name(sym::track_caller)).is_some())
649+
.is_some();
650+
let unstable_span =
651+
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
652+
653+
let hir_id = if parent_has_track_caller {
654+
let generator_hir_id = self.lower_node_id(closure_node_id);
655+
self.lower_attrs(
656+
generator_hir_id,
657+
&[Attribute {
658+
kind: AttrKind::Normal(ptr::P(NormalAttr {
659+
item: AttrItem {
660+
path: Path::from_ident(Ident::new(sym::track_caller, span)),
661+
args: MacArgs::Empty,
662+
tokens: None,
663+
},
664+
tokens: None,
665+
})),
666+
id: self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id(),
667+
style: AttrStyle::Outer,
668+
span: unstable_span,
669+
}],
670+
);
671+
generator_hir_id
672+
} else {
673+
self.lower_node_id(closure_node_id)
649674
};
650675

676+
let generator = hir::Expr { hir_id, kind: generator_kind, span: self.lower_span(span) };
677+
651678
// `future::from_generator`:
652-
let unstable_span =
653-
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
654679
let gen_future = self.expr_lang_item_path(
655680
unstable_span,
656681
hir::LangItem::FromGenerator,

‎compiler/rustc_ast_lowering/src/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
8686
impl_trait_defs: Vec::new(),
8787
impl_trait_bounds: Vec::new(),
8888
allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
89-
allow_gen_future: Some([sym::gen_future][..].into()),
89+
allow_gen_future: Some([sym::gen_future, sym::closure_track_caller][..].into()),
9090
allow_into_future: Some([sym::into_future][..].into()),
9191
generics_def_id_map: Default::default(),
9292
};

‎library/core/src/future/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ where
8282

8383
impl<T: Generator<ResumeTy, Yield = ()>> Future for GenFuture<T> {
8484
type Output = T::Return;
85+
#[track_caller]
8586
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
8687
// SAFETY: Safe because we're !Unpin + !Drop, and this is just a field projection.
8788
let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// run-pass
2+
// edition:2021
3+
#![feature(closure_track_caller)]
4+
5+
use std::future::Future;
6+
use std::panic;
7+
use std::sync::{Arc, Mutex};
8+
use std::task::{Context, Poll, Wake};
9+
use std::thread::{self, Thread};
10+
11+
/// A waker that wakes up the current thread when called.
12+
struct ThreadWaker(Thread);
13+
14+
impl Wake for ThreadWaker {
15+
fn wake(self: Arc<Self>) {
16+
self.0.unpark();
17+
}
18+
}
19+
20+
/// Run a future to completion on the current thread.
21+
fn block_on<T>(fut: impl Future<Output = T>) -> T {
22+
// Pin the future so it can be polled.
23+
let mut fut = Box::pin(fut);
24+
25+
// Create a new context to be passed to the future.
26+
let t = thread::current();
27+
let waker = Arc::new(ThreadWaker(t)).into();
28+
let mut cx = Context::from_waker(&waker);
29+
30+
// Run the future to completion.
31+
loop {
32+
match fut.as_mut().poll(&mut cx) {
33+
Poll::Ready(res) => return res,
34+
Poll::Pending => thread::park(),
35+
}
36+
}
37+
}
38+
39+
async fn bar() {
40+
panic!()
41+
}
42+
43+
async fn foo() {
44+
bar().await
45+
}
46+
47+
#[track_caller]
48+
async fn bar_track_caller() {
49+
panic!()
50+
}
51+
52+
async fn foo_track_caller() {
53+
bar_track_caller().await
54+
}
55+
56+
fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 {
57+
let loc = Arc::new(Mutex::new(None));
58+
59+
let hook = panic::take_hook();
60+
{
61+
let loc = loc.clone();
62+
panic::set_hook(Box::new(move |info| {
63+
*loc.lock().unwrap() = info.location().map(|loc| loc.line())
64+
}));
65+
}
66+
panic::catch_unwind(f).unwrap_err();
67+
panic::set_hook(hook);
68+
let x = loc.lock().unwrap().unwrap();
69+
x
70+
}
71+
72+
fn main() {
73+
assert_eq!(panicked_at(|| block_on(foo())), 40);
74+
assert_eq!(panicked_at(|| block_on(foo_track_caller())), 53);
75+
}

0 commit comments

Comments
 (0)
Please sign in to comment.