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}