88#![ feature( is_terminal) ]
99#![ feature( once_cell) ]
1010#![ feature( decl_macro) ]
11+ #![ feature( panic_info_message) ]
1112#![ recursion_limit = "256" ]
1213#![ allow( rustc:: potential_query_instability) ]
1314#![ deny( rustc:: untranslatable_diagnostic) ]
@@ -30,6 +31,8 @@ use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
3031use rustc_interface:: { interface, Queries } ;
3132use rustc_lint:: LintStore ;
3233use rustc_metadata:: locator;
34+ use rustc_query_impl:: QueryCtxt ;
35+ use rustc_query_system:: query:: QueryContext ;
3336use rustc_save_analysis as save;
3437use rustc_save_analysis:: DumpHandler ;
3538use rustc_session:: config:: { nightly_options, CG_OPTIONS , Z_OPTIONS } ;
@@ -45,7 +48,6 @@ use rustc_target::json::ToJson;
4548
4649use std:: borrow:: Cow ;
4750use std:: cmp:: max;
48- use std:: env;
4951use std:: ffi:: OsString ;
5052use std:: fs;
5153use std:: io:: { self , IsTerminal , Read , Write } ;
@@ -55,6 +57,7 @@ use std::process::{self, Command, Stdio};
5557use std:: str;
5658use std:: sync:: LazyLock ;
5759use std:: time:: Instant ;
60+ use std:: { backtrace, env} ;
5861
5962pub mod args;
6063pub mod pretty;
@@ -1181,6 +1184,102 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
11811184 }
11821185}
11831186
1187+ struct IceError ;
1188+ impl From < time:: error:: InvalidFormatDescription > for IceError {
1189+ fn from ( _: time:: error:: InvalidFormatDescription ) -> IceError {
1190+ IceError
1191+ }
1192+ }
1193+ impl From < time:: error:: Format > for IceError {
1194+ fn from ( _: time:: error:: Format ) -> IceError {
1195+ IceError
1196+ }
1197+ }
1198+ impl From < std:: io:: Error > for IceError {
1199+ fn from ( _: std:: io:: Error ) -> IceError {
1200+ IceError
1201+ }
1202+ }
1203+
1204+ fn write_ice_to_disk ( info : & panic:: PanicInfo < ' _ > ) -> Result < String , IceError > {
1205+ let capture = backtrace:: Backtrace :: force_capture ( ) ;
1206+ let now = time:: OffsetDateTime :: now_utc ( ) ;
1207+ let format = time:: format_description:: parse ( "[year]-[month]-[day]_[hour]:[minute]:[second]" ) ?;
1208+ let file_now = now. format ( & format) ?;
1209+ let format = time:: format_description:: parse ( "[year]-[month]-[day] [hour]:[minute]:[second]" ) ?;
1210+ let now = now. format ( & format) ?;
1211+ let path = format ! ( "rustc-ice-context-{file_now}.txt" ) ;
1212+ let mut file = std:: fs:: File :: create ( & path) ?;
1213+ writeln ! (
1214+ file,
1215+ "rustc {}{} running on {} at {now}" ,
1216+ util:: version_str!( ) . unwrap_or( "unknown_version" ) ,
1217+ match ( option_env!( "CFG_VER_HASH" ) , option_env!( "CFG_VER_DATE" ) ) {
1218+ ( Some ( hash) , Some ( date) ) => format!( " ({hash} - {date})" ) ,
1219+ ( Some ( val) , None ) | ( None , Some ( val) ) => format!( " ({val})" ) ,
1220+ ( None , None ) => String :: new( ) ,
1221+ } ,
1222+ config:: host_triple( ) ,
1223+ ) ?;
1224+
1225+ if let Some ( ( flags, excluded_cargo_defaults) ) = extra_compiler_flags ( ) {
1226+ writeln ! ( file, "compiler flags:" ) ?;
1227+ for flag in flags {
1228+ writeln ! ( file, " {flag}" ) ?;
1229+ }
1230+ if excluded_cargo_defaults {
1231+ writeln ! ( file, "some of the compiler flags provided by cargo are hidden" ) ?;
1232+ }
1233+ }
1234+ writeln ! ( file, "" ) ?;
1235+ match ( info. message ( ) , info. location ( ) ) {
1236+ ( Some ( message) , Some ( location) ) => {
1237+ writeln ! ( file, "panicked at {location}:\n {message}" ) ?;
1238+ }
1239+ ( None , Some ( location) ) => {
1240+ writeln ! ( file, "panicked at {location}" ) ?;
1241+ }
1242+ ( Some ( message) , None ) => {
1243+ writeln ! ( file, "panicked\n {message}" ) ?;
1244+ }
1245+ ( None , None ) => {
1246+ writeln ! ( file, "panicked" ) ?;
1247+ }
1248+ }
1249+
1250+ writeln ! ( file, "" ) ?;
1251+ writeln ! ( file, "{}" , capture) ?;
1252+
1253+ // Be careful relying on global state here: this code is called from
1254+ // a panic hook, which means that the global `Handler` may be in a weird
1255+ // state if it was responsible for triggering the panic.
1256+ // This the same as `interface::try_print_query_stack` but writing to file.
1257+ rustc_middle:: ty:: tls:: with_context_opt ( |icx| {
1258+ let Some ( icx) = icx else { return Err ( IceError ) ; } ;
1259+ let qcx = QueryCtxt :: from_tcx ( icx. tcx ) ;
1260+ let query_map = qcx. try_collect_active_jobs ( ) ;
1261+ let mut i = 0 ;
1262+ let mut current_query = icx. query ;
1263+ writeln ! ( file, "" ) ?;
1264+ writeln ! ( file, "query stack during panic:" ) ?;
1265+ while let Some ( query) = current_query {
1266+ let Some ( query_info) = query_map. as_ref ( ) . and_then ( |map| map. get ( & query) ) else {
1267+ break ;
1268+ } ;
1269+ writeln ! (
1270+ file,
1271+ "#{} [{:?}] {}" ,
1272+ i, query_info. query. dep_kind, query_info. query. description
1273+ ) ?;
1274+ current_query = query_info. job . parent ;
1275+ i += 1 ;
1276+ }
1277+ writeln ! ( file, "end of query stack" ) ?;
1278+ Ok ( ( ) )
1279+ } ) ?;
1280+ Ok ( path)
1281+ }
1282+
11841283static DEFAULT_HOOK : LazyLock < Box < dyn Fn ( & panic:: PanicInfo < ' _ > ) + Sync + Send + ' static > > =
11851284 LazyLock :: new ( || {
11861285 let hook = panic:: take_hook ( ) ;
@@ -1196,17 +1295,25 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
11961295 }
11971296 } ;
11981297
1298+ let is_dev = util:: version_str!( ) . map_or_else (
1299+ || std:: env:: var ( "RUSTC_BACKTRACE_FORCE" ) . as_deref ( ) != Ok ( "1" ) ,
1300+ |v| {
1301+ v. ends_with ( "-dev" )
1302+ && std:: env:: var ( "RUSTC_BACKTRACE_FORCE" ) . as_deref ( ) != Ok ( "0" )
1303+ } ,
1304+ ) ;
1305+ let written_ice = if !is_dev { write_ice_to_disk ( info) } else { Err ( IceError ) } ;
11991306 // Invoke the default handler, which prints the actual panic message and optionally a backtrace
12001307 // Don't do this for delayed bugs, which already emit their own more useful backtrace.
1201- if !info. payload ( ) . is :: < rustc_errors:: DelayedBugPanic > ( ) {
1308+ if !info. payload ( ) . is :: < rustc_errors:: DelayedBugPanic > ( ) && written_ice . is_err ( ) {
12021309 ( * DEFAULT_HOOK ) ( info) ;
12031310
12041311 // Separate the output with an empty line
12051312 eprintln ! ( ) ;
12061313 }
12071314
12081315 // Print the ICE message
1209- report_ice ( info, BUG_REPORT_URL ) ;
1316+ report_ice ( info, BUG_REPORT_URL , written_ice . ok ( ) ) ;
12101317 } ) ) ;
12111318 hook
12121319 } ) ;
@@ -1217,7 +1324,7 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
12171324///
12181325/// When `install_ice_hook` is called, this function will be called as the panic
12191326/// hook.
1220- pub fn report_ice ( info : & panic:: PanicInfo < ' _ > , bug_report_url : & str ) {
1327+ pub fn report_ice ( info : & panic:: PanicInfo < ' _ > , bug_report_url : & str , reported_ice : Option < String > ) {
12211328 let fallback_bundle =
12221329 rustc_errors:: fallback_fluent_bundle ( rustc_errors:: DEFAULT_LOCALE_RESOURCES , false ) ;
12231330 let emitter = Box :: new ( rustc_errors:: emitter:: EmitterWriter :: stderr (
@@ -1238,39 +1345,51 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
12381345 if !info. payload ( ) . is :: < rustc_errors:: ExplicitBug > ( )
12391346 && !info. payload ( ) . is :: < rustc_errors:: DelayedBugPanic > ( )
12401347 {
1241- let mut d = rustc_errors:: Diagnostic :: new ( rustc_errors:: Level :: Bug , "unexpected panic" ) ;
1348+ let mut d = rustc_errors:: Diagnostic :: new (
1349+ rustc_errors:: Level :: Bug ,
1350+ "the compiler unexpectedly panicked. this is a bug." ,
1351+ ) ;
12421352 handler. emit_diagnostic ( & mut d) ;
12431353 }
12441354
1245- let mut xs: Vec < Cow < ' static , str > > = vec ! [
1246- "the compiler unexpectedly panicked. this is a bug." . into( ) ,
1247- format!( "we would appreciate a bug report: {bug_report_url}" ) . into( ) ,
1248- format!(
1249- "rustc {} running on {}" ,
1250- util:: version_str!( ) . unwrap_or( "unknown_version" ) ,
1251- config:: host_triple( )
1252- )
1253- . into( ) ,
1254- ] ;
1255-
1256- if let Some ( ( flags, excluded_cargo_defaults) ) = extra_compiler_flags ( ) {
1257- xs. push ( format ! ( "compiler flags: {}" , flags. join( " " ) ) . into ( ) ) ;
1258-
1259- if excluded_cargo_defaults {
1260- xs. push ( "some of the compiler flags provided by cargo are hidden" . into ( ) ) ;
1355+ let xs: Vec < Cow < ' static , str > > = if let Some ( path) = & reported_ice {
1356+ vec ! [
1357+ format!( "all necessary context about this bug was written to `{path}`" ) . into( ) ,
1358+ format!( "we would appreciate a bug report with this context at <{bug_report_url}>" )
1359+ . into( ) ,
1360+ ]
1361+ } else {
1362+ let mut xs = vec ! [
1363+ format!( "we would appreciate a bug report at <{bug_report_url}>" ) . into( ) ,
1364+ format!(
1365+ "rustc {} running on {}" ,
1366+ util:: version_str!( ) . unwrap_or( "unknown_version" ) ,
1367+ config:: host_triple( )
1368+ )
1369+ . into( ) ,
1370+ ] ;
1371+ if let Some ( ( flags, excluded_cargo_defaults) ) = extra_compiler_flags ( ) {
1372+ xs. push ( format ! ( "compiler flags: {}" , flags. join( " " ) ) . into ( ) ) ;
1373+
1374+ if excluded_cargo_defaults {
1375+ xs. push ( "some of the compiler flags provided by cargo are hidden" . into ( ) ) ;
1376+ }
12611377 }
1262- }
1378+ xs
1379+ } ;
12631380
12641381 for note in & xs {
12651382 handler. note_without_error ( note. as_ref ( ) ) ;
12661383 }
12671384
1268- // If backtraces are enabled, also print the query stack
1269- let backtrace = env:: var_os ( "RUST_BACKTRACE" ) . map_or ( false , |x| & x != "0" ) ;
1385+ if reported_ice. is_none ( ) {
1386+ // If backtraces are enabled, also print the query stack
1387+ let backtrace = env:: var_os ( "RUST_BACKTRACE" ) . map_or ( false , |x| & x != "0" ) ;
12701388
1271- let num_frames = if backtrace { None } else { Some ( 2 ) } ;
1389+ let num_frames = if backtrace { None } else { Some ( 2 ) } ;
12721390
1273- interface:: try_print_query_stack ( & handler, num_frames) ;
1391+ interface:: try_print_query_stack ( & handler, num_frames) ;
1392+ }
12741393
12751394 #[ cfg( windows) ]
12761395 unsafe {
0 commit comments