1
Fork 0

Rollup merge of #121206 - nnethercote:top-level-error-handling, r=oli-obk

Top level error handling

The interactions between the following things are surprisingly complicated:
- `emit_stashed_diagnostics`,
- `flush_delayed`,
- normal return vs `abort_if_errors`/`FatalError.raise()` unwinding in the call to the closure in `interface::run_compiler`.

This PR disentangles it all.

r? `@oli-obk`
This commit is contained in:
Matthias Krüger 2024-02-21 22:48:56 +01:00 committed by GitHub
commit 5c89029585
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 174 additions and 155 deletions

View file

@ -3,7 +3,7 @@ use rustc_ast::CRATE_NODE_ID;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::memmap::Mmap; use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{DiagCtxt, ErrorGuaranteed}; use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError};
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::find_native_static_library; use rustc_metadata::find_native_static_library;
@ -487,7 +487,9 @@ fn collate_raw_dylibs<'a, 'b>(
} }
} }
} }
sess.compile_status()?; if let Some(guar) = sess.dcx().has_errors() {
return Err(guar);
}
Ok(dylib_table Ok(dylib_table
.into_iter() .into_iter()
.map(|(name, imports)| { .map(|(name, imports)| {
@ -720,10 +722,7 @@ fn link_dwarf_object<'a>(
Ok(()) Ok(())
}) { }) {
Ok(()) => {} Ok(()) => {}
Err(e) => { Err(e) => sess.dcx().emit_fatal(errors::ThorinErrorWrapper(e)),
sess.dcx().emit_err(errors::ThorinErrorWrapper(e));
sess.dcx().abort_if_errors();
}
} }
} }
@ -999,7 +998,7 @@ fn link_natively<'a>(
sess.dcx().emit_note(errors::CheckInstalledVisualStudio); sess.dcx().emit_note(errors::CheckInstalledVisualStudio);
sess.dcx().emit_note(errors::InsufficientVSCodeProduct); sess.dcx().emit_note(errors::InsufficientVSCodeProduct);
} }
sess.dcx().abort_if_errors(); FatalError.raise();
} }
} }

View file

@ -449,10 +449,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let Some(llfn) = cx.declare_c_main(llfty) else { let Some(llfn) = cx.declare_c_main(llfty) else {
// FIXME: We should be smart and show a better diagnostic here. // FIXME: We should be smart and show a better diagnostic here.
let span = cx.tcx().def_span(rust_main_def_id); let span = cx.tcx().def_span(rust_main_def_id);
let dcx = cx.tcx().dcx(); cx.tcx().dcx().emit_fatal(errors::MultipleMainFunctions { span });
dcx.emit_err(errors::MultipleMainFunctions { span });
dcx.abort_if_errors();
bug!();
}; };
// `main` should respect same config for frame pointer elimination as rest of code // `main` should respect same config for frame pointer elimination as rest of code

View file

@ -144,16 +144,6 @@ pub const EXIT_FAILURE: i32 = 1;
pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\ pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md"; ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T {
match result {
Err(..) => {
sess.dcx().abort_if_errors();
panic!("error reported but abort_if_errors didn't abort???");
}
Ok(x) => x,
}
}
pub trait Callbacks { pub trait Callbacks {
/// Called before creating the compiler instance /// Called before creating the compiler instance
fn config(&mut self, _config: &mut interface::Config) {} fn config(&mut self, _config: &mut interface::Config) {}
@ -349,27 +339,33 @@ fn run_compiler(
}, },
}; };
callbacks.config(&mut config);
default_early_dcx.abort_if_errors();
drop(default_early_dcx); drop(default_early_dcx);
callbacks.config(&mut config);
interface::run_compiler(config, |compiler| { interface::run_compiler(config, |compiler| {
let sess = &compiler.sess; let sess = &compiler.sess;
let codegen_backend = &*compiler.codegen_backend; let codegen_backend = &*compiler.codegen_backend;
// This is used for early exits unrelated to errors. E.g. when just
// printing some information without compiling, or exiting immediately
// after parsing, etc.
let early_exit = || {
if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) }
};
// This implements `-Whelp`. It should be handled very early, like // This implements `-Whelp`. It should be handled very early, like
// `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because // `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because
// it must happen after lints are registered, during session creation. // it must happen after lints are registered, during session creation.
if sess.opts.describe_lints { if sess.opts.describe_lints {
describe_lints(sess); describe_lints(sess);
return sess.compile_status(); return early_exit();
} }
let early_dcx = EarlyDiagCtxt::new(sess.opts.error_format); let early_dcx = EarlyDiagCtxt::new(sess.opts.error_format);
if print_crate_info(&early_dcx, codegen_backend, sess, has_input) == Compilation::Stop { if print_crate_info(&early_dcx, codegen_backend, sess, has_input) == Compilation::Stop {
return sess.compile_status(); return early_exit();
} }
if !has_input { if !has_input {
@ -378,16 +374,16 @@ fn run_compiler(
if !sess.opts.unstable_opts.ls.is_empty() { if !sess.opts.unstable_opts.ls.is_empty() {
list_metadata(&early_dcx, sess, &*codegen_backend.metadata_loader()); list_metadata(&early_dcx, sess, &*codegen_backend.metadata_loader());
return sess.compile_status(); return early_exit();
} }
if sess.opts.unstable_opts.link_only { if sess.opts.unstable_opts.link_only {
process_rlink(sess, compiler); process_rlink(sess, compiler);
return sess.compile_status(); return early_exit();
} }
let linker = compiler.enter(|queries| { let linker = compiler.enter(|queries| {
let early_exit = || sess.compile_status().map(|_| None); let early_exit = || early_exit().map(|_| None);
queries.parse()?; queries.parse()?;
if let Some(ppm) = &sess.opts.pretty { if let Some(ppm) = &sess.opts.pretty {
@ -659,10 +655,11 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) {
}; };
} }
}; };
let result = compiler.codegen_backend.link(sess, codegen_results, &outputs); if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() {
abort_on_err(result, sess); FatalError.raise();
}
} else { } else {
dcx.emit_fatal(RlinkNotAFile {}) dcx.emit_fatal(RlinkNotAFile {});
} }
} }

View file

@ -2,6 +2,7 @@
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast_pretty::pprust as pprust_ast; use rustc_ast_pretty::pprust as pprust_ast;
use rustc_errors::FatalError;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir_pretty as pprust_hir; use rustc_hir_pretty as pprust_hir;
use rustc_middle::bug; use rustc_middle::bug;
@ -18,7 +19,6 @@ use std::fmt::Write;
pub use self::PpMode::*; pub use self::PpMode::*;
pub use self::PpSourceMode::*; pub use self::PpSourceMode::*;
use crate::abort_on_err;
struct AstNoAnn; struct AstNoAnn;
@ -243,7 +243,9 @@ impl<'tcx> PrintExtra<'tcx> {
pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
if ppm.needs_analysis() { if ppm.needs_analysis() {
abort_on_err(ex.tcx().analysis(()), sess); if ex.tcx().analysis(()).is_err() {
FatalError.raise();
}
} }
let (src, src_name) = get_source(sess); let (src, src_name) = get_source(sess);
@ -334,7 +336,9 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
ThirTree => { ThirTree => {
let tcx = ex.tcx(); let tcx = ex.tcx();
let mut out = String::new(); let mut out = String::new();
abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess); if rustc_hir_analysis::check_crate(tcx).is_err() {
FatalError.raise();
}
debug!("pretty printing THIR tree"); debug!("pretty printing THIR tree");
for did in tcx.hir().body_owners() { for did in tcx.hir().body_owners() {
let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_tree(did)); let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_tree(did));
@ -344,7 +348,9 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
ThirFlat => { ThirFlat => {
let tcx = ex.tcx(); let tcx = ex.tcx();
let mut out = String::new(); let mut out = String::new();
abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess); if rustc_hir_analysis::check_crate(tcx).is_err() {
FatalError.raise();
}
debug!("pretty printing THIR flat"); debug!("pretty printing THIR flat");
for did in tcx.hir().body_owners() { for did in tcx.hir().body_owners() {
let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_flat(did)); let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_flat(did));

View file

@ -471,9 +471,10 @@ struct DiagCtxtInner {
emitted_diagnostics: FxHashSet<Hash128>, emitted_diagnostics: FxHashSet<Hash128>,
/// Stashed diagnostics emitted in one stage of the compiler that may be /// Stashed diagnostics emitted in one stage of the compiler that may be
/// stolen by other stages (e.g. to improve them and add more information). /// stolen and emitted/cancelled by other stages (e.g. to improve them and
/// The stashed diagnostics count towards the total error count. /// add more information). All stashed diagnostics must be emitted with
/// When `.abort_if_errors()` is called, these are also emitted. /// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
/// otherwise an assertion failure will occur.
stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>, stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>,
future_breakage_diagnostics: Vec<Diagnostic>, future_breakage_diagnostics: Vec<Diagnostic>,
@ -558,7 +559,9 @@ pub struct DiagCtxtFlags {
impl Drop for DiagCtxtInner { impl Drop for DiagCtxtInner {
fn drop(&mut self) { fn drop(&mut self) {
self.emit_stashed_diagnostics(); // Any stashed diagnostics should have been handled by
// `emit_stashed_diagnostics` by now.
assert!(self.stashed_diagnostics.is_empty());
if self.err_guars.is_empty() { if self.err_guars.is_empty() {
self.flush_delayed() self.flush_delayed()
@ -750,17 +753,24 @@ impl DiagCtxt {
} }
/// Emit all stashed diagnostics. /// Emit all stashed diagnostics.
pub fn emit_stashed_diagnostics(&self) { pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow_mut().emit_stashed_diagnostics() self.inner.borrow_mut().emit_stashed_diagnostics()
} }
/// This excludes lint errors, delayed bugs, and stashed errors. /// This excludes lint errors, delayed bugs and stashed errors.
#[inline] #[inline]
pub fn err_count(&self) -> usize { pub fn err_count_excluding_lint_errs(&self) -> usize {
self.inner.borrow().err_guars.len() self.inner.borrow().err_guars.len()
} }
/// This excludes normal errors, lint errors and delayed bugs. Unless /// This excludes delayed bugs and stashed errors.
#[inline]
pub fn err_count(&self) -> usize {
let inner = self.inner.borrow();
inner.err_guars.len() + inner.lint_err_guars.len()
}
/// This excludes normal errors, lint errors, and delayed bugs. Unless
/// absolutely necessary, avoid using this. It's dubious because stashed /// absolutely necessary, avoid using this. It's dubious because stashed
/// errors can later be cancelled, so the presence of a stashed error at /// errors can later be cancelled, so the presence of a stashed error at
/// some point of time doesn't guarantee anything -- there are no /// some point of time doesn't guarantee anything -- there are no
@ -769,27 +779,29 @@ impl DiagCtxt {
self.inner.borrow().stashed_err_count self.inner.borrow().stashed_err_count
} }
/// This excludes lint errors, delayed bugs, and stashed errors. /// This excludes lint errors, delayed bugs, and stashed errors. Unless
/// absolutely necessary, prefer `has_errors` to this method.
pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow().has_errors_excluding_lint_errors()
}
/// This excludes delayed bugs and stashed errors.
pub fn has_errors(&self) -> Option<ErrorGuaranteed> { pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow().has_errors() self.inner.borrow().has_errors()
} }
/// This excludes delayed bugs and stashed errors. Unless absolutely
/// necessary, prefer `has_errors` to this method.
pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow().has_errors_or_lint_errors()
}
/// This excludes stashed errors. Unless absolutely necessary, prefer /// This excludes stashed errors. Unless absolutely necessary, prefer
/// `has_errors` or `has_errors_or_lint_errors` to this method. /// `has_errors` to this method.
pub fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> { pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow().has_errors_or_lint_errors_or_delayed_bugs() self.inner.borrow().has_errors_or_delayed_bugs()
} }
pub fn print_error_count(&self, registry: &Registry) { pub fn print_error_count(&self, registry: &Registry) {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
inner.emit_stashed_diagnostics(); // Any stashed diagnostics should have been handled by
// `emit_stashed_diagnostics` by now.
assert!(inner.stashed_diagnostics.is_empty());
if inner.treat_err_as_bug() { if inner.treat_err_as_bug() {
return; return;
@ -864,10 +876,12 @@ impl DiagCtxt {
} }
} }
/// This excludes delayed bugs and stashed errors. Used for early aborts
/// after errors occurred -- e.g. because continuing in the face of errors is
/// likely to lead to bad results, such as spurious/uninteresting
/// additional errors -- when returning an error `Result` is difficult.
pub fn abort_if_errors(&self) { pub fn abort_if_errors(&self) {
let mut inner = self.inner.borrow_mut(); if self.has_errors().is_some() {
inner.emit_stashed_diagnostics();
if !inner.err_guars.is_empty() {
FatalError.raise(); FatalError.raise();
} }
} }
@ -1268,10 +1282,10 @@ impl DiagCtxt {
// `DiagCtxtInner::foo`. // `DiagCtxtInner::foo`.
impl DiagCtxtInner { impl DiagCtxtInner {
/// Emit all stashed diagnostics. /// Emit all stashed diagnostics.
fn emit_stashed_diagnostics(&mut self) { fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
let mut guar = None;
let has_errors = !self.err_guars.is_empty(); let has_errors = !self.err_guars.is_empty();
for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
// Decrement the count tracking the stash; emitting will increment it.
if diag.is_error() { if diag.is_error() {
if diag.is_lint.is_none() { if diag.is_lint.is_none() {
self.stashed_err_count -= 1; self.stashed_err_count -= 1;
@ -1284,8 +1298,9 @@ impl DiagCtxtInner {
continue; continue;
} }
} }
self.emit_diagnostic(diag); guar = guar.or(self.emit_diagnostic(diag));
} }
guar
} }
// Return value is only `Some` if the level is `Error` or `DelayedBug`. // Return value is only `Some` if the level is `Error` or `DelayedBug`.
@ -1329,7 +1344,7 @@ impl DiagCtxtInner {
DelayedBug => { DelayedBug => {
// If we have already emitted at least one error, we don't need // If we have already emitted at least one error, we don't need
// to record the delayed bug, because it'll never be used. // to record the delayed bug, because it'll never be used.
return if let Some(guar) = self.has_errors_or_lint_errors() { return if let Some(guar) = self.has_errors() {
Some(guar) Some(guar)
} else { } else {
let backtrace = std::backtrace::Backtrace::capture(); let backtrace = std::backtrace::Backtrace::capture();
@ -1445,17 +1460,16 @@ impl DiagCtxtInner {
.is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get()) .is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get())
} }
fn has_errors(&self) -> Option<ErrorGuaranteed> { fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
self.err_guars.get(0).copied() self.err_guars.get(0).copied()
} }
fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> { fn has_errors(&self) -> Option<ErrorGuaranteed> {
self.has_errors().or_else(|| self.lint_err_guars.get(0).copied()) self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied())
} }
fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> { fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
self.has_errors_or_lint_errors() self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
.or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
} }
/// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`. /// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`.
@ -1488,6 +1502,11 @@ impl DiagCtxtInner {
} }
fn flush_delayed(&mut self) { fn flush_delayed(&mut self) {
// Stashed diagnostics must be emitted before delayed bugs are flushed.
// Otherwise, we might ICE prematurely when errors would have
// eventually happened.
assert!(self.stashed_diagnostics.is_empty());
if self.delayed_bugs.is_empty() { if self.delayed_bugs.is_empty() {
return; return;
} }

View file

@ -312,7 +312,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone(); let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone();
if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() { if sess.dcx().has_errors_or_delayed_bugs().is_some() {
// If there have been any errors during compilation, we don't want to // If there have been any errors during compilation, we don't want to
// publish this session directory. Rather, we'll just delete it. // publish this session directory. Rather, we'll just delete it.

View file

@ -32,7 +32,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
return; return;
} }
// This is going to be deleted in finalize_session_directory, so let's not create it. // This is going to be deleted in finalize_session_directory, so let's not create it.
if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() { if sess.dcx().has_errors_or_delayed_bugs().is_some() {
return; return;
} }
@ -87,7 +87,7 @@ pub fn save_work_product_index(
return; return;
} }
// This is going to be deleted in finalize_session_directory, so let's not create it // This is going to be deleted in finalize_session_directory, so let's not create it
if sess.dcx().has_errors_or_lint_errors().is_some() { if sess.dcx().has_errors().is_some() {
return; return;
} }

View file

@ -712,7 +712,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
reported_trait_errors: Default::default(), reported_trait_errors: Default::default(),
reported_signature_mismatch: Default::default(), reported_signature_mismatch: Default::default(),
tainted_by_errors: Cell::new(None), tainted_by_errors: Cell::new(None),
err_count_on_creation: tcx.dcx().err_count(), err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(),
stashed_err_count_on_creation: tcx.dcx().stashed_err_count(), stashed_err_count_on_creation: tcx.dcx().stashed_err_count(),
universe: Cell::new(ty::UniverseIndex::ROOT), universe: Cell::new(ty::UniverseIndex::ROOT),
intercrate, intercrate,
@ -1267,8 +1267,11 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> { pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
if let Some(guar) = self.tainted_by_errors.get() { if let Some(guar) = self.tainted_by_errors.get() {
Some(guar) Some(guar)
} else if self.dcx().err_count() > self.err_count_on_creation { } else if self.dcx().err_count_excluding_lint_errs() > self.err_count_on_creation {
// Errors reported since this infcx was made. // Errors reported since this infcx was made. Lint errors are
// excluded to avoid some being swallowed in the presence of
// non-lint errors. (It's arguable whether or not this exclusion is
// important.)
let guar = self.dcx().has_errors().unwrap(); let guar = self.dcx().has_errors().unwrap();
self.set_tainted_by_errors(guar); self.set_tainted_by_errors(guar);
Some(guar) Some(guar)

View file

@ -423,18 +423,43 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
Compiler { sess, codegen_backend, override_queries: config.override_queries }; Compiler { sess, codegen_backend, override_queries: config.override_queries };
rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || { rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
let r = { // There are two paths out of `f`.
let _sess_abort_error = defer(|| { // - Normal exit.
compiler.sess.finish_diagnostics(&config.registry); // - Panic, e.g. triggered by `abort_if_errors`.
//
// We must run `finish_diagnostics` in both cases.
let res = {
// If `f` panics, `finish_diagnostics` will run during
// unwinding because of the `defer`.
let mut guar = None;
let sess_abort_guard = defer(|| {
guar = compiler.sess.finish_diagnostics(&config.registry);
}); });
f(&compiler) let res = f(&compiler);
// If `f` doesn't panic, `finish_diagnostics` will run
// normally when `sess_abort_guard` is dropped.
drop(sess_abort_guard);
// If `finish_diagnostics` emits errors (e.g. stashed
// errors) we can't return an error directly, because the
// return type of this function is `R`, not `Result<R, E>`.
// But we need to communicate the errors' existence to the
// caller, otherwise the caller might mistakenly think that
// no errors occurred and return a zero exit code. So we
// abort (panic) instead, similar to if `f` had panicked.
if guar.is_some() {
compiler.sess.dcx().abort_if_errors();
}
res
}; };
let prof = compiler.sess.prof.clone(); let prof = compiler.sess.prof.clone();
prof.generic_activity("drop_compiler").run(move || drop(compiler)); prof.generic_activity("drop_compiler").run(move || drop(compiler));
r
res
}) })
}, },
) )

View file

@ -772,12 +772,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
// lot of annoying errors in the ui tests (basically, // lot of annoying errors in the ui tests (basically,
// lint warnings and so on -- kindck used to do this abort, but // lint warnings and so on -- kindck used to do this abort, but
// kindck is gone now). -nmatsakis // kindck is gone now). -nmatsakis
if let Some(reported) = sess.dcx().has_errors() { //
return Err(reported); // But we exclude lint errors from this, because lint errors are typically
} else if sess.dcx().stashed_err_count() > 0 { // less serious and we're more likely to want to continue (#87337).
// Without this case we sometimes get delayed bug ICEs and I don't if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() {
// understand why. -nnethercote return Err(guar);
return Err(sess.dcx().delayed_bug("some stashed error is waiting for use"));
} }
sess.time("misc_checking_3", || { sess.time("misc_checking_3", || {
@ -937,9 +936,7 @@ pub fn start_codegen<'tcx>(
if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) {
if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) { if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) {
let dcx = tcx.dcx(); tcx.dcx().emit_fatal(errors::CantEmitMIR { error });
dcx.emit_err(errors::CantEmitMIR { error });
dcx.abort_if_errors();
} }
} }

View file

@ -222,12 +222,12 @@ impl<'tcx> Queries<'tcx> {
pub fn codegen_and_build_linker(&'tcx self) -> Result<Linker> { pub fn codegen_and_build_linker(&'tcx self) -> Result<Linker> {
self.global_ctxt()?.enter(|tcx| { self.global_ctxt()?.enter(|tcx| {
// Don't do code generation if there were any errors // Don't do code generation if there were any errors. Likewise if
self.compiler.sess.compile_status()?; // there were any delayed bugs, because codegen will likely cause
// more ICEs, obscuring the original problem.
// If we have any delayed bugs, for example because we created TyKind::Error earlier, if let Some(guar) = self.compiler.sess.dcx().has_errors_or_delayed_bugs() {
// it's likely that codegen will only cause more ICEs, obscuring the original problem return Err(guar);
self.compiler.sess.dcx().flush_delayed(); }
// Hook for UI tests. // Hook for UI tests.
Self::check_for_rustc_errors_attr(tcx); Self::check_for_rustc_errors_attr(tcx);
@ -261,7 +261,9 @@ impl Linker {
let (codegen_results, work_products) = let (codegen_results, work_products) =
codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames); codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames);
sess.compile_status()?; if let Some(guar) = sess.dcx().has_errors() {
return Err(guar);
}
sess.time("serialize_work_products", || { sess.time("serialize_work_products", || {
rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products) rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products)

View file

@ -926,7 +926,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
what: &str, what: &str,
needs_dep: &dyn Fn(&CrateMetadata) -> bool, needs_dep: &dyn Fn(&CrateMetadata) -> bool,
) { ) {
// don't perform this validation if the session has errors, as one of // Don't perform this validation if the session has errors, as one of
// those errors may indicate a circular dependency which could cause // those errors may indicate a circular dependency which could cause
// this to stack overflow. // this to stack overflow.
if self.dcx().has_errors().is_some() { if self.dcx().has_errors().is_some() {

View file

@ -153,11 +153,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
) -> Self::Const { ) -> Self::Const {
Const::new_bound(self, debruijn, var, ty) Const::new_bound(self, debruijn, var, ty)
} }
fn expect_error_or_delayed_bug() {
let has_errors = ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs());
assert!(has_errors.is_some());
}
} }
type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>; type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;

View file

@ -237,7 +237,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
) { ) {
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
ty::Adt(adt, _) => adt.variant_of_res(res), ty::Adt(adt, _) => adt.variant_of_res(res),
_ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"), _ => {
self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
return;
}
}; };
let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len()); let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len());
let first_n = pats.iter().enumerate().take(dotdot); let first_n = pats.iter().enumerate().take(dotdot);

View file

@ -817,7 +817,7 @@ impl<D: Deps> DepGraphData<D> {
None => {} None => {}
} }
if let None = qcx.dep_context().sess().dcx().has_errors_or_lint_errors_or_delayed_bugs() { if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() {
panic!("try_mark_previous_green() - Forcing the DepNode should have set its color") panic!("try_mark_previous_green() - Forcing the DepNode should have set its color")
} }

View file

@ -6,6 +6,7 @@ use crate::errors::{
}; };
use crate::Session; use crate::Session;
use rustc_ast::{self as ast, attr}; use rustc_ast::{self as ast, attr};
use rustc_errors::FatalError;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use std::path::Path; use std::path::Path;
@ -115,7 +116,7 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
} }
if err_count > 0 { if err_count > 0 {
sess.dcx().abort_if_errors(); FatalError.raise();
} }
} }

View file

@ -258,7 +258,8 @@ impl Session {
} }
} }
fn check_miri_unleashed_features(&self) { fn check_miri_unleashed_features(&self) -> Option<ErrorGuaranteed> {
let mut guar = None;
let unleashed_features = self.miri_unleashed_features.lock(); let unleashed_features = self.miri_unleashed_features.lock();
if !unleashed_features.is_empty() { if !unleashed_features.is_empty() {
let mut must_err = false; let mut must_err = false;
@ -279,18 +280,22 @@ impl Session {
// If we should err, make sure we did. // If we should err, make sure we did.
if must_err && self.dcx().has_errors().is_none() { if must_err && self.dcx().has_errors().is_none() {
// We have skipped a feature gate, and not run into other errors... reject. // We have skipped a feature gate, and not run into other errors... reject.
self.dcx().emit_err(errors::NotCircumventFeature); guar = Some(self.dcx().emit_err(errors::NotCircumventFeature));
} }
} }
guar
} }
/// Invoked all the way at the end to finish off diagnostics printing. /// Invoked all the way at the end to finish off diagnostics printing.
pub fn finish_diagnostics(&self, registry: &Registry) { pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> {
self.check_miri_unleashed_features(); let mut guar = None;
guar = guar.or(self.check_miri_unleashed_features());
guar = guar.or(self.dcx().emit_stashed_diagnostics());
self.dcx().print_error_count(registry); self.dcx().print_error_count(registry);
if self.opts.json_future_incompat { if self.opts.json_future_incompat {
self.dcx().emit_future_breakage_report(); self.dcx().emit_future_breakage_report();
} }
guar
} }
/// Returns true if the crate is a testing one. /// Returns true if the crate is a testing one.
@ -312,16 +317,6 @@ impl Session {
err err
} }
pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> {
// We must include lint errors here.
if let Some(reported) = self.dcx().has_errors_or_lint_errors() {
self.dcx().emit_stashed_diagnostics();
Err(reported)
} else {
Ok(())
}
}
/// Record the fact that we called `trimmed_def_paths`, and do some /// Record the fact that we called `trimmed_def_paths`, and do some
/// checking about whether its cost was justified. /// checking about whether its cost was justified.
pub fn record_trimmed_def_paths(&self) { pub fn record_trimmed_def_paths(&self) {
@ -1410,10 +1405,6 @@ impl EarlyDiagCtxt {
Self { dcx: DiagCtxt::with_emitter(emitter) } Self { dcx: DiagCtxt::with_emitter(emitter) }
} }
pub fn abort_if_errors(&self) {
self.dcx.abort_if_errors()
}
/// Swap out the underlying dcx once we acquire the user's preference on error emission /// Swap out the underlying dcx once we acquire the user's preference on error emission
/// format. Any errors prior to that will cause an abort and all stashed diagnostics of the /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the
/// previous dcx will be emitted. /// previous dcx will be emitted.

View file

@ -22,7 +22,7 @@ use crate::traits::{
use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::{ use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan, StashKey, StringPart, FatalError, MultiSpan, StashKey, StringPart,
}; };
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def::{DefKind, Namespace, Res};
@ -193,14 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit); let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit);
mutate(&mut err); mutate(&mut err);
err.emit(); err.emit();
FatalError.raise();
self.dcx().abort_if_errors();
// FIXME: this should be something like `build_overflow_error_fatal`, which returns
// `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`.
unreachable!(
"did not expect compilation to continue after `abort_if_errors`, \
since an error was definitely emitted!"
);
} }
fn build_overflow_error<T>( fn build_overflow_error<T>(

View file

@ -95,9 +95,6 @@ pub trait Interner: Sized {
fn mk_bound_ty(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Ty; fn mk_bound_ty(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Ty;
fn mk_bound_region(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Region; fn mk_bound_region(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Region;
fn mk_bound_const(self, debruijn: DebruijnIndex, var: BoundVar, ty: Self::Ty) -> Self::Const; fn mk_bound_const(self, debruijn: DebruijnIndex, var: BoundVar, ty: Self::Ty) -> Self::Const;
/// Assert that an error has been delayed or emitted.
fn expect_error_or_delayed_bug();
} }
/// Common capabilities of placeholder kinds /// Common capabilities of placeholder kinds

View file

@ -3,7 +3,7 @@ use rustc_data_structures::sync::Lrc;
use rustc_data_structures::unord::UnordSet; use rustc_data_structures::unord::UnordSet;
use rustc_errors::emitter::{DynEmitter, HumanEmitter}; use rustc_errors::emitter::{DynEmitter, HumanEmitter};
use rustc_errors::json::JsonEmitter; use rustc_errors::json::JsonEmitter;
use rustc_errors::{codes::*, TerminalUrl}; use rustc_errors::{codes::*, ErrorGuaranteed, TerminalUrl};
use rustc_feature::UnstableFeatures; use rustc_feature::UnstableFeatures;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
@ -306,7 +306,7 @@ pub(crate) fn run_global_ctxt(
show_coverage: bool, show_coverage: bool,
render_options: RenderOptions, render_options: RenderOptions,
output_format: OutputFormat, output_format: OutputFormat,
) -> (clean::Crate, RenderOptions, Cache) { ) -> Result<(clean::Crate, RenderOptions, Cache), ErrorGuaranteed> {
// Certain queries assume that some checks were run elsewhere // Certain queries assume that some checks were run elsewhere
// (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425), // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425),
// so type-check everything other than function bodies in this crate before running lints. // so type-check everything other than function bodies in this crate before running lints.
@ -331,7 +331,10 @@ pub(crate) fn run_global_ctxt(
}); });
}); });
tcx.dcx().abort_if_errors(); if let Some(guar) = tcx.dcx().has_errors() {
return Err(guar);
}
tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx)); tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx));
tcx.sess.time("check_mod_attrs", || { tcx.sess.time("check_mod_attrs", || {
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_attrs(module)) tcx.hir().for_each_module(|module| tcx.ensure().check_mod_attrs(module))
@ -452,14 +455,13 @@ pub(crate) fn run_global_ctxt(
tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc))); tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc)));
// We must include lint errors here. if let Some(guar) = tcx.dcx().has_errors() {
if tcx.dcx().has_errors_or_lint_errors().is_some() { return Err(guar);
rustc_errors::FatalError.raise();
} }
krate = tcx.sess.time("create_format_cache", || Cache::populate(&mut ctxt, krate)); krate = tcx.sess.time("create_format_cache", || Cache::populate(&mut ctxt, krate));
(krate, ctxt.render_options, ctxt.cache) Ok((krate, ctxt.render_options, ctxt.cache))
} }
/// Due to <https://github.com/rust-lang/rust/pull/73566>, /// Due to <https://github.com/rust-lang/rust/pull/73566>,

View file

@ -153,8 +153,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
collector collector
}); });
// We must include lint errors here. if compiler.sess.dcx().has_errors().is_some() {
if compiler.sess.dcx().has_errors_or_lint_errors().is_some() {
FatalError.raise(); FatalError.raise();
} }

View file

@ -78,8 +78,7 @@ use std::io::{self, IsTerminal};
use std::process; use std::process;
use std::sync::{atomic::AtomicBool, Arc}; use std::sync::{atomic::AtomicBool, Arc};
use rustc_driver::abort_on_err; use rustc_errors::{ErrorGuaranteed, FatalError};
use rustc_errors::ErrorGuaranteed;
use rustc_interface::interface; use rustc_interface::interface;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup};
@ -779,7 +778,7 @@ fn main_args(
} }
compiler.enter(|queries| { compiler.enter(|queries| {
let mut gcx = abort_on_err(queries.global_ctxt(), sess); let Ok(mut gcx) = queries.global_ctxt() else { FatalError.raise() };
if sess.dcx().has_errors().is_some() { if sess.dcx().has_errors().is_some() {
sess.dcx().fatal("Compilation failed, aborting rustdoc"); sess.dcx().fatal("Compilation failed, aborting rustdoc");
} }
@ -787,7 +786,7 @@ fn main_args(
gcx.enter(|tcx| { gcx.enter(|tcx| {
let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || { let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
core::run_global_ctxt(tcx, show_coverage, render_options, output_format) core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
}); })?;
info!("finished with rustc"); info!("finished with rustc");
if let Some(options) = scrape_examples_options { if let Some(options) = scrape_examples_options {

View file

@ -68,7 +68,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
queries: &'tcx rustc_interface::Queries<'tcx>, queries: &'tcx rustc_interface::Queries<'tcx>,
) -> Compilation { ) -> Compilation {
queries.global_ctxt().unwrap().enter(|tcx| { queries.global_ctxt().unwrap().enter(|tcx| {
if tcx.sess.compile_status().is_err() { if tcx.sess.dcx().has_errors().is_some() {
tcx.dcx().fatal("miri cannot be run on programs that fail compilation"); tcx.dcx().fatal("miri cannot be run on programs that fail compilation");
} }

View file

@ -21,5 +21,3 @@ LL | same_output(foo, rpit);
query stack during panic: query stack during panic:
end of query stack end of query stack
error: aborting due to 2 previous errors

View file

@ -12,5 +12,3 @@ LL | builder.state().on_entry(|_| {});
query stack during panic: query stack during panic:
end of query stack end of query stack
error: aborting due to 1 previous error

View file

@ -21,5 +21,3 @@ LL | query(get_rpit);
query stack during panic: query stack during panic:
end of query stack end of query stack
error: aborting due to 2 previous errors