Auto merge of #121129 - nnethercote:codegen-Diags, r=estebank

Improve codegen diagnostic handling

Clarify the workings of the temporary `Diagnostic` type used to send diagnostics from codegen threads to the main thread.

r? `@estebank`
This commit is contained in:
bors 2024-02-22 08:01:37 +00:00
commit f70f19fef4
11 changed files with 81 additions and 56 deletions

View File

@ -103,8 +103,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for ParseTargetMachineConfig<'_
fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> { fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
let diag: DiagnosticBuilder<'_, G> = self.0.into_diagnostic(dcx, level); let diag: DiagnosticBuilder<'_, G> = self.0.into_diagnostic(dcx, level);
let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); let (message, _) = diag.messages.first().expect("`LlvmError` with no message");
let message = dcx.eagerly_translate_to_string(message.clone(), diag.args()); let message = dcx.eagerly_translate_to_string(message.clone(), diag.args.iter());
DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config) DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config)
.with_arg("error", message) .with_arg("error", message)
} }

View File

@ -16,8 +16,8 @@ use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::Emitter; use rustc_errors::emitter::Emitter;
use rustc_errors::translation::Translate; use rustc_errors::translation::Translate;
use rustc_errors::{ use rustc_errors::{
DiagCtxt, DiagnosticArgName, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrCode, DiagCtxt, DiagnosticArgMap, DiagnosticBuilder, DiagnosticMessage, ErrCode, FatalError,
FatalError, FluentBundle, Level, Style, FluentBundle, Level, MultiSpan, Style,
}; };
use rustc_fs_util::link_or_copy; use rustc_fs_util::link_or_copy;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
@ -999,11 +999,29 @@ pub(crate) enum Message<B: WriteBackendMethods> {
/// process another codegen unit. /// process another codegen unit.
pub struct CguMessage; pub struct CguMessage;
// A cut-down version of `rustc_errors::Diagnostic` that impls `Send`, which
// can be used to send diagnostics from codegen threads to the main thread.
// It's missing the following fields from `rustc_errors::Diagnostic`.
// - `span`: it doesn't impl `Send`.
// - `suggestions`: it doesn't impl `Send`, and isn't used for codegen
// diagnostics.
// - `sort_span`: it doesn't impl `Send`.
// - `is_lint`: lints aren't relevant during codegen.
// - `emitted_at`: not used for codegen diagnostics.
struct Diagnostic { struct Diagnostic {
msgs: Vec<(DiagnosticMessage, Style)>, level: Level,
args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>, messages: Vec<(DiagnosticMessage, Style)>,
code: Option<ErrCode>, code: Option<ErrCode>,
lvl: Level, children: Vec<Subdiagnostic>,
args: DiagnosticArgMap,
}
// A cut-down version of `rustc_errors::SubDiagnostic` that impls `Send`. It's
// missing the following fields from `rustc_errors::SubDiagnostic`.
// - `span`: it doesn't impl `Send`.
pub struct Subdiagnostic {
level: Level,
messages: Vec<(DiagnosticMessage, Style)>,
} }
#[derive(PartialEq, Clone, Copy, Debug)] #[derive(PartialEq, Clone, Copy, Debug)]
@ -1766,7 +1784,6 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
enum SharedEmitterMessage { enum SharedEmitterMessage {
Diagnostic(Diagnostic), Diagnostic(Diagnostic),
InlineAsmError(u32, String, Level, Option<(String, Vec<InnerSpan>)>), InlineAsmError(u32, String, Level, Option<(String, Vec<InnerSpan>)>),
AbortIfErrors,
Fatal(String), Fatal(String),
} }
@ -1812,24 +1829,29 @@ impl Translate for SharedEmitter {
} }
impl Emitter for SharedEmitter { impl Emitter for SharedEmitter {
fn emit_diagnostic(&mut self, diag: rustc_errors::Diagnostic) { fn emit_diagnostic(&mut self, mut diag: rustc_errors::Diagnostic) {
let args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue> = // Check that we aren't missing anything interesting when converting to
diag.args().map(|(name, arg)| (name.clone(), arg.clone())).collect(); // the cut-down local `Diagnostic`.
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { assert_eq!(diag.span, MultiSpan::new());
msgs: diag.messages.clone(), assert_eq!(diag.suggestions, Ok(vec![]));
args: args.clone(), assert_eq!(diag.sort_span, rustc_span::DUMMY_SP);
code: diag.code, assert_eq!(diag.is_lint, None);
lvl: diag.level(), // No sensible check for `diag.emitted_at`.
})));
for child in &diag.children { let args = mem::replace(&mut diag.args, DiagnosticArgMap::default());
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { drop(
msgs: child.messages.clone(), self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
args: args.clone(), level: diag.level(),
code: None, messages: diag.messages,
lvl: child.level, code: diag.code,
}))); children: diag
} .children
drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); .into_iter()
.map(|child| Subdiagnostic { level: child.level, messages: child.messages })
.collect(),
args,
})),
);
} }
fn source_map(&self) -> Option<&Lrc<SourceMap>> { fn source_map(&self) -> Option<&Lrc<SourceMap>> {
@ -1854,11 +1876,24 @@ impl SharedEmitterMain {
match message { match message {
Ok(SharedEmitterMessage::Diagnostic(diag)) => { Ok(SharedEmitterMessage::Diagnostic(diag)) => {
// The diagnostic has been received on the main thread.
// Convert it back to a full `Diagnostic` and emit.
let dcx = sess.dcx(); let dcx = sess.dcx();
let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msgs); let mut d =
rustc_errors::Diagnostic::new_with_messages(diag.level, diag.messages);
d.code = diag.code; // may be `None`, that's ok d.code = diag.code; // may be `None`, that's ok
d.replace_args(diag.args); d.children = diag
.children
.into_iter()
.map(|sub| rustc_errors::SubDiagnostic {
level: sub.level,
messages: sub.messages,
span: MultiSpan::new(),
})
.collect();
d.args = diag.args;
dcx.emit_diagnostic(d); dcx.emit_diagnostic(d);
sess.dcx().abort_if_errors();
} }
Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => { Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {
assert!(matches!(level, Level::Error | Level::Warning | Level::Note)); assert!(matches!(level, Level::Error | Level::Warning | Level::Note));
@ -1891,9 +1926,6 @@ impl SharedEmitterMain {
err.emit(); err.emit();
} }
Ok(SharedEmitterMessage::AbortIfErrors) => {
sess.dcx().abort_if_errors();
}
Ok(SharedEmitterMessage::Fatal(msg)) => { Ok(SharedEmitterMessage::Fatal(msg)) => {
sess.dcx().fatal(msg); sess.dcx().fatal(msg);
} }

View File

@ -437,7 +437,7 @@ pub trait ReportErrorExt {
let mut diag = dcx.struct_allow(DiagnosticMessage::Str(String::new().into())); let mut diag = dcx.struct_allow(DiagnosticMessage::Str(String::new().into()));
let message = self.diagnostic_message(); let message = self.diagnostic_message();
self.add_args(&mut diag); self.add_args(&mut diag);
let s = dcx.eagerly_translate_to_string(message, diag.args()); let s = dcx.eagerly_translate_to_string(message, diag.args.iter());
diag.cancel(); diag.cancel();
s s
}) })
@ -864,7 +864,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
let dummy_level = Level::Bug; let dummy_level = Level::Bug;
let dummy_diag: DiagnosticBuilder<'_, ()> = let dummy_diag: DiagnosticBuilder<'_, ()> =
e.into_diagnostic().into_diagnostic(diag.dcx, dummy_level); e.into_diagnostic().into_diagnostic(diag.dcx, dummy_level);
for (name, val) in dummy_diag.args() { for (name, val) in dummy_diag.args.iter() {
diag.arg(name.clone(), val.clone()); diag.arg(name.clone(), val.clone());
} }
dummy_diag.cancel(); dummy_diag.cancel();

View File

@ -446,7 +446,7 @@ pub fn format_interp_error<'tcx>(dcx: &DiagCtxt, e: InterpErrorInfo<'tcx>) -> St
let mut diag = dcx.struct_allow(""); let mut diag = dcx.struct_allow("");
let msg = e.diagnostic_message(); let msg = e.diagnostic_message();
e.add_args(&mut diag); e.add_args(&mut diag);
let s = dcx.eagerly_translate_to_string(msg, diag.args()); let s = dcx.eagerly_translate_to_string(msg, diag.args.iter());
diag.cancel(); diag.cancel();
s s
} }

View File

@ -45,7 +45,7 @@ impl Translate for AnnotateSnippetEmitter {
impl Emitter for AnnotateSnippetEmitter { impl Emitter for AnnotateSnippetEmitter {
/// The entry point for the diagnostics generation /// The entry point for the diagnostics generation
fn emit_diagnostic(&mut self, mut diag: Diagnostic) { fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
let fluent_args = to_fluent_args(diag.args()); let fluent_args = to_fluent_args(diag.args.iter());
let mut suggestions = diag.suggestions.unwrap_or(vec![]); let mut suggestions = diag.suggestions.unwrap_or(vec![]);
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args); self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);

View File

@ -43,6 +43,8 @@ pub enum DiagnosticArgValue {
StrListSepByAnd(Vec<Cow<'static, str>>), StrListSepByAnd(Vec<Cow<'static, str>>),
} }
pub type DiagnosticArgMap = FxIndexMap<DiagnosticArgName, DiagnosticArgValue>;
/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" /// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
/// (or "proof") token that the emission happened. /// (or "proof") token that the emission happened.
pub trait EmissionGuarantee: Sized { pub trait EmissionGuarantee: Sized {
@ -275,7 +277,7 @@ pub struct Diagnostic {
pub span: MultiSpan, pub span: MultiSpan,
pub children: Vec<SubDiagnostic>, pub children: Vec<SubDiagnostic>,
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>, pub args: DiagnosticArgMap,
/// This is not used for highlighting or rendering any error message. Rather, it can be used /// This is not used for highlighting or rendering any error message. Rather, it can be used
/// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
@ -403,14 +405,6 @@ impl Diagnostic {
self.args.insert(name.into(), arg.into_diagnostic_arg()); self.args.insert(name.into(), arg.into_diagnostic_arg());
} }
pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_>> {
self.args.iter()
}
pub fn replace_args(&mut self, args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>) {
self.args = args;
}
/// Fields used for Hash, and PartialEq trait. /// Fields used for Hash, and PartialEq trait.
fn keys( fn keys(
&self, &self,
@ -431,7 +425,7 @@ impl Diagnostic {
&self.span, &self.span,
&self.children, &self.children,
&self.suggestions, &self.suggestions,
self.args().collect(), self.args.iter().collect(),
// omit self.sort_span // omit self.sort_span
&self.is_lint, &self.is_lint,
// omit self.emitted_at // omit self.emitted_at
@ -1165,7 +1159,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
subdiagnostic: impl AddToDiagnostic, subdiagnostic: impl AddToDiagnostic,
) -> &mut Self { ) -> &mut Self {
subdiagnostic.add_to_diagnostic_with(self, |diag, msg| { subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
let args = diag.args(); let args = diag.args.iter();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
dcx.eagerly_translate(msg, args) dcx.eagerly_translate(msg, args)
}); });

View File

@ -519,7 +519,7 @@ impl Emitter for HumanEmitter {
} }
fn emit_diagnostic(&mut self, mut diag: Diagnostic) { fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
let fluent_args = to_fluent_args(diag.args()); let fluent_args = to_fluent_args(diag.args.iter());
let mut suggestions = diag.suggestions.unwrap_or(vec![]); let mut suggestions = diag.suggestions.unwrap_or(vec![]);
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args); self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);

View File

@ -341,7 +341,7 @@ struct UnusedExterns<'a, 'b, 'c> {
impl Diagnostic { impl Diagnostic {
fn from_errors_diagnostic(diag: crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { fn from_errors_diagnostic(diag: crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
let args = to_fluent_args(diag.args()); let args = to_fluent_args(diag.args.iter());
let sugg = diag.suggestions.iter().flatten().map(|sugg| { let sugg = diag.suggestions.iter().flatten().map(|sugg| {
let translated_message = let translated_message =
je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap(); je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();

View File

@ -37,9 +37,10 @@ extern crate self as rustc_errors;
pub use codes::*; pub use codes::*;
pub use diagnostic::{ pub use diagnostic::{
AddToDiagnostic, BugAbort, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName, AddToDiagnostic, BugAbort, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgMap,
DiagnosticArgValue, DiagnosticBuilder, DiagnosticStyledString, EmissionGuarantee, FatalAbort, DiagnosticArgName, DiagnosticArgValue, DiagnosticBuilder, DiagnosticStyledString,
IntoDiagnostic, IntoDiagnosticArg, StringPart, SubDiagnostic, SubdiagnosticMessageOp, EmissionGuarantee, FatalAbort, IntoDiagnostic, IntoDiagnosticArg, StringPart, SubDiagnostic,
SubdiagnosticMessageOp,
}; };
pub use diagnostic_impls::{ pub use diagnostic_impls::{
DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter, DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
@ -1496,9 +1497,8 @@ impl DiagCtxtInner {
diag: &Diagnostic, diag: &Diagnostic,
msg: impl Into<SubdiagnosticMessage>, msg: impl Into<SubdiagnosticMessage>,
) -> SubdiagnosticMessage { ) -> SubdiagnosticMessage {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
self.eagerly_translate(msg, args) self.eagerly_translate(msg, diag.args.iter())
} }
fn flush_delayed(&mut self) { fn flush_delayed(&mut self) {

View File

@ -159,7 +159,7 @@ impl Emitter for BufferEmitter {
fn emit_diagnostic(&mut self, diag: Diagnostic) { fn emit_diagnostic(&mut self, diag: Diagnostic) {
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
let fluent_args = to_fluent_args(diag.args()); let fluent_args = to_fluent_args(diag.args.iter());
let translated_main_message = self let translated_main_message = self
.translate_message(&diag.messages[0].0, &fluent_args) .translate_message(&diag.messages[0].0, &fluent_args)
.unwrap_or_else(|e| panic!("{e}")); .unwrap_or_else(|e| panic!("{e}"));

View File

@ -1,6 +1,6 @@
error: cannot prefer dynamic linking when performing LTO error: cannot prefer dynamic linking when performing LTO
|
note: only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO = note: only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
error: aborting due to 1 previous error error: aborting due to 1 previous error