673ab17c7f
This patch adds a feature gate `async_fn_track_caller` that is separate from `closure_track_caller`. This is to allow enabling `async_fn_track_caller` separately. Fixes #110009
135 lines
3.2 KiB
Rust
135 lines
3.2 KiB
Rust
// 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);
|
|
}
|