@@ -25,7 +25,7 @@ use rustc_data_structures::profiling::{
25
25
use rustc_data_structures::sync::SeqCst;
26
26
use rustc_errors::registry::{InvalidErrorCode, Registry};
27
27
use rustc_errors::{
28
- DiagnosticMessage, ErrorGuaranteed, PResult, SubdiagnosticMessage, TerminalUrl,
28
+ DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl,
29
29
};
30
30
use rustc_feature::find_gated_cfg;
31
31
use rustc_fluent_macro::fluent_messages;
@@ -55,7 +55,7 @@ use std::panic::{self, catch_unwind};
55
55
use std::path::PathBuf;
56
56
use std::process::{self, Command, Stdio};
57
57
use std::str;
58
- use std::sync::LazyLock ;
58
+ use std::sync::OnceLock ;
59
59
use std::time::Instant;
60
60
61
61
// This import blocks the use of panicking `print` and `println` in all the code
@@ -119,7 +119,7 @@ pub const EXIT_SUCCESS: i32 = 0;
119
119
/// Exit status code used for compilation failures and invalid flags.
120
120
pub const EXIT_FAILURE: i32 = 1;
121
121
122
- const BUG_REPORT_URL : &str = "https://github.com/rust-lang/rust/issues/new\
122
+ pub const DEFAULT_BUG_REPORT_URL : &str = "https://github.com/rust-lang/rust/issues/new\
123
123
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
124
124
125
125
const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
@@ -1196,43 +1196,66 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
1196
1196
}
1197
1197
}
1198
1198
1199
- static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
1200
- LazyLock::new(|| {
1201
- let hook = panic::take_hook();
1202
- panic::set_hook(Box::new(|info| {
1203
- // If the error was caused by a broken pipe then this is not a bug.
1204
- // Write the error and return immediately. See #98700.
1205
- #[cfg(windows)]
1206
- if let Some(msg) = info.payload().downcast_ref::<String>() {
1207
- if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)")
1208
- {
1209
- early_error_no_abort(ErrorOutputType::default(), &msg);
1210
- return;
1211
- }
1212
- };
1199
+ /// Stores the default panic hook, from before [`install_ice_hook`] was called.
1200
+ static DEFAULT_HOOK: OnceLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
1201
+ OnceLock::new();
1202
+
1203
+ /// Installs a panic hook that will print the ICE message on unexpected panics.
1204
+ ///
1205
+ /// The hook is intended to be useable even by external tools. You can pass a custom
1206
+ /// `bug_report_url`, or report arbitrary info in `extra_info`. Note that `extra_info` is called in
1207
+ /// a context where *the thread is currently panicking*, so it must not panic or the process will
1208
+ /// abort.
1209
+ ///
1210
+ /// If you have no extra info to report, pass the empty closure `|_| ()` as the argument to
1211
+ /// extra_info.
1212
+ ///
1213
+ /// A custom rustc driver can skip calling this to set up a custom ICE hook.
1214
+ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) {
1215
+ // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
1216
+ // full backtraces. When a compiler ICE happens, we want to gather
1217
+ // as much information as possible to present in the issue opened
1218
+ // by the user. Compiler developers and other rustc users can
1219
+ // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
1220
+ // (e.g. `RUST_BACKTRACE=1`)
1221
+ if std::env::var("RUST_BACKTRACE").is_err() {
1222
+ std::env::set_var("RUST_BACKTRACE", "full");
1223
+ }
1213
1224
1214
- // Invoke the default handler, which prints the actual panic message and optionally a backtrace
1215
- // Don't do this for delayed bugs, which already emit their own more useful backtrace.
1216
- if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
1217
- (*DEFAULT_HOOK)(info);
1225
+ let default_hook = DEFAULT_HOOK.get_or_init(panic::take_hook);
1218
1226
1219
- // Separate the output with an empty line
1220
- eprintln!();
1227
+ panic::set_hook(Box::new(move |info| {
1228
+ // If the error was caused by a broken pipe then this is not a bug.
1229
+ // Write the error and return immediately. See #98700.
1230
+ #[cfg(windows)]
1231
+ if let Some(msg) = info.payload().downcast_ref::<String>() {
1232
+ if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") {
1233
+ early_error_no_abort(ErrorOutputType::default(), &msg);
1234
+ return;
1221
1235
}
1236
+ };
1222
1237
1223
- // Print the ICE message
1224
- report_ice(info, BUG_REPORT_URL);
1225
- }));
1226
- hook
1227
- });
1238
+ // Invoke the default handler, which prints the actual panic message and optionally a backtrace
1239
+ // Don't do this for delayed bugs, which already emit their own more useful backtrace.
1240
+ if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
1241
+ (*default_hook)(info);
1242
+
1243
+ // Separate the output with an empty line
1244
+ eprintln!();
1245
+ }
1246
+
1247
+ // Print the ICE message
1248
+ report_ice(info, bug_report_url, extra_info);
1249
+ }));
1250
+ }
1228
1251
1229
1252
/// Prints the ICE message, including query stack, but without backtrace.
1230
1253
///
1231
1254
/// The message will point the user at `bug_report_url` to report the ICE.
1232
1255
///
1233
1256
/// When `install_ice_hook` is called, this function will be called as the panic
1234
1257
/// hook.
1235
- pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
1258
+ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler) ) {
1236
1259
let fallback_bundle =
1237
1260
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
1238
1261
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
@@ -1277,29 +1300,17 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
1277
1300
1278
1301
interface::try_print_query_stack(&handler, num_frames);
1279
1302
1303
+ // We don't trust this callback not to panic itself, so run it at the end after we're sure we've
1304
+ // printed all the relevant info.
1305
+ extra_info(&handler);
1306
+
1280
1307
#[cfg(windows)]
1281
1308
if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
1282
1309
// Trigger a debugger if we crashed during bootstrap
1283
1310
unsafe { windows::Win32::System::Diagnostics::Debug::DebugBreak() };
1284
1311
}
1285
1312
}
1286
1313
1287
- /// Installs a panic hook that will print the ICE message on unexpected panics.
1288
- ///
1289
- /// A custom rustc driver can skip calling this to set up a custom ICE hook.
1290
- pub fn install_ice_hook() {
1291
- // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
1292
- // full backtraces. When a compiler ICE happens, we want to gather
1293
- // as much information as possible to present in the issue opened
1294
- // by the user. Compiler developers and other rustc users can
1295
- // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
1296
- // (e.g. `RUST_BACKTRACE=1`)
1297
- if std::env::var("RUST_BACKTRACE").is_err() {
1298
- std::env::set_var("RUST_BACKTRACE", "full");
1299
- }
1300
- LazyLock::force(&DEFAULT_HOOK);
1301
- }
1302
-
1303
1314
/// This allows tools to enable rust logging without having to magically match rustc's
1304
1315
/// tracing crate version.
1305
1316
pub fn init_rustc_env_logger() {
@@ -1370,7 +1381,7 @@ pub fn main() -> ! {
1370
1381
init_rustc_env_logger();
1371
1382
signal_handler::install();
1372
1383
let mut callbacks = TimePassesCallbacks::default();
1373
- install_ice_hook();
1384
+ install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| () );
1374
1385
let exit_code = catch_with_exit_code(|| {
1375
1386
let args = env::args_os()
1376
1387
.enumerate()
0 commit comments