| //@ run-pass |
| //@ edition:2021 |
| //@ revisions: afn cls nofeat |
| //@ needs-unwind |
| // gate-test-async_fn_track_caller |
| #![feature(async_closure, stmt_expr_attributes)] |
| #![cfg_attr(afn, feature(async_fn_track_caller))] |
| #![cfg_attr(cls, feature(closure_track_caller))] |
| #![allow(unused)] |
| |
| use std::future::Future; |
| use std::panic; |
| use std::sync::{Arc, Mutex}; |
| use std::task::{Context, Poll, Wake}; |
| use std::thread::{self, Thread}; |
| |
| /// A waker that wakes up the current thread when called. |
| struct ThreadWaker(Thread); |
| |
| impl Wake for ThreadWaker { |
| fn wake(self: Arc<Self>) { |
| self.0.unpark(); |
| } |
| } |
| |
| /// Run a future to completion on the current thread. |
| fn block_on<T>(fut: impl Future<Output = T>) -> T { |
| // Pin the future so it can be polled. |
| let mut fut = Box::pin(fut); |
| |
| // Create a new context to be passed to the future. |
| let t = thread::current(); |
| let waker = Arc::new(ThreadWaker(t)).into(); |
| let mut cx = Context::from_waker(&waker); |
| |
| // Run the future to completion. |
| loop { |
| match fut.as_mut().poll(&mut cx) { |
| Poll::Ready(res) => return res, |
| Poll::Pending => thread::park(), |
| } |
| } |
| } |
| |
| async fn bar() { |
| panic!() |
| } |
| |
| async fn foo() { |
| bar().await |
| } |
| |
| #[track_caller] |
| //[cls]~^ WARN `#[track_caller]` on async functions is a no-op |
| //[nofeat]~^^ WARN `#[track_caller]` on async functions is a no-op |
| async fn bar_track_caller() { |
| panic!() |
| } |
| |
| async fn foo_track_caller() { |
| bar_track_caller().await |
| } |
| |
| struct Foo; |
| |
| impl Foo { |
| #[track_caller] |
| //[cls]~^ WARN `#[track_caller]` on async functions is a no-op |
| //[nofeat]~^^ WARN `#[track_caller]` on async functions is a no-op |
| async fn bar_assoc() { |
| panic!(); |
| } |
| } |
| |
| async fn foo_assoc() { |
| Foo::bar_assoc().await |
| } |
| |
| // Since compilation is expected to fail for this fn when using |
| // `nofeat`, we test that separately in `async-closure-gate.rs` |
| #[cfg(cls)] |
| async fn foo_closure() { |
| let c = #[track_caller] async || { |
| panic!(); |
| }; |
| c().await |
| } |
| |
| // Since compilation is expected to fail for this fn when using |
| // `nofeat`, we test that separately in `async-block.rs` |
| #[cfg(cls)] |
| async fn foo_block() { |
| let a = #[track_caller] async { |
| panic!(); |
| }; |
| a.await |
| } |
| |
| fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { |
| let loc = Arc::new(Mutex::new(None)); |
| |
| let hook = panic::take_hook(); |
| { |
| let loc = loc.clone(); |
| panic::set_hook(Box::new(move |info| { |
| *loc.lock().unwrap() = info.location().map(|loc| loc.line()) |
| })); |
| } |
| panic::catch_unwind(f).unwrap_err(); |
| panic::set_hook(hook); |
| let x = loc.lock().unwrap().unwrap(); |
| x |
| } |
| |
| fn main() { |
| assert_eq!(panicked_at(|| block_on(foo())), 46 |
| ); |
| |
| #[cfg(afn)] |
| assert_eq!(panicked_at(|| block_on(foo_track_caller())), 61); |
| #[cfg(any(cls, nofeat))] |
| assert_eq!(panicked_at(|| block_on(foo_track_caller())), 57); |
| |
| #[cfg(afn)] |
| assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); |
| #[cfg(any(cls, nofeat))] |
| assert_eq!(panicked_at(|| block_on(foo_assoc())), 71); |
| |
| #[cfg(cls)] |
| assert_eq!(panicked_at(|| block_on(foo_closure())), 84); |
| |
| #[cfg(cls)] |
| assert_eq!(panicked_at(|| block_on(foo_block())), 96); |
| } |