summaryrefslogtreecommitdiffstats
path: root/tests/ui/async-await/track-caller/panic-track-caller.rs
blob: df8290e5fffc7f7363f88b8c257190cb4f90c575 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 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);
}