summaryrefslogtreecommitdiffstats
path: root/tests/ui/async-await/track-caller/panic-track-caller.rs
blob: 65bb23e0b4b9ec7c1d3b8c8d631713ff2119d551 (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
// run-pass
// edition:2021
// revisions: feat nofeat
// needs-unwind
#![feature(async_closure, stmt_expr_attributes)]
#![cfg_attr(feat, feature(closure_track_caller))]

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] //[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] //[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(feat)]
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(feat)]
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())), 43);

    #[cfg(feat)]
    assert_eq!(panicked_at(|| block_on(foo_track_caller())), 56);
    #[cfg(nofeat)]
    assert_eq!(panicked_at(|| block_on(foo_track_caller())), 52);

    #[cfg(feat)]
    assert_eq!(panicked_at(|| block_on(foo_assoc())), 69);
    #[cfg(nofeat)]
    assert_eq!(panicked_at(|| block_on(foo_assoc())), 64);

    #[cfg(feat)]
    assert_eq!(panicked_at(|| block_on(foo_closure())), 79);

    #[cfg(feat)]
    assert_eq!(panicked_at(|| block_on(foo_block())), 89);
}