@@ -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" ] ;
@@ -1211,43 +1211,66 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
1211
1211
}
1212
1212
}
1213
1213
1214
- static DEFAULT_HOOK : LazyLock < Box < dyn Fn ( & panic:: PanicInfo < ' _ > ) + Sync + Send + ' static > > =
1215
- LazyLock :: new ( || {
1216
- let hook = panic:: take_hook ( ) ;
1217
- panic:: set_hook ( Box :: new ( |info| {
1218
- // If the error was caused by a broken pipe then this is not a bug.
1219
- // Write the error and return immediately. See #98700.
1220
- #[ cfg( windows) ]
1221
- if let Some ( msg) = info. payload ( ) . downcast_ref :: < String > ( ) {
1222
- if msg. starts_with ( "failed printing to stdout: " ) && msg. ends_with ( "(os error 232)" )
1223
- {
1224
- early_error_no_abort ( ErrorOutputType :: default ( ) , & msg) ;
1225
- return ;
1226
- }
1227
- } ;
1214
+ /// Stores the default panic hook, from before [`install_ice_hook`] was called.
1215
+ static DEFAULT_HOOK : OnceLock < Box < dyn Fn ( & panic:: PanicInfo < ' _ > ) + Sync + Send + ' static > > =
1216
+ OnceLock :: new ( ) ;
1217
+
1218
+ /// Installs a panic hook that will print the ICE message on unexpected panics.
1219
+ ///
1220
+ /// The hook is intended to be useable even by external tools. You can pass a custom
1221
+ /// `bug_report_url`, or report arbitrary info in `extra_info`. Note that `extra_info` is called in
1222
+ /// a context where *the thread is currently panicking*, so it must not panic or the process will
1223
+ /// abort.
1224
+ ///
1225
+ /// If you have no extra info to report, pass the empty closure `|_| ()` as the argument to
1226
+ /// extra_info.
1227
+ ///
1228
+ /// A custom rustc driver can skip calling this to set up a custom ICE hook.
1229
+ pub fn install_ice_hook ( bug_report_url : & ' static str , extra_info : fn ( & Handler ) ) {
1230
+ // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
1231
+ // full backtraces. When a compiler ICE happens, we want to gather
1232
+ // as much information as possible to present in the issue opened
1233
+ // by the user. Compiler developers and other rustc users can
1234
+ // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
1235
+ // (e.g. `RUST_BACKTRACE=1`)
1236
+ if std:: env:: var ( "RUST_BACKTRACE" ) . is_err ( ) {
1237
+ std:: env:: set_var ( "RUST_BACKTRACE" , "full" ) ;
1238
+ }
1228
1239
1229
- // Invoke the default handler, which prints the actual panic message and optionally a backtrace
1230
- // Don't do this for delayed bugs, which already emit their own more useful backtrace.
1231
- if !info. payload ( ) . is :: < rustc_errors:: DelayedBugPanic > ( ) {
1232
- ( * DEFAULT_HOOK ) ( info) ;
1240
+ let default_hook = DEFAULT_HOOK . get_or_init ( panic:: take_hook) ;
1233
1241
1234
- // Separate the output with an empty line
1235
- eprintln ! ( ) ;
1242
+ panic:: set_hook ( Box :: new ( move |info| {
1243
+ // If the error was caused by a broken pipe then this is not a bug.
1244
+ // Write the error and return immediately. See #98700.
1245
+ #[ cfg( windows) ]
1246
+ if let Some ( msg) = info. payload ( ) . downcast_ref :: < String > ( ) {
1247
+ if msg. starts_with ( "failed printing to stdout: " ) && msg. ends_with ( "(os error 232)" ) {
1248
+ early_error_no_abort ( ErrorOutputType :: default ( ) , & msg) ;
1249
+ return ;
1236
1250
}
1251
+ } ;
1237
1252
1238
- // Print the ICE message
1239
- report_ice ( info, BUG_REPORT_URL ) ;
1240
- } ) ) ;
1241
- hook
1242
- } ) ;
1253
+ // Invoke the default handler, which prints the actual panic message and optionally a backtrace
1254
+ // Don't do this for delayed bugs, which already emit their own more useful backtrace.
1255
+ if !info. payload ( ) . is :: < rustc_errors:: DelayedBugPanic > ( ) {
1256
+ ( * default_hook) ( info) ;
1257
+
1258
+ // Separate the output with an empty line
1259
+ eprintln ! ( ) ;
1260
+ }
1261
+
1262
+ // Print the ICE message
1263
+ report_ice ( info, bug_report_url, extra_info) ;
1264
+ } ) ) ;
1265
+ }
1243
1266
1244
1267
/// Prints the ICE message, including query stack, but without backtrace.
1245
1268
///
1246
1269
/// The message will point the user at `bug_report_url` to report the ICE.
1247
1270
///
1248
1271
/// When `install_ice_hook` is called, this function will be called as the panic
1249
1272
/// hook.
1250
- pub fn report_ice ( info : & panic:: PanicInfo < ' _ > , bug_report_url : & str ) {
1273
+ pub fn report_ice ( info : & panic:: PanicInfo < ' _ > , bug_report_url : & str , extra_info : fn ( & Handler ) ) {
1251
1274
let fallback_bundle =
1252
1275
rustc_errors:: fallback_fluent_bundle ( crate :: DEFAULT_LOCALE_RESOURCES . to_vec ( ) , false ) ;
1253
1276
let emitter = Box :: new ( rustc_errors:: emitter:: EmitterWriter :: stderr (
@@ -1292,29 +1315,17 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
1292
1315
1293
1316
interface:: try_print_query_stack ( & handler, num_frames) ;
1294
1317
1318
+ // We don't trust this callback not to panic itself, so run it at the end after we're sure we've
1319
+ // printed all the relevant info.
1320
+ extra_info ( & handler) ;
1321
+
1295
1322
#[ cfg( windows) ]
1296
1323
if env:: var ( "RUSTC_BREAK_ON_ICE" ) . is_ok ( ) {
1297
1324
// Trigger a debugger if we crashed during bootstrap
1298
1325
unsafe { windows:: Win32 :: System :: Diagnostics :: Debug :: DebugBreak ( ) } ;
1299
1326
}
1300
1327
}
1301
1328
1302
- /// Installs a panic hook that will print the ICE message on unexpected panics.
1303
- ///
1304
- /// A custom rustc driver can skip calling this to set up a custom ICE hook.
1305
- pub fn install_ice_hook ( ) {
1306
- // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
1307
- // full backtraces. When a compiler ICE happens, we want to gather
1308
- // as much information as possible to present in the issue opened
1309
- // by the user. Compiler developers and other rustc users can
1310
- // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
1311
- // (e.g. `RUST_BACKTRACE=1`)
1312
- if std:: env:: var ( "RUST_BACKTRACE" ) . is_err ( ) {
1313
- std:: env:: set_var ( "RUST_BACKTRACE" , "full" ) ;
1314
- }
1315
- LazyLock :: force ( & DEFAULT_HOOK ) ;
1316
- }
1317
-
1318
1329
/// This allows tools to enable rust logging without having to magically match rustc's
1319
1330
/// tracing crate version.
1320
1331
pub fn init_rustc_env_logger ( ) {
@@ -1385,7 +1396,7 @@ pub fn main() -> ! {
1385
1396
init_rustc_env_logger ( ) ;
1386
1397
signal_handler:: install ( ) ;
1387
1398
let mut callbacks = TimePassesCallbacks :: default ( ) ;
1388
- install_ice_hook ( ) ;
1399
+ install_ice_hook ( DEFAULT_BUG_REPORT_URL , |_| ( ) ) ;
1389
1400
let exit_code = catch_with_exit_code ( || {
1390
1401
let args = env:: args_os ( )
1391
1402
. enumerate ( )
0 commit comments