From 3330940f7f2ec6ce7babd53240702406efaf13d1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 12 Jan 2024 08:04:22 +1100 Subject: [PATCH 1/3] Avoid repetition in `flush_delayed` calls. There are two places that handle normal delayed bugs. This commit factors out some repeated code. Also, we can use `std::mem::take` instead of `std::mem::replace`. --- compiler/rustc_errors/src/lib.rs | 35 ++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 404c89ea01b64..136c15acb35c2 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -519,6 +519,11 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { pub static TRACK_DIAGNOSTIC: AtomicRef = AtomicRef::new(&(default_track_diagnostic as _)); +enum DelayedBugKind { + Normal, + GoodPath, +} + #[derive(Copy, Clone, Default)] pub struct DiagCtxtFlags { /// If false, warning-level lints are suppressed. @@ -541,8 +546,7 @@ impl Drop for DiagCtxtInner { self.emit_stashed_diagnostics(); if !self.has_errors() { - let bugs = std::mem::replace(&mut self.span_delayed_bugs, Vec::new()); - self.flush_delayed(bugs, "no errors encountered even though `span_delayed_bug` issued"); + self.flush_delayed(DelayedBugKind::Normal) } // FIXME(eddyb) this explains what `good_path_delayed_bugs` are! @@ -551,11 +555,7 @@ impl Drop for DiagCtxtInner { // lints can be `#[allow]`'d, potentially leading to this triggering. // Also, "good path" should be replaced with a better naming. if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() { - let bugs = std::mem::replace(&mut self.good_path_delayed_bugs, Vec::new()); - self.flush_delayed( - bugs, - "no warnings or errors encountered even though `good_path_delayed_bugs` issued", - ); + self.flush_delayed(DelayedBugKind::GoodPath); } if self.check_unstable_expect_diagnostics { @@ -1218,9 +1218,7 @@ impl DiagCtxt { } pub fn flush_delayed(&self) { - let mut inner = self.inner.borrow_mut(); - let bugs = std::mem::replace(&mut inner.span_delayed_bugs, Vec::new()); - inner.flush_delayed(bugs, "no errors encountered even though `span_delayed_bug` issued"); + self.inner.borrow_mut().flush_delayed(DelayedBugKind::Normal); } } @@ -1396,11 +1394,18 @@ impl DiagCtxtInner { self.emit_diagnostic(Diagnostic::new(FailureNote, msg)); } - fn flush_delayed( - &mut self, - bugs: Vec, - explanation: impl Into + Copy, - ) { + fn flush_delayed(&mut self, kind: DelayedBugKind) { + let (bugs, explanation) = match kind { + DelayedBugKind::Normal => ( + std::mem::take(&mut self.span_delayed_bugs), + "no errors encountered even though `span_delayed_bug` issued", + ), + DelayedBugKind::GoodPath => ( + std::mem::take(&mut self.good_path_delayed_bugs), + "no warnings or errors encountered even though `good_path_delayed_bugs` issued", + ), + }; + if bugs.is_empty() { return; } From eb79bc0470e0eefbc7608cffb99879969d68faea Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 12 Jan 2024 03:15:14 +0000 Subject: [PATCH 2/3] Good path bugs are just a flavor of delayed bug --- .../src/annotate_snippet_emitter_writer.rs | 2 +- compiler/rustc_errors/src/diagnostic.rs | 15 +++-- compiler/rustc_errors/src/lib.rs | 56 +++++++++++-------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 980ac31011931..648c9118400e9 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -85,7 +85,7 @@ fn source_string(file: Lrc, line: &Line) -> String { /// Maps `Diagnostic::Level` to `snippet::AnnotationType` fn annotation_type_for_level(level: Level) -> AnnotationType { match level { - Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error => AnnotationType::Error, + Level::Bug | Level::DelayedBug(_) | Level::Fatal | Level::Error => AnnotationType::Error, Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning, Level::Note | Level::OnceNote => AnnotationType::Note, Level::Help | Level::OnceHelp => AnnotationType::Help, diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index d8d6922a1bcda..786aced5b4f91 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,7 +1,7 @@ use crate::snippet::Style; use crate::{ - CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, MultiSpan, - SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, + CodeSuggestion, DelayedBugKind, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, + MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_error_messages::fluent_value_from_str_list_sep_by_and; @@ -243,12 +243,15 @@ impl Diagnostic { pub fn is_error(&self) -> bool { match self.level { - Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error | Level::FailureNote => { - true - } + Level::Bug + | Level::DelayedBug(DelayedBugKind::Normal) + | Level::Fatal + | Level::Error + | Level::FailureNote => true, Level::ForceWarning(_) | Level::Warning + | Level::DelayedBug(DelayedBugKind::GoodPath) | Level::Note | Level::OnceNote | Level::Help @@ -318,7 +321,7 @@ impl Diagnostic { "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error", self.level ); - self.level = Level::DelayedBug; + self.level = Level::DelayedBug(DelayedBugKind::Normal); } /// Appends a labeled span to the diagnostic. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 136c15acb35c2..aefebfec4487a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -519,7 +519,8 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { pub static TRACK_DIAGNOSTIC: AtomicRef = AtomicRef::new(&(default_track_diagnostic as _)); -enum DelayedBugKind { +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)] +pub enum DelayedBugKind { Normal, GoodPath, } @@ -865,7 +866,8 @@ impl DiagCtxt { if treat_next_err_as_bug { self.bug(msg); } - DiagnosticBuilder::::new(self, DelayedBug, msg).emit() + DiagnosticBuilder::::new(self, DelayedBug(DelayedBugKind::Normal), msg) + .emit() } /// Like `delayed_bug`, but takes an additional span. @@ -882,16 +884,15 @@ impl DiagCtxt { if treat_next_err_as_bug { self.span_bug(sp, msg); } - DiagnosticBuilder::::new(self, DelayedBug, msg).with_span(sp).emit() + DiagnosticBuilder::::new(self, DelayedBug(DelayedBugKind::Normal), msg) + .with_span(sp) + .emit() } // FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's // where the explanation of what "good path" is (also, it should be renamed). pub fn good_path_delayed_bug(&self, msg: impl Into) { - let mut inner = self.inner.borrow_mut(); - let diagnostic = Diagnostic::new(DelayedBug, msg); - let backtrace = std::backtrace::Backtrace::capture(); - inner.good_path_delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); + DiagnosticBuilder::<()>::new(self, DelayedBug(DelayedBugKind::GoodPath), msg).emit() } #[track_caller] @@ -1268,17 +1269,27 @@ impl DiagCtxtInner { return None; } - if diagnostic.level == DelayedBug { - // FIXME(eddyb) this should check for `has_errors` and stop pushing - // once *any* errors were emitted (and truncate `span_delayed_bugs` - // when an error is first emitted, also), but maybe there's a case - // in which that's not sound? otherwise this is really inefficient. - let backtrace = std::backtrace::Backtrace::capture(); - self.span_delayed_bugs - .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); + // FIXME(eddyb) this should check for `has_errors` and stop pushing + // once *any* errors were emitted (and truncate `span_delayed_bugs` + // when an error is first emitted, also), but maybe there's a case + // in which that's not sound? otherwise this is really inefficient. + match diagnostic.level { + DelayedBug(DelayedBugKind::Normal) => { + let backtrace = std::backtrace::Backtrace::capture(); + self.span_delayed_bugs + .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); - #[allow(deprecated)] - return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + #[allow(deprecated)] + return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + } + DelayedBug(DelayedBugKind::GoodPath) => { + let backtrace = std::backtrace::Backtrace::capture(); + self.good_path_delayed_bugs + .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); + + return None; + } + _ => {} } if diagnostic.has_future_breakage() { @@ -1438,7 +1449,7 @@ impl DiagCtxtInner { if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner }; // "Undelay" the `DelayedBug`s (into plain `Bug`s). - if bug.level != DelayedBug { + if !matches!(bug.level, DelayedBug(_)) { // NOTE(eddyb) not panicking here because we're already producing // an ICE, and the more information the merrier. bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel { @@ -1526,8 +1537,9 @@ pub enum Level { /// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths /// that should only be reached when compiling erroneous code. /// - /// Its `EmissionGuarantee` is `ErrorGuaranteed`. - DelayedBug, + /// Its `EmissionGuarantee` is `ErrorGuaranteed` for `Normal` delayed bugs, and `()` for + /// `GoodPath` delayed bugs. + DelayedBug(DelayedBugKind), /// An error that causes an immediate abort. Used for things like configuration errors, /// internal overflows, some file operation errors. @@ -1602,7 +1614,7 @@ impl Level { fn color(self) -> ColorSpec { let mut spec = ColorSpec::new(); match self { - Bug | DelayedBug | Fatal | Error => { + Bug | DelayedBug(_) | Fatal | Error => { spec.set_fg(Some(Color::Red)).set_intense(true); } ForceWarning(_) | Warning => { @@ -1622,7 +1634,7 @@ impl Level { pub fn to_str(self) -> &'static str { match self { - Bug | DelayedBug => "error: internal compiler error", + Bug | DelayedBug(_) => "error: internal compiler error", Fatal | Error => "error", ForceWarning(_) | Warning => "warning", Note | OnceNote => "note", From 7df43d3c814b4d4a2d0231a5d02fe36e70901627 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 12 Jan 2024 00:30:04 +0000 Subject: [PATCH 3/3] Give me a way to emit all the delayed bugs --- compiler/rustc_errors/src/lib.rs | 6 ++++ compiler/rustc_session/src/config.rs | 1 + compiler/rustc_session/src/options.rs | 3 ++ tests/ui/treat-err-as-bug/eagerly-emit.rs | 11 ++++++++ tests/ui/treat-err-as-bug/eagerly-emit.stderr | 28 +++++++++++++++++++ 5 files changed, 49 insertions(+) create mode 100644 tests/ui/treat-err-as-bug/eagerly-emit.rs create mode 100644 tests/ui/treat-err-as-bug/eagerly-emit.stderr diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index aefebfec4487a..8c2752af65974 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -533,6 +533,9 @@ pub struct DiagCtxtFlags { /// If Some, the Nth error-level diagnostic is upgraded to bug-level. /// (rustc: see `-Z treat-err-as-bug`) pub treat_err_as_bug: Option, + /// Eagerly emit delayed bugs as errors, so that the compiler debugger may + /// see all of the errors being emitted at once. + pub eagerly_emit_delayed_bugs: bool, /// Show macro backtraces. /// (rustc: see `-Z macro-backtrace`) pub macro_backtrace: bool, @@ -1274,6 +1277,9 @@ impl DiagCtxtInner { // when an error is first emitted, also), but maybe there's a case // in which that's not sound? otherwise this is really inefficient. match diagnostic.level { + DelayedBug(_) if self.flags.eagerly_emit_delayed_bugs => { + diagnostic.level = Error; + } DelayedBug(DelayedBugKind::Normal) => { let backtrace = std::backtrace::Backtrace::capture(); self.span_delayed_bugs diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 61796d7a6cafc..8918dce7d3a41 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1146,6 +1146,7 @@ impl UnstableOptions { DiagCtxtFlags { can_emit_warnings, treat_err_as_bug: self.treat_err_as_bug, + eagerly_emit_delayed_bugs: self.eagerly_emit_delayed_bugs, macro_backtrace: self.macro_backtrace, deduplicate_diagnostics: self.deduplicate_diagnostics, track_diagnostics: self.track_diagnostics, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c97b18ebd66ad..2d91a3fbd9116 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1583,6 +1583,9 @@ options! { "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), dylib_lto: bool = (false, parse_bool, [UNTRACKED], "enables LTO for dylib crate type"), + eagerly_emit_delayed_bugs: bool = (false, parse_bool, [UNTRACKED], + "emit delayed bugs eagerly as errors instead of stashing them and emitting \ + them only if an error has not been emitted"), ehcont_guard: bool = (false, parse_bool, [TRACKED], "generate Windows EHCont Guard tables"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], diff --git a/tests/ui/treat-err-as-bug/eagerly-emit.rs b/tests/ui/treat-err-as-bug/eagerly-emit.rs new file mode 100644 index 0000000000000..5f32f5a1d94b7 --- /dev/null +++ b/tests/ui/treat-err-as-bug/eagerly-emit.rs @@ -0,0 +1,11 @@ +// compile-flags: -Zeagerly-emit-delayed-bugs + +trait Foo {} + +fn main() {} + +fn f() -> impl Foo { + //~^ ERROR the trait bound `i32: Foo` is not satisfied + //~| ERROR `report_selection_error` did not emit an error + 1i32 +} diff --git a/tests/ui/treat-err-as-bug/eagerly-emit.stderr b/tests/ui/treat-err-as-bug/eagerly-emit.stderr new file mode 100644 index 0000000000000..3d25741d52de8 --- /dev/null +++ b/tests/ui/treat-err-as-bug/eagerly-emit.stderr @@ -0,0 +1,28 @@ +error: `report_selection_error` did not emit an error + --> $DIR/eagerly-emit.rs:7:11 + | +LL | fn f() -> impl Foo { + | ^^^^^^^^ + +error: trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging + +error[E0277]: the trait bound `i32: Foo` is not satisfied + --> $DIR/eagerly-emit.rs:7:11 + | +LL | fn f() -> impl Foo { + | ^^^^^^^^ the trait `Foo` is not implemented for `i32` +... +LL | 1i32 + | ---- return type was inferred to be `i32` here + | +help: this trait has no implementations, consider adding one + --> $DIR/eagerly-emit.rs:3:1 + | +LL | trait Foo {} + | ^^^^^^^^^ + +error: expected fulfillment errors + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`.