rustc_errors: only box the diagnostic field in DiagnosticBuilder.

This commit is contained in:
Eduard-Mihai Burtescu 2022-01-24 11:23:14 +00:00
parent 68fa81baa3
commit a8dfa3757c
3 changed files with 25 additions and 31 deletions

View File

@ -15,20 +15,14 @@ use tracing::debug;
/// extending `HandlerFlags`, accessed via `self.handler.flags`. /// extending `HandlerFlags`, accessed via `self.handler.flags`.
#[must_use] #[must_use]
#[derive(Clone)] #[derive(Clone)]
pub struct DiagnosticBuilder<'a>(Box<DiagnosticBuilderInner<'a>>); pub struct DiagnosticBuilder<'a> {
/// This is a large type, and often used as a return value, especially within
/// the frequently-used `PResult` type. In theory, return value optimization
/// (RVO) should avoid unnecessary copying. In practice, it does not (at the
/// time of writing). The split between `DiagnosticBuilder` and
/// `DiagnosticBuilderInner` exists to avoid many `memcpy` calls.
// FIXME(eddyb) try having two pointers in `DiagnosticBuilder`, by only boxing
// `Diagnostic` (i.e. `struct DiagnosticBuilder(&Handler, Box<Diagnostic>);`).
#[must_use]
#[derive(Clone)]
struct DiagnosticBuilderInner<'a> {
handler: &'a Handler, handler: &'a Handler,
diagnostic: Diagnostic,
/// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a
/// return value, especially within the frequently-used `PResult` type.
/// In theory, return value optimization (RVO) should avoid unnecessary
/// copying. In practice, it does not (at the time of writing).
diagnostic: Box<Diagnostic>,
} }
/// In general, the `DiagnosticBuilder` uses deref to allow access to /// In general, the `DiagnosticBuilder` uses deref to allow access to
@ -61,7 +55,7 @@ macro_rules! forward {
$(#[$attrs])* $(#[$attrs])*
#[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
self.0.diagnostic.$n($($name),*); self.diagnostic.$n($($name),*);
self self
} }
}; };
@ -78,7 +72,7 @@ macro_rules! forward {
$(#[$attrs])* $(#[$attrs])*
#[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self { pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self {
self.0.diagnostic.$n($($name),*); self.diagnostic.$n($($name),*);
self self
} }
}; };
@ -88,20 +82,20 @@ impl<'a> Deref for DiagnosticBuilder<'a> {
type Target = Diagnostic; type Target = Diagnostic;
fn deref(&self) -> &Diagnostic { fn deref(&self) -> &Diagnostic {
&self.0.diagnostic &self.diagnostic
} }
} }
impl<'a> DerefMut for DiagnosticBuilder<'a> { impl<'a> DerefMut for DiagnosticBuilder<'a> {
fn deref_mut(&mut self) -> &mut Diagnostic { fn deref_mut(&mut self) -> &mut Diagnostic {
&mut self.0.diagnostic &mut self.diagnostic
} }
} }
impl<'a> DiagnosticBuilder<'a> { impl<'a> DiagnosticBuilder<'a> {
/// Emit the diagnostic. /// Emit the diagnostic.
pub fn emit(&mut self) { pub fn emit(&mut self) {
self.0.handler.emit_diagnostic(&self); self.handler.emit_diagnostic(&self);
self.cancel(); self.cancel();
} }
@ -131,19 +125,19 @@ impl<'a> DiagnosticBuilder<'a> {
/// Converts the builder to a `Diagnostic` for later emission, /// Converts the builder to a `Diagnostic` for later emission,
/// unless handler has disabled such buffering. /// unless handler has disabled such buffering.
pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> { pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
if self.0.handler.flags.dont_buffer_diagnostics if self.handler.flags.dont_buffer_diagnostics
|| self.0.handler.flags.treat_err_as_bug.is_some() || self.handler.flags.treat_err_as_bug.is_some()
{ {
self.emit(); self.emit();
return None; return None;
} }
let handler = self.0.handler; let handler = self.handler;
// We must use `Level::Cancelled` for `dummy` to avoid an ICE about an // We must use `Level::Cancelled` for `dummy` to avoid an ICE about an
// unused diagnostic. // unused diagnostic.
let dummy = Diagnostic::new(Level::Cancelled, ""); let dummy = Diagnostic::new(Level::Cancelled, "");
let diagnostic = std::mem::replace(&mut self.0.diagnostic, dummy); let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy);
// Logging here is useful to help track down where in logs an error was // Logging here is useful to help track down where in logs an error was
// actually emitted. // actually emitted.
@ -170,7 +164,7 @@ impl<'a> DiagnosticBuilder<'a> {
/// locally in whichever way makes the most sense. /// locally in whichever way makes the most sense.
pub fn delay_as_bug(&mut self) { pub fn delay_as_bug(&mut self) {
self.level = Level::Bug; self.level = Level::Bug;
self.0.handler.delay_as_bug(self.0.diagnostic.clone()); self.handler.delay_as_bug((*self.diagnostic).clone());
self.cancel(); self.cancel();
} }
@ -187,7 +181,7 @@ impl<'a> DiagnosticBuilder<'a> {
/// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
/// primary. /// primary.
pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self { pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self {
self.0.diagnostic.span_label(span, label); self.diagnostic.span_label(span, label);
self self
} }
@ -200,7 +194,7 @@ impl<'a> DiagnosticBuilder<'a> {
) -> &mut Self { ) -> &mut Self {
let label = label.as_ref(); let label = label.as_ref();
for span in spans { for span in spans {
self.0.diagnostic.span_label(span, label); self.diagnostic.span_label(span, label);
} }
self self
} }
@ -340,13 +334,13 @@ impl<'a> DiagnosticBuilder<'a> {
/// diagnostic. /// diagnostic.
crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> { crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
debug!("Created new diagnostic"); debug!("Created new diagnostic");
DiagnosticBuilder(Box::new(DiagnosticBuilderInner { handler, diagnostic })) DiagnosticBuilder { handler, diagnostic: Box::new(diagnostic) }
} }
} }
impl<'a> Debug for DiagnosticBuilder<'a> { impl<'a> Debug for DiagnosticBuilder<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.diagnostic.fmt(f) self.diagnostic.fmt(f)
} }
} }
@ -356,7 +350,7 @@ impl<'a> Drop for DiagnosticBuilder<'a> {
fn drop(&mut self) { fn drop(&mut self) {
if !panicking() && !self.cancelled() { if !panicking() && !self.cancelled() {
let mut db = DiagnosticBuilder::new( let mut db = DiagnosticBuilder::new(
self.0.handler, self.handler,
Level::Bug, Level::Bug,
"the following error was constructed but not emitted", "the following error was constructed but not emitted",
); );

View File

@ -54,9 +54,9 @@ pub use snippet::Style;
pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>; pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
// `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
// (See also the comment on `DiagnosticBuilderInner`.) // (See also the comment on `DiagnosticBuilder`'s `diagnostic` field.)
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24);
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
pub enum SuggestionStyle { pub enum SuggestionStyle {

View File

@ -315,7 +315,7 @@ mod tests {
code: None, code: None,
message: vec![], message: vec![],
children: vec![], children: vec![],
suggestions: vec![], suggestions: Ok(vec![]),
span: span.unwrap_or_else(MultiSpan::new), span: span.unwrap_or_else(MultiSpan::new),
sort_span: DUMMY_SP, sort_span: DUMMY_SP,
is_lint: false, is_lint: false,