Skip to content

Commit b2e6fe1

Browse files
committed
Overhaul the handling of errors at the top-level.
Currently `emit_stashed_diagnostic` is called from four(!) different places: `print_error_count`, `DiagCtxtInner::drop`, `abort_if_errors`, and `compile_status`. And `flush_delayed` is called from two different places: `DiagCtxtInner::drop` and `Queries`. This is pretty gross! Each one should really be called from a single place, but there's a bunch of entanglements. This commit cleans up this mess. Specifically, it: - Removes all the existing calls to `emit_stashed_diagnostic`, and adds a single new call in `finish_diagnostics`. - Removes the early `flush_delayed` call in `codegen_and_build_linker`, replacing it with a simple early return if delayed bugs are present. - Changes `DiagCtxtInner::drop` and `DiagCtxtInner::flush_delayed` so they both assert that the stashed diagnostics are empty (i.e. processed beforehand). - Changes `interface::run_compiler` so that any errors emitted during `finish_diagnostics` (i.e. late-emitted stashed diagnostics) are counted and cannot be overlooked. This requires adding `ErrorGuaranteed` return values to several functions. - Removes the `stashed_err_count` call in `analysis`. This is possible now that we don't have to worry about calling `flush_delayed` early from `codegen_and_build_linker` when stashed diagnostics are pending. - Changes the `span_bug` case in `handle_tuple_field_pattern_match` to a `delayed_span_bug`, because it now can be reached due to the removal of the `stashed_err_count` call in `analysis`. - Slightly changes the expected output of three tests. If no errors are emitted but there are delayed bugs, the error count is no longer printed. This is because delayed bugs are now always printed after the error count is printed (or not printed, if the error count is zero). There is a lot going on in this commit. It's hard to break into smaller pieces because the existing code is very tangled. It took me a long time and a lot of effort to understand how the different pieces interact, and I think the new code is a lot simpler and easier to understand.
1 parent 6fa6f55 commit b2e6fe1

File tree

9 files changed

+73
-42
lines changed

9 files changed

+73
-42
lines changed

compiler/rustc_errors/src/lib.rs

+18-12
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,10 @@ struct DiagCtxtInner {
476476
emitted_diagnostics: FxHashSet<Hash128>,
477477

478478
/// Stashed diagnostics emitted in one stage of the compiler that may be
479-
/// stolen by other stages (e.g. to improve them and add more information).
480-
/// The stashed diagnostics count towards the total error count.
481-
/// When `.abort_if_errors()` is called, these are also emitted.
479+
/// stolen and emitted/cancelled by other stages (e.g. to improve them and
480+
/// add more information). All stashed diagnostics must be emitted with
481+
/// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
482+
/// otherwise an assertion failure will occur.
482483
stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>,
483484

484485
future_breakage_diagnostics: Vec<Diagnostic>,
@@ -563,7 +564,9 @@ pub struct DiagCtxtFlags {
563564

564565
impl Drop for DiagCtxtInner {
565566
fn drop(&mut self) {
566-
self.emit_stashed_diagnostics();
567+
// Any stashed diagnostics should have been handled by
568+
// `emit_stashed_diagnostics` by now.
569+
assert!(self.stashed_diagnostics.is_empty());
567570

568571
if self.err_guars.is_empty() {
569572
self.flush_delayed()
@@ -755,7 +758,7 @@ impl DiagCtxt {
755758
}
756759

757760
/// Emit all stashed diagnostics.
758-
pub fn emit_stashed_diagnostics(&self) {
761+
pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> {
759762
self.inner.borrow_mut().emit_stashed_diagnostics()
760763
}
761764

@@ -801,8 +804,6 @@ impl DiagCtxt {
801804
pub fn print_error_count(&self, registry: &Registry) {
802805
let mut inner = self.inner.borrow_mut();
803806

804-
inner.emit_stashed_diagnostics();
805-
806807
if inner.treat_err_as_bug() {
807808
return;
808809
}
@@ -877,9 +878,7 @@ impl DiagCtxt {
877878
}
878879

879880
pub fn abort_if_errors(&self) {
880-
let mut inner = self.inner.borrow_mut();
881-
inner.emit_stashed_diagnostics();
882-
if !inner.err_guars.is_empty() {
881+
if self.has_errors().is_some() {
883882
FatalError.raise();
884883
}
885884
}
@@ -1280,7 +1279,8 @@ impl DiagCtxt {
12801279
// `DiagCtxtInner::foo`.
12811280
impl DiagCtxtInner {
12821281
/// Emit all stashed diagnostics.
1283-
fn emit_stashed_diagnostics(&mut self) {
1282+
fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
1283+
let mut guar = None;
12841284
let has_errors = !self.err_guars.is_empty();
12851285
for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
12861286
if diag.is_error() {
@@ -1295,8 +1295,9 @@ impl DiagCtxtInner {
12951295
continue;
12961296
}
12971297
}
1298-
self.emit_diagnostic(diag);
1298+
guar = guar.or(self.emit_diagnostic(diag));
12991299
}
1300+
guar
13001301
}
13011302

13021303
// Return value is only `Some` if the level is `Error` or `DelayedBug`.
@@ -1489,6 +1490,11 @@ impl DiagCtxtInner {
14891490
}
14901491

14911492
fn flush_delayed(&mut self) {
1493+
// Stashed diagnostics must be emitted before delayed bugs are flushed.
1494+
// Otherwise, we might ICE prematurely when errors would have
1495+
// eventually happened.
1496+
assert!(self.stashed_diagnostics.is_empty());
1497+
14921498
if self.delayed_bugs.is_empty() {
14931499
return;
14941500
}

compiler/rustc_interface/src/interface.rs

+31-6
Original file line numberDiff line numberDiff line change
@@ -423,18 +423,43 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
423423
Compiler { sess, codegen_backend, override_queries: config.override_queries };
424424

425425
rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
426-
let r = {
427-
let _sess_abort_error = defer(|| {
428-
compiler.sess.finish_diagnostics(&config.registry);
426+
// There are two paths out of `f`.
427+
// - Normal exit.
428+
// - Panic, e.g. triggered by `abort_if_errors`.
429+
//
430+
// We must run `finish_diagnostics` in both cases.
431+
let res = {
432+
// If `f` panics, `finish_diagnostics` will run during
433+
// unwinding because of the `defer`.
434+
let mut guar = None;
435+
let sess_abort_guard = defer(|| {
436+
guar = compiler.sess.finish_diagnostics(&config.registry);
429437
});
430438

431-
f(&compiler)
439+
let res = f(&compiler);
440+
441+
// If `f` doesn't panic, `finish_diagnostics` will run
442+
// normally when `sess_abort_guard` is dropped.
443+
drop(sess_abort_guard);
444+
445+
// If `finish_diagnostics` emits errors (e.g. stashed
446+
// errors) we can't return an error directly, because the
447+
// return type of this function is `R`, not `Result<R, E>`.
448+
// But we need to communicate the errors' existence to the
449+
// caller, otherwise the caller might mistakenly think that
450+
// no errors occurred and return a zero exit code. So we
451+
// abort (panic) instead, similar to if `f` had panicked.
452+
if guar.is_some() {
453+
compiler.sess.dcx().abort_if_errors();
454+
}
455+
456+
res
432457
};
433458

434459
let prof = compiler.sess.prof.clone();
435-
436460
prof.generic_activity("drop_compiler").run(move || drop(compiler));
437-
r
461+
462+
res
438463
})
439464
},
440465
)

compiler/rustc_interface/src/passes.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -772,12 +772,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
772772
// lot of annoying errors in the ui tests (basically,
773773
// lint warnings and so on -- kindck used to do this abort, but
774774
// kindck is gone now). -nmatsakis
775-
if let Some(reported) = sess.dcx().has_errors_excluding_lint_errors() {
776-
return Err(reported);
777-
} else if sess.dcx().stashed_err_count() > 0 {
778-
// Without this case we sometimes get delayed bug ICEs and I don't
779-
// understand why. -nnethercote
780-
return Err(sess.dcx().delayed_bug("some stashed error is waiting for use"));
775+
//
776+
// But we exclude lint errors from this, because lint errors are typically
777+
// less serious and we're more likely to want to continue (#87337).
778+
if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() {
779+
return Err(guar);
781780
}
782781

783782
sess.time("misc_checking_3", || {

compiler/rustc_interface/src/queries.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,12 @@ impl<'tcx> Queries<'tcx> {
222222

223223
pub fn codegen_and_build_linker(&'tcx self) -> Result<Linker> {
224224
self.global_ctxt()?.enter(|tcx| {
225-
// Don't do code generation if there were any errors
226-
self.compiler.sess.compile_status()?;
227-
228-
// If we have any delayed bugs, for example because we created TyKind::Error earlier,
229-
// it's likely that codegen will only cause more ICEs, obscuring the original problem
230-
self.compiler.sess.dcx().flush_delayed();
225+
// Don't do code generation if there were any errors. Likewise if
226+
// there were any delayed bugs, because codegen will likely cause
227+
// more ICEs, obscuring the original problem.
228+
if let Some(guar) = self.compiler.sess.dcx().has_errors_or_delayed_bugs() {
229+
return Err(guar);
230+
}
231231

232232
// Hook for UI tests.
233233
Self::check_for_rustc_errors_attr(tcx);

compiler/rustc_passes/src/dead.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
237237
) {
238238
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
239239
ty::Adt(adt, _) => adt.variant_of_res(res),
240-
_ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"),
240+
_ => {
241+
self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
242+
return;
243+
}
241244
};
242245
let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len());
243246
let first_n = pats.iter().enumerate().take(dotdot);

compiler/rustc_session/src/session.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ impl Session {
258258
}
259259
}
260260

261-
fn check_miri_unleashed_features(&self) {
261+
fn check_miri_unleashed_features(&self) -> Option<ErrorGuaranteed> {
262+
let mut guar = None;
262263
let unleashed_features = self.miri_unleashed_features.lock();
263264
if !unleashed_features.is_empty() {
264265
let mut must_err = false;
@@ -279,18 +280,22 @@ impl Session {
279280
// If we should err, make sure we did.
280281
if must_err && self.dcx().has_errors().is_none() {
281282
// We have skipped a feature gate, and not run into other errors... reject.
282-
self.dcx().emit_err(errors::NotCircumventFeature);
283+
guar = Some(self.dcx().emit_err(errors::NotCircumventFeature));
283284
}
284285
}
286+
guar
285287
}
286288

287289
/// Invoked all the way at the end to finish off diagnostics printing.
288-
pub fn finish_diagnostics(&self, registry: &Registry) {
289-
self.check_miri_unleashed_features();
290+
pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> {
291+
let mut guar = None;
292+
guar = guar.or(self.check_miri_unleashed_features());
293+
guar = guar.or(self.dcx().emit_stashed_diagnostics());
290294
self.dcx().print_error_count(registry);
291295
if self.opts.json_future_incompat {
292296
self.dcx().emit_future_breakage_report();
293297
}
298+
guar
294299
}
295300

296301
/// Returns true if the crate is a testing one.
@@ -314,7 +319,6 @@ impl Session {
314319

315320
pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> {
316321
if let Some(reported) = self.dcx().has_errors() {
317-
self.dcx().emit_stashed_diagnostics();
318322
Err(reported)
319323
} else {
320324
Ok(())

tests/ui/impl-trait/equality-in-canonical-query.clone.stderr

-2
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,3 @@ LL | same_output(foo, rpit);
2121

2222
query stack during panic:
2323
end of query stack
24-
error: aborting due to 2 previous errors
25-

tests/ui/inference/issue-80409.no-compat.stderr

-2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,3 @@ error: internal compiler error: error performing ParamEnvAnd { param_env: ParamE
22
|
33
= query stack during panic:
44
end of query stack
5-
error: aborting due to 1 previous error
6-

tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.current.stderr

-2
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,3 @@ LL | query(get_rpit);
2121

2222
query stack during panic:
2323
end of query stack
24-
error: aborting due to 2 previous errors
25-

0 commit comments

Comments
 (0)