diff --git a/Cargo.lock b/Cargo.lock index edee662ab6470..f8c7321ea515f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3212,6 +3212,7 @@ dependencies = [ "rustc_trait_selection", "rustc_ty_utils", "serde_json", + "time", "tracing", "windows", ] @@ -4815,6 +4816,33 @@ dependencies = [ name = "tier-check" version = "0.1.0" +[[package]] +name = "time" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + [[package]] name = "tinystr" version = "0.7.1" diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index ececa29b23155..1c5d7a7c68e92 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -362,7 +362,7 @@ pub struct CodegenContext { impl CodegenContext { pub fn create_diag_handler(&self) -> Handler { - Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone())) + Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone()), None) } pub fn config(&self, kind: ModuleKind) -> &ModuleConfig { diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 67352c55c9019..a7b01618ade39 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [lib] [dependencies] +time = { version = "0.3", default-features = false, features = ["formatting", ] } tracing = { version = "0.1.35" } serde_json = "1.0.59" rustc_log = { path = "../rustc_log" } diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 22b4ec6b0d1b1..9b2f2c3386078 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -3,7 +3,11 @@ driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden driver_impl_ice_flags = compiler flags: {$flags} +driver_impl_ice_path = please attach the file at `{$path}` to your bug report +driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error} +driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}` driver_impl_ice_version = rustc {$version} running on {$triple} + driver_impl_rlink_empty_version_number = The input does not contain version number driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}` diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 9352fe3147e59..957d7a6cfb909 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -7,6 +7,8 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(lazy_cell)] #![feature(decl_macro)] +#![feature(ice_to_disk)] +#![feature(let_chains)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] @@ -57,8 +59,11 @@ use std::panic::{self, catch_unwind}; use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::OnceLock; -use std::time::Instant; +use std::time::{Instant, SystemTime}; +use time::format_description::well_known::Rfc3339; +use time::OffsetDateTime; #[allow(unused_macros)] macro do_not_use_print($($t:tt)*) { @@ -297,6 +302,7 @@ fn run_compiler( input: Input::File(PathBuf::new()), output_file: ofile, output_dir: odir, + ice_file: ice_path().clone(), file_loader, locale_resources: DEFAULT_LOCALE_RESOURCES, lint_caps: Default::default(), @@ -1295,9 +1301,35 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { } } -/// Stores the default panic hook, from before [`install_ice_hook`] was called. -static DEFAULT_HOOK: OnceLock) + Sync + Send + 'static>> = - OnceLock::new(); +pub static ICE_PATH: OnceLock> = OnceLock::new(); + +pub fn ice_path() -> &'static Option { + ICE_PATH.get_or_init(|| { + if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() { + return None; + } + if let Ok("0") = std::env::var("RUST_BACKTRACE").as_deref() { + return None; + } + let mut path = match std::env::var("RUSTC_ICE").as_deref() { + // Explicitly opting out of writing ICEs to disk. + Ok("0") => return None, + Ok(s) => match PathBuf::try_from(s) { + Ok(p) => p, + // On parse error, fallback to the default directory, otherwise the backtraces might be + // accidentally lost due to a simple typo. The directory might still not be accessible + // for some other reason. + Err(_) => std::env::current_dir().unwrap_or_default(), + }, + Err(_) => std::env::current_dir().unwrap_or_default(), + }; + let now: OffsetDateTime = SystemTime::now().into(); + let file_now = now.format(&Rfc3339).unwrap_or(String::new()); + let pid = std::process::id(); + path.push(format!("rustc-ice-{file_now}-{pid}.txt")); + Some(path) + }) +} /// Installs a panic hook that will print the ICE message on unexpected panics. /// @@ -1321,8 +1353,6 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) std::env::set_var("RUST_BACKTRACE", "full"); } - let default_hook = DEFAULT_HOOK.get_or_init(panic::take_hook); - panic::set_hook(Box::new(move |info| { // If the error was caused by a broken pipe then this is not a bug. // Write the error and return immediately. See #98700. @@ -1339,7 +1369,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) // Invoke the default handler, which prints the actual panic message and optionally a backtrace // Don't do this for delayed bugs, which already emit their own more useful backtrace. if !info.payload().is::() { - (*default_hook)(info); + std::panic_hook_with_disk_dump(info, ice_path().as_deref()); // Separate the output with an empty line eprintln!(); @@ -1371,7 +1401,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: false, TerminalUrl::No, )); - let handler = rustc_errors::Handler::with_emitter(true, None, emitter); + let handler = rustc_errors::Handler::with_emitter(true, None, emitter, None); // a .span_bug or .bug call has already printed what // it wants to print. @@ -1382,10 +1412,40 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: } handler.emit_note(session_diagnostics::IceBugReport { bug_report_url }); - handler.emit_note(session_diagnostics::IceVersion { - version: util::version_str!().unwrap_or("unknown_version"), - triple: config::host_triple(), - }); + + let version = util::version_str!().unwrap_or("unknown_version"); + let triple = config::host_triple(); + + static FIRST_PANIC: AtomicBool = AtomicBool::new(true); + + let file = if let Some(path) = ice_path().as_ref() { + // Create the ICE dump target file. + match crate::fs::File::options().create(true).append(true).open(&path) { + Ok(mut file) => { + handler + .emit_note(session_diagnostics::IcePath { path: path.display().to_string() }); + if FIRST_PANIC.swap(false, Ordering::SeqCst) { + let _ = write!(file, "\n\nrustc version: {version}\nplatform: {triple}"); + } + Some(file) + } + Err(err) => { + // The path ICE couldn't be written to disk, provide feedback to the user as to why. + handler.emit_warning(session_diagnostics::IcePathError { + path: path.display().to_string(), + error: err.to_string(), + env_var: std::env::var("RUSTC_ICE") + .ok() + .map(|env_var| session_diagnostics::IcePathErrorEnv { env_var }), + }); + handler.emit_note(session_diagnostics::IceVersion { version, triple }); + None + } + } + } else { + handler.emit_note(session_diagnostics::IceVersion { version, triple }); + None + }; if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() { handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") }); @@ -1399,7 +1459,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: let num_frames = if backtrace { None } else { Some(2) }; - interface::try_print_query_stack(&handler, num_frames); + interface::try_print_query_stack(&handler, num_frames, file); // We don't trust this callback not to panic itself, so run it at the end after we're sure we've // printed all the relevant info. diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 638b368f70214..f7f06b7d0f2b8 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -1,4 +1,4 @@ -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, Subdiagnostic}; #[derive(Diagnostic)] #[diag(driver_impl_rlink_unable_to_read)] @@ -56,6 +56,27 @@ pub(crate) struct IceVersion<'a> { pub triple: &'a str, } +#[derive(Diagnostic)] +#[diag(driver_impl_ice_path)] +pub(crate) struct IcePath { + pub path: String, +} + +#[derive(Diagnostic)] +#[diag(driver_impl_ice_path_error)] +pub(crate) struct IcePathError { + pub path: String, + pub error: String, + #[subdiagnostic] + pub env_var: Option, +} + +#[derive(Subdiagnostic)] +#[note(driver_impl_ice_path_error_env)] +pub(crate) struct IcePathErrorEnv { + pub env_var: String, +} + #[derive(Diagnostic)] #[diag(driver_impl_ice_flags)] pub(crate) struct IceFlags { diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index f3ee83fd4d218..8e52a33ce3090 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -355,6 +355,13 @@ impl DiagnosticMessage { } } } + + pub fn as_str(&self) -> Option<&str> { + match self { + DiagnosticMessage::Eager(s) | DiagnosticMessage::Str(s) => Some(s), + DiagnosticMessage::FluentIdentifier(_, _) => None, + } + } } impl From for DiagnosticMessage { diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 671dc449eaa73..db0dd4ffe8e17 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -64,7 +64,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); - let handler = Handler::with_emitter(true, None, Box::new(je)); + let handler = Handler::with_emitter(true, None, Box::new(je), None); handler.span_err(span, "foo"); let bytes = output.lock().unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index b9db25103a3a8..31410c39d368e 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -47,9 +47,10 @@ use std::borrow::Cow; use std::error::Report; use std::fmt; use std::hash::Hash; +use std::io::Write; use std::num::NonZeroUsize; use std::panic; -use std::path::Path; +use std::path::{Path, PathBuf}; use termcolor::{Color, ColorSpec}; @@ -461,6 +462,10 @@ struct HandlerInner { /// /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html fulfilled_expectations: FxHashSet, + + /// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be + /// stored along side the main panic backtrace. + ice_file: Option, } /// A key denoting where from a diagnostic was stashed. @@ -550,6 +555,7 @@ impl Handler { sm: Option>, fluent_bundle: Option>, fallback_bundle: LazyFallbackBundle, + ice_file: Option, ) -> Self { Self::with_tty_emitter_and_flags( color_config, @@ -557,6 +563,7 @@ impl Handler { fluent_bundle, fallback_bundle, HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, + ice_file, ) } @@ -566,6 +573,7 @@ impl Handler { fluent_bundle: Option>, fallback_bundle: LazyFallbackBundle, flags: HandlerFlags, + ice_file: Option, ) -> Self { let emitter = Box::new(EmitterWriter::stderr( color_config, @@ -579,23 +587,26 @@ impl Handler { flags.track_diagnostics, TerminalUrl::No, )); - Self::with_emitter_and_flags(emitter, flags) + Self::with_emitter_and_flags(emitter, flags, ice_file) } pub fn with_emitter( can_emit_warnings: bool, treat_err_as_bug: Option, emitter: Box, + ice_file: Option, ) -> Self { Handler::with_emitter_and_flags( emitter, HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, + ice_file, ) } pub fn with_emitter_and_flags( emitter: Box, flags: HandlerFlags, + ice_file: Option, ) -> Self { Self { flags, @@ -618,6 +629,7 @@ impl Handler { check_unstable_expect_diagnostics: false, unstable_expect_diagnostics: Vec::new(), fulfilled_expectations: Default::default(), + ice_file, }), } } @@ -1657,8 +1669,21 @@ impl HandlerInner { explanation: impl Into + Copy, ) { let mut no_bugs = true; + // If backtraces are enabled, also print the query stack + let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0"); for bug in bugs { - let mut bug = bug.decorate(); + if let Some(file) = self.ice_file.as_ref() + && let Ok(mut out) = std::fs::File::options().append(true).open(file) + { + let _ = write!( + &mut out, + "\n\ndelayed span bug: {}\n{}", + bug.inner.styled_message().iter().filter_map(|(msg, _)| msg.as_str()).collect::(), + &bug.note + ); + } + let mut bug = + if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner }; if no_bugs { // Put the overall explanation before the `DelayedBug`s, to diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 8a5e09475ff13..6490e52955db8 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -161,7 +161,7 @@ fn test_harness(file_text: &str, span_labels: Vec, expected_output: & false, TerminalUrl::No, ); - let handler = Handler::with_emitter(true, None, Box::new(emitter)); + let handler = Handler::with_emitter(true, None, Box::new(emitter), None); #[allow(rustc::untranslatable_diagnostic)] handler.span_err(msp, "foo"); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 953c2e4b8f86f..4e1668c8a8cf0 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -255,6 +255,7 @@ pub struct Config { pub input: Input, pub output_dir: Option, pub output_file: Option, + pub ice_file: Option, pub file_loader: Option>, pub locale_resources: &'static [&'static str], @@ -315,6 +316,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.lint_caps, config.make_codegen_backend, registry.clone(), + config.ice_file, ); if let Some(parse_sess_created) = config.parse_sess_created { @@ -346,7 +348,11 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se ) } -pub fn try_print_query_stack(handler: &Handler, num_frames: Option) { +pub fn try_print_query_stack( + handler: &Handler, + num_frames: Option, + file: Option, +) { eprintln!("query stack during panic:"); // Be careful relying on global state here: this code is called from @@ -358,7 +364,8 @@ pub fn try_print_query_stack(handler: &Handler, num_frames: Option) { QueryCtxt::new(icx.tcx), icx.query, handler, - num_frames + num_frames, + file, )) } else { 0 diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 09141afd13707..5c6c3491b388c 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -67,6 +67,7 @@ fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Se None, None, "", + None, ); (sess, cfg) } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 035ea2414f767..12d33f06309b7 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -70,6 +70,7 @@ pub fn create_session( Box Box + Send>, >, descriptions: Registry, + ice_file: Option, ) -> (Session, Box) { let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend { make_codegen_backend(&sopts) @@ -111,6 +112,7 @@ pub fn create_session( file_loader, target_override, rustc_version_str().unwrap_or("unknown"), + ice_file, ); codegen_backend.init(&sess); diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index f45f7ca5da6dd..e964ba851bdf6 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -13,6 +13,7 @@ use rustc_session::Session; use rustc_span::Span; use std::hash::Hash; +use std::io::Write; use std::num::NonZeroU64; #[cfg(parallel_compiler)] @@ -617,30 +618,50 @@ pub fn print_query_stack( mut current_query: Option, handler: &Handler, num_frames: Option, + mut file: Option, ) -> usize { // Be careful relying on global state here: this code is called from // a panic hook, which means that the global `Handler` may be in a weird // state if it was responsible for triggering the panic. - let mut i = 0; + let mut count_printed = 0; + let mut count_total = 0; let query_map = qcx.try_collect_active_jobs(); + if let Some(ref mut file) = file { + let _ = writeln!(file, "\n\nquery stack during panic:"); + } while let Some(query) = current_query { - if Some(i) == num_frames { - break; - } let Some(query_info) = query_map.as_ref().and_then(|map| map.get(&query)) else { break; }; - let mut diag = Diagnostic::new( - Level::FailureNote, - format!("#{} [{:?}] {}", i, query_info.query.dep_kind, query_info.query.description), - ); - diag.span = query_info.job.span.into(); - handler.force_print_diagnostic(diag); + if Some(count_printed) < num_frames || num_frames.is_none() { + // Only print to stderr as many stack frames as `num_frames` when present. + let mut diag = Diagnostic::new( + Level::FailureNote, + format!( + "#{} [{:?}] {}", + count_printed, query_info.query.dep_kind, query_info.query.description + ), + ); + diag.span = query_info.job.span.into(); + handler.force_print_diagnostic(diag); + count_printed += 1; + } + + if let Some(ref mut file) = file { + let _ = writeln!( + file, + "#{} [{:?}] {}", + count_total, query_info.query.dep_kind, query_info.query.description + ); + } current_query = query_info.job.parent; - i += 1; + count_total += 1; } - i + if let Some(ref mut file) = file { + let _ = writeln!(file, "end of query stack"); + } + count_printed } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 194f7201ff357..0a0ec927209a3 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -229,6 +229,7 @@ impl ParseSess { Some(sm.clone()), None, fallback_bundle, + None, ); ParseSess::with_span_handler(handler, sm) } @@ -259,12 +260,20 @@ impl ParseSess { pub fn with_silent_emitter(fatal_note: Option) -> Self { let fallback_bundle = fallback_fluent_bundle(Vec::new(), false); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fatal_handler = - Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, None, fallback_bundle); + let fatal_handler = Handler::with_tty_emitter( + ColorConfig::Auto, + false, + None, + None, + None, + fallback_bundle, + None, + ); let handler = Handler::with_emitter( false, None, Box::new(SilentEmitter { fatal_handler, fatal_note }), + None, ); ParseSess::with_span_handler(handler, sm) } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 5be122ffbdeb0..7922394081e31 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1392,6 +1392,7 @@ pub fn build_session( file_loader: Option>, target_override: Option, cfg_version: &'static str, + ice_file: Option, ) -> Session { // FIXME: This is not general enough to make the warning lint completely override // normal diagnostic warnings, since the warning lint can also be denied and changed @@ -1440,6 +1441,7 @@ pub fn build_session( let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( emitter, sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings), + ice_file, ); let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile @@ -1731,7 +1733,7 @@ pub struct EarlyErrorHandler { impl EarlyErrorHandler { pub fn new(output: ErrorOutputType) -> Self { let emitter = mk_emitter(output); - Self { handler: rustc_errors::Handler::with_emitter(true, None, emitter) } + Self { handler: rustc_errors::Handler::with_emitter(true, None, emitter, None) } } pub fn abort_if_errors(&self) { @@ -1745,7 +1747,7 @@ impl EarlyErrorHandler { self.handler.abort_if_errors(); let emitter = mk_emitter(output); - self.handler = Handler::with_emitter(true, None, emitter); + self.handler = Handler::with_emitter(true, None, emitter, None); } #[allow(rustc::untranslatable_diagnostic)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 72b9ad3480b32..0c91841fd81e2 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -605,6 +605,9 @@ pub mod alloc; mod panicking; mod personality; +#[unstable(feature = "ice_to_disk", issue = "none")] +pub use panicking::panic_hook_with_disk_dump; + #[path = "../../backtrace/src/lib.rs"] #[allow(dead_code, unused_attributes, fuzzy_provenance_casts)] mod backtrace_rs; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index a6a370409c0e2..0e90d618ad434 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -234,7 +234,16 @@ where *hook = Hook::Custom(Box::new(move |info| hook_fn(&prev, info))); } +/// The default panic handler. fn default_hook(info: &PanicInfo<'_>) { + panic_hook_with_disk_dump(info, None) +} + +#[unstable(feature = "ice_to_disk", issue = "none")] +/// The implementation of the default panic handler. +/// +/// It can also write the backtrace to a given `path`. This functionality is used only by `rustc`. +pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path::Path>) { // If this is a double panic, make sure that we print a backtrace // for this panic. Otherwise only print it if logging is enabled. let backtrace = if panic_count::get_count() >= 2 { @@ -256,7 +265,7 @@ fn default_hook(info: &PanicInfo<'_>) { let thread = thread_info::current_thread(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); - let write = |err: &mut dyn crate::io::Write| { + let write = |err: &mut dyn crate::io::Write, backtrace: Option| { let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}"); static FIRST_PANIC: AtomicBool = AtomicBool::new(true); @@ -270,10 +279,19 @@ fn default_hook(info: &PanicInfo<'_>) { } Some(BacktraceStyle::Off) => { if FIRST_PANIC.swap(false, Ordering::SeqCst) { - let _ = writeln!( - err, - "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace" - ); + if let Some(path) = path { + let _ = writeln!( + err, + "note: a backtrace for this error was stored at `{}`", + path.display(), + ); + } else { + let _ = writeln!( + err, + "note: run with `RUST_BACKTRACE=1` environment variable to display a \ + backtrace" + ); + } } } // If backtraces aren't supported, do nothing. @@ -281,11 +299,17 @@ fn default_hook(info: &PanicInfo<'_>) { } }; + if let Some(path) = path + && let Ok(mut out) = crate::fs::File::options().create(true).write(true).open(&path) + { + write(&mut out, BacktraceStyle::full()); + } + if let Some(local) = set_output_capture(None) { - write(&mut *local.lock().unwrap_or_else(|e| e.into_inner())); + write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()), backtrace); set_output_capture(Some(local)); } else if let Some(mut out) = panic_output() { - write(&mut out); + write(&mut out, backtrace); } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 9687b8b18878e..bf4ac4b4f3bb2 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -176,6 +176,7 @@ pub(crate) fn new_handler( rustc_errors::Handler::with_emitter_and_flags( emitter, unstable_opts.diagnostic_handler_flags(true), + None, ) } @@ -296,6 +297,7 @@ pub(crate) fn create_config( }), make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), + ice_file: None, } } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 217257316c84f..ab3807a609a9a 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -108,6 +108,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { override_queries: None, make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), + ice_file: None, }; let test_args = options.test_args.clone(); @@ -586,7 +587,7 @@ pub(crate) fn make_test( ); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let handler = Handler::with_emitter(false, None, Box::new(emitter), None); let sess = ParseSess::with_span_handler(handler, sm); let mut found_main = false; @@ -774,7 +775,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { TerminalUrl::No, ); - let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let handler = Handler::with_emitter(false, None, Box::new(emitter), None); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) { diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index f489f5081daa2..544cc07f27ad8 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -40,7 +40,7 @@ fn check_rust_syntax( let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle }; let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let handler = Handler::with_emitter(false, None, Box::new(emitter), None); let source = dox[code_block.code].to_owned(); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 87d88f707529c..7599c37b8d8ce 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -724,7 +724,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { false, TerminalUrl::No, ); - let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let handler = Handler::with_emitter(false, None, Box::new(emitter), None); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 81b5015dde33d..92d2425cd3b92 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -162,6 +162,7 @@ fn default_handler( ignore_path_set, can_reset, }), + None, ) } @@ -233,7 +234,7 @@ impl ParseSess { } pub(crate) fn set_silent_emitter(&mut self) { - self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter()); + self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter(), None); } pub(crate) fn span_to_filename(&self, span: Span) -> FileName { diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index ecc84c1618c0f..8a448c7ef6fce 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -246,6 +246,9 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "thiserror-impl", "thorin-dwp", "thread_local", + "time", + "time-core", + "time-macros", "tinystr", "tinyvec", "tinyvec_macros", diff --git a/tests/run-make-fulldeps/issue-19371/foo.rs b/tests/run-make-fulldeps/issue-19371/foo.rs index 9cca6200050bd..42a94b519cb9e 100644 --- a/tests/run-make-fulldeps/issue-19371/foo.rs +++ b/tests/run-make-fulldeps/issue-19371/foo.rs @@ -52,6 +52,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) { input, output_file: Some(OutFileName::Real(output)), output_dir: None, + ice_file: None, file_loader: None, locale_resources: &[], lint_caps: Default::default(), diff --git a/tests/run-make/exit-code/Makefile b/tests/run-make/exit-code/Makefile index 6458b71688fff..155e5cd112344 100644 --- a/tests/run-make/exit-code/Makefile +++ b/tests/run-make/exit-code/Makefile @@ -5,7 +5,7 @@ all: $(RUSTC) success.rs; [ $$? -eq 0 ] $(RUSTC) --invalid-arg-foo; [ $$? -eq 1 ] $(RUSTC) compile-error.rs; [ $$? -eq 1 ] - $(RUSTC) -Ztreat-err-as-bug compile-error.rs; [ $$? -eq 101 ] + RUSTC_ICE=0 $(RUSTC) -Ztreat-err-as-bug compile-error.rs; [ $$? -eq 101 ] $(RUSTDOC) -o $(TMPDIR)/exit-code success.rs; [ $$? -eq 0 ] $(RUSTDOC) --invalid-arg-foo; [ $$? -eq 1 ] $(RUSTDOC) compile-error.rs; [ $$? -eq 1 ] diff --git a/tests/run-make/short-ice/check.sh b/tests/run-make/short-ice/check.sh index 96cd8fe86bc30..435bce04e8e34 100644 --- a/tests/run-make/short-ice/check.sh +++ b/tests/run-make/short-ice/check.sh @@ -1,5 +1,5 @@ #!/bin/sh - +export RUSTC_ICE=0 RUST_BACKTRACE=1 $RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-1.log 2>&1 RUST_BACKTRACE=full $RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-2.log 2>&1 diff --git a/tests/rustdoc-ui/ice-bug-report-url.rs b/tests/rustdoc-ui/ice-bug-report-url.rs index 8ede91cf8f4f4..7689d78d31f33 100644 --- a/tests/rustdoc-ui/ice-bug-report-url.rs +++ b/tests/rustdoc-ui/ice-bug-report-url.rs @@ -1,4 +1,5 @@ // compile-flags: -Ztreat-err-as-bug +// rustc-env:RUSTC_ICE=0 // failure-status: 101 // error-pattern: aborting due to // error-pattern: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md diff --git a/tests/rustdoc-ui/ice-bug-report-url.stderr b/tests/rustdoc-ui/ice-bug-report-url.stderr index 98c08b9a8944b..7d9f05f8f4ed0 100644 --- a/tests/rustdoc-ui/ice-bug-report-url.stderr +++ b/tests/rustdoc-ui/ice-bug-report-url.stderr @@ -1,5 +1,5 @@ error: expected one of `->`, `where`, or `{`, found `` - --> $DIR/ice-bug-report-url.rs:13:10 + --> $DIR/ice-bug-report-url.rs:14:10 | LL | fn wrong() | ^ expected one of `->`, `where`, or `{` diff --git a/tests/ui/const-generics/late-bound-vars/in_closure.rs b/tests/ui/const-generics/late-bound-vars/in_closure.rs index 00fb535f04879..a6ac79506a536 100644 --- a/tests/ui/const-generics/late-bound-vars/in_closure.rs +++ b/tests/ui/const-generics/late-bound-vars/in_closure.rs @@ -1,4 +1,5 @@ // failure-status: 101 +// rustc-env:RUSTC_ICE=0 // known-bug: unknown // error-pattern:internal compiler error // normalize-stderr-test "internal compiler error.*" -> "" diff --git a/tests/ui/const-generics/late-bound-vars/simple.rs b/tests/ui/const-generics/late-bound-vars/simple.rs index 5d19aaf0b9555..518333f099eed 100644 --- a/tests/ui/const-generics/late-bound-vars/simple.rs +++ b/tests/ui/const-generics/late-bound-vars/simple.rs @@ -1,5 +1,6 @@ // failure-status: 101 // known-bug: unknown +// rustc-env:RUSTC_ICE=0 // error-pattern:internal compiler error // normalize-stderr-test "internal compiler error.*" -> "" // normalize-stderr-test "DefId\([^)]*\)" -> "..." diff --git a/tests/ui/consts/const-eval/const-eval-query-stack.rs b/tests/ui/consts/const-eval/const-eval-query-stack.rs index 81f28c1755deb..e9b2fb9d87c12 100644 --- a/tests/ui/consts/const-eval/const-eval-query-stack.rs +++ b/tests/ui/consts/const-eval/const-eval-query-stack.rs @@ -1,10 +1,12 @@ // compile-flags: -Ztreat-err-as-bug=1 // failure-status: 101 // rustc-env:RUST_BACKTRACE=1 +// rustc-env:RUSTC_ICE=0 // normalize-stderr-test "\nerror: .*unexpectedly panicked.*\n\n" -> "" // normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> "" // normalize-stderr-test "note: compiler flags.*\n\n" -> "" // normalize-stderr-test "note: rustc.*running on.*\n\n" -> "" +// normalize-stderr-test "note: please attach the file at `.*` to your bug report\n\n" -> "" // normalize-stderr-test "thread.*panicked.*\n" -> "" // normalize-stderr-test "stack backtrace:\n" -> "" // normalize-stderr-test "\s\d{1,}: .*\n" -> "" diff --git a/tests/ui/consts/const-eval/const-eval-query-stack.stderr b/tests/ui/consts/const-eval/const-eval-query-stack.stderr index 01fb8153cf384..f34cb71902fc6 100644 --- a/tests/ui/consts/const-eval/const-eval-query-stack.stderr +++ b/tests/ui/consts/const-eval/const-eval-query-stack.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const-eval-query-stack.rs:16:16 + --> $DIR/const-eval-query-stack.rs:18:16 | LL | const X: i32 = 1 / 0; | ^^^^^ attempt to divide `1_i32` by zero diff --git a/tests/ui/impl-trait/issues/issue-86800.rs b/tests/ui/impl-trait/issues/issue-86800.rs index ec4fda322d04f..6319ba4fd0cc5 100644 --- a/tests/ui/impl-trait/issues/issue-86800.rs +++ b/tests/ui/impl-trait/issues/issue-86800.rs @@ -2,6 +2,7 @@ // edition:2021 // compile-flags:-Z treat-err-as-bug=1 +// rustc-env:RUSTC_ICE=0 // error-pattern: aborting due to `-Z treat-err-as-bug=1` // failure-status:101 // normalize-stderr-test ".*note: .*\n\n" -> "" diff --git a/tests/ui/impl-trait/issues/issue-86800.stderr b/tests/ui/impl-trait/issues/issue-86800.stderr index facab390d1524..040b9281e47c9 100644 --- a/tests/ui/impl-trait/issues/issue-86800.stderr +++ b/tests/ui/impl-trait/issues/issue-86800.stderr @@ -1,5 +1,5 @@ error: unconstrained opaque type - --> $DIR/issue-86800.rs:31:34 + --> $DIR/issue-86800.rs:32:34 | LL | type TransactionFuture<'__, O> = impl '__ + Future>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/impl-trait/normalize-tait-in-const.rs b/tests/ui/impl-trait/normalize-tait-in-const.rs index d2e34c00b6422..151fc49e0e645 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.rs +++ b/tests/ui/impl-trait/normalize-tait-in-const.rs @@ -4,6 +4,7 @@ // normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" // normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " // rustc-env:RUST_BACKTRACE=0 +// rustc-env:RUSTC_ICE=0 #![feature(type_alias_impl_trait)] #![feature(const_trait_impl)] diff --git a/tests/ui/layout/valid_range_oob.rs b/tests/ui/layout/valid_range_oob.rs index 74aa47fe40549..35003dd6bf39e 100644 --- a/tests/ui/layout/valid_range_oob.rs +++ b/tests/ui/layout/valid_range_oob.rs @@ -2,6 +2,7 @@ // normalize-stderr-test "note: .*\n\n" -> "" // normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" // rustc-env:RUST_BACKTRACE=0 +// rustc-env:RUSTC_ICE=0 #![feature(rustc_attrs)] diff --git a/tests/ui/mir/validate/storage-live.rs b/tests/ui/mir/validate/storage-live.rs index ed3c26ed6da5a..34aed6a4ee95b 100644 --- a/tests/ui/mir/validate/storage-live.rs +++ b/tests/ui/mir/validate/storage-live.rs @@ -1,4 +1,5 @@ // compile-flags: -Zvalidate-mir -Ztreat-err-as-bug +// rustc-env:RUSTC_ICE=0 // failure-status: 101 // error-pattern: broken MIR in // error-pattern: StorageLive(_1) which already has storage here diff --git a/tests/ui/mir/validate/storage-live.stderr b/tests/ui/mir/validate/storage-live.stderr index 720fb0a90b08b..57292a239257c 100644 --- a/tests/ui/mir/validate/storage-live.stderr +++ b/tests/ui/mir/validate/storage-live.stderr @@ -1,6 +1,6 @@ error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (before pass CheckPackedRef) at bb0[1]: StorageLive(_1) which already has storage here - --> $DIR/storage-live.rs:22:13 + --> $DIR/storage-live.rs:23:13 | LL | StorageLive(a); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/panics/default-backtrace-ice.rs b/tests/ui/panics/default-backtrace-ice.rs index b40203c339d05..00f8bdf44578d 100644 --- a/tests/ui/panics/default-backtrace-ice.rs +++ b/tests/ui/panics/default-backtrace-ice.rs @@ -1,4 +1,5 @@ // unset-rustc-env:RUST_BACKTRACE +// rustc-env:RUSTC_ICE=0 // compile-flags:-Z treat-err-as-bug=1 // error-pattern:stack backtrace: // failure-status:101 diff --git a/tests/ui/panics/default-backtrace-ice.stderr b/tests/ui/panics/default-backtrace-ice.stderr index 815ce4dd01528..6e3ec24cfe232 100644 --- a/tests/ui/panics/default-backtrace-ice.stderr +++ b/tests/ui/panics/default-backtrace-ice.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `missing_ident` in this scope - --> $DIR/default-backtrace-ice.rs:21:13 + --> $DIR/default-backtrace-ice.rs:22:13 | LL | fn main() { missing_ident; } | ^^^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/recursion/issue-95134.rs b/tests/ui/recursion/issue-95134.rs index 2f1cffa2fa907..0071791ec013d 100644 --- a/tests/ui/recursion/issue-95134.rs +++ b/tests/ui/recursion/issue-95134.rs @@ -3,6 +3,7 @@ // compile-flags: -Copt-level=0 // dont-check-failure-status // dont-check-compiler-stderr +// rustc-env:RUSTC_ICE=0 pub fn encode_num(n: u32, mut writer: Writer) -> Result<(), Writer::Error> { if n > 15 { diff --git a/tests/ui/treat-err-as-bug/delay_span_bug.rs b/tests/ui/treat-err-as-bug/delay_span_bug.rs index 832afddf89147..a646092d16bac 100644 --- a/tests/ui/treat-err-as-bug/delay_span_bug.rs +++ b/tests/ui/treat-err-as-bug/delay_span_bug.rs @@ -1,4 +1,5 @@ // compile-flags: -Ztreat-err-as-bug +// rustc-env:RUSTC_ICE=0 // failure-status: 101 // error-pattern: aborting due to `-Z treat-err-as-bug=1` // error-pattern: [trigger_delay_span_bug] triggering a delay span bug diff --git a/tests/ui/treat-err-as-bug/delay_span_bug.stderr b/tests/ui/treat-err-as-bug/delay_span_bug.stderr index 22c6175048a63..bc48a6720a90b 100644 --- a/tests/ui/treat-err-as-bug/delay_span_bug.stderr +++ b/tests/ui/treat-err-as-bug/delay_span_bug.stderr @@ -1,5 +1,5 @@ error: internal compiler error: delayed span bug triggered by #[rustc_error(delay_span_bug_from_inside_query)] - --> $DIR/delay_span_bug.rs:12:1 + --> $DIR/delay_span_bug.rs:13:1 | LL | fn main() {} | ^^^^^^^^^ diff --git a/tests/ui/typeck/issue-103899.rs b/tests/ui/typeck/issue-103899.rs index ac9e4c716962f..e33be0950dac2 100644 --- a/tests/ui/typeck/issue-103899.rs +++ b/tests/ui/typeck/issue-103899.rs @@ -2,6 +2,7 @@ // failure-status: 101 // dont-check-compiler-stderr // known-bug: #103899 +// rustc-env:RUSTC_ICE=0 trait BaseWithAssoc { type Assoc;