flux_common/
bug.rs

1use std::{
2    cell::Cell,
3    fmt,
4    panic::{Location, UnwindSafe},
5};
6
7use flux_config as config;
8use rustc_errors::{ExplicitBug, MultiSpan};
9use rustc_middle::ty::tls;
10use rustc_span::{ErrorGuaranteed, Span};
11
12thread_local! {
13    static TRACKED_SPAN: Cell<Option<Span>> = const { Cell::new(None) };
14}
15
16pub fn track_span<R>(span: Span, f: impl FnOnce() -> R) -> R {
17    TRACKED_SPAN.with(|cell| {
18        if span.is_dummy() {
19            return f();
20        }
21        let old = cell.replace(Some(span));
22        let r = f();
23        cell.set(old);
24        r
25    })
26}
27
28#[macro_export]
29macro_rules! tracked_span_dbg_assert_eq {
30    ($($arg:tt)*) => {
31        if core::cfg!(debug_assertions) {
32            $crate::tracked_span_assert_eq!($($arg)*);
33        }
34    };
35}
36
37#[macro_export]
38macro_rules! tracked_span_assert_eq {
39    ($left:expr, $right:expr $(,)?) => {
40        match (&$left, &$right) {
41            (left_val, right_val) => {
42                if !(*left_val == *right_val) {
43                    $crate::tracked_span_bug!(
44                        "assertion `left == right` failed\n  left: {left_val:?}\n right: {right_val:?}"
45                    )
46                }
47            }
48        }
49    };
50}
51
52#[macro_export]
53macro_rules! tracked_span_bug {
54    () => ( $crate::tracked_span_bug!("impossible case reached") );
55    ($msg:expr) => ({ $crate::bug::tracked_span_bug_fmt(::std::format_args!($msg)) });
56    ($msg:expr,) => ({ $crate::tracked_span_bug!($msg) });
57    ($fmt:expr, $($arg:tt)+) => ({
58        $crate::bug::tracked_span_bug_fmt(::std::format_args!($fmt, $($arg)+))
59    });
60}
61
62#[macro_export]
63macro_rules! bug {
64    () => ( $crate::bug!("impossible case reached") );
65    ($msg:expr) => ({ $crate::bug::bug_fmt(::std::format_args!($msg)) });
66    ($msg:expr,) => ({ $crate::bug!($msg) });
67    ($fmt:expr, $($arg:tt)+) => ({
68        $crate::bug::bug_fmt(::std::format_args!($fmt, $($arg)+))
69    });
70}
71
72#[macro_export]
73macro_rules! span_bug {
74    ($span:expr, $msg:expr) => ({ $crate::bug::span_bug_fmt($span, ::std::format_args!($msg)) });
75    ($span:expr, $msg:expr,) => ({ $crate::span_bug!($span, $msg) });
76    ($span:expr, $fmt:expr, $($arg:tt)+) => ({
77        $crate::bug::span_bug_fmt($span, ::std::format_args!($fmt, $($arg)+))
78    });
79}
80
81#[track_caller]
82pub fn bug_fmt(args: fmt::Arguments<'_>) -> ! {
83    opt_span_bug_fmt(None::<Span>, args, Location::caller());
84}
85
86#[track_caller]
87pub fn span_bug_fmt<S: Into<MultiSpan>>(span: S, args: fmt::Arguments<'_>) -> ! {
88    opt_span_bug_fmt(Some(span), args, Location::caller());
89}
90
91#[track_caller]
92pub fn tracked_span_bug_fmt(args: fmt::Arguments<'_>) -> ! {
93    let location = Location::caller();
94    opt_span_bug_fmt(TRACKED_SPAN.get(), args, location);
95}
96
97#[track_caller]
98fn opt_span_bug_fmt<S: Into<MultiSpan>>(
99    span: Option<S>,
100    args: fmt::Arguments<'_>,
101    location: &'static Location<'static>,
102) -> ! {
103    tls::with_opt(
104        #[track_caller]
105        move |tcx| {
106            let msg = format!("{location}: {args}");
107            match (tcx, span) {
108                (Some(tcx), Some(span)) => tcx.dcx().span_bug(span, msg),
109                (Some(tcx), None) => tcx.dcx().bug(msg),
110                (None, _) => std::panic::panic_any(msg),
111            }
112        },
113    )
114}
115
116pub fn catch_bugs<R>(msg: &str, f: impl FnOnce() -> R + UnwindSafe) -> Result<R, ErrorGuaranteed> {
117    if config::catch_bugs() {
118        match std::panic::catch_unwind(f) {
119            Ok(v) => Ok(v),
120            Err(payload) => {
121                tls::with_opt(move |tcx| {
122                    let Some(tcx) = tcx else { std::panic::resume_unwind(payload) };
123
124                    if payload.is::<ExplicitBug>() {
125                        eprintln!("note: bug caught [{msg}]\n");
126                        Err(tcx.dcx().delayed_bug("bug wasn't reported"))
127                    } else {
128                        eprintln!("note: uncaught panic [{msg}]\n");
129                        std::panic::resume_unwind(payload)
130                    }
131                })
132            }
133        }
134    } else {
135        Ok(f())
136    }
137}