@@ -25,7 +25,7 @@ use rustc_data_structures::profiling::{
2525use rustc_data_structures:: sync:: SeqCst ;
2626use rustc_errors:: registry:: { InvalidErrorCode , Registry } ;
2727use rustc_errors:: {
28- DiagnosticMessage , ErrorGuaranteed , PResult , SubdiagnosticMessage , TerminalUrl ,
28+ DiagnosticMessage , ErrorGuaranteed , Handler , PResult , SubdiagnosticMessage , TerminalUrl ,
2929} ;
3030use rustc_feature:: find_gated_cfg;
3131use rustc_fluent_macro:: fluent_messages;
@@ -55,7 +55,7 @@ use std::panic::{self, catch_unwind};
5555use std:: path:: PathBuf ;
5656use std:: process:: { self , Command , Stdio } ;
5757use std:: str;
58- use std:: sync:: LazyLock ;
58+ use std:: sync:: OnceLock ;
5959use std:: time:: Instant ;
6060
6161// This import blocks the use of panicking `print` and `println` in all the code
@@ -119,7 +119,7 @@ pub const EXIT_SUCCESS: i32 = 0;
119119/// Exit status code used for compilation failures and invalid flags.
120120pub const EXIT_FAILURE : i32 = 1 ;
121121
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\
123123 ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
124124
125125const 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 {
11961196 }
11971197}
11981198
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+ }
12131224
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) ;
12181226
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 ;
12211235 }
1236+ } ;
12221237
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+ }
12281251
12291252/// Prints the ICE message, including query stack, but without backtrace.
12301253///
12311254/// The message will point the user at `bug_report_url` to report the ICE.
12321255///
12331256/// When `install_ice_hook` is called, this function will be called as the panic
12341257/// 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 ) ) {
12361259 let fallback_bundle =
12371260 rustc_errors:: fallback_fluent_bundle ( crate :: DEFAULT_LOCALE_RESOURCES . to_vec ( ) , false ) ;
12381261 let emitter = Box :: new ( rustc_errors:: emitter:: EmitterWriter :: stderr (
@@ -1277,29 +1300,17 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
12771300
12781301 interface:: try_print_query_stack ( & handler, num_frames) ;
12791302
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+
12801307 #[ cfg( windows) ]
12811308 if env:: var ( "RUSTC_BREAK_ON_ICE" ) . is_ok ( ) {
12821309 // Trigger a debugger if we crashed during bootstrap
12831310 unsafe { windows:: Win32 :: System :: Diagnostics :: Debug :: DebugBreak ( ) } ;
12841311 }
12851312}
12861313
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-
13031314/// This allows tools to enable rust logging without having to magically match rustc's
13041315/// tracing crate version.
13051316pub fn init_rustc_env_logger ( ) {
@@ -1370,7 +1381,7 @@ pub fn main() -> ! {
13701381 init_rustc_env_logger ( ) ;
13711382 signal_handler:: install ( ) ;
13721383 let mut callbacks = TimePassesCallbacks :: default ( ) ;
1373- install_ice_hook ( ) ;
1384+ install_ice_hook ( DEFAULT_BUG_REPORT_URL , |_| ( ) ) ;
13741385 let exit_code = catch_with_exit_code ( || {
13751386 let args = env:: args_os ( )
13761387 . enumerate ( )
0 commit comments