Skip to content

Commit cecc101

Browse files
Rollup merge of #163148 - nnethercote:improve-Diag-hashing, r=oli-obk
Clean up diagnostic hashing `DiagInner` impls `PartialEq` and `Hash`, as you'd expect for storing it in a hash table. But there's a couple of strange things. - We only store the hash value of the `DiagInner` to do deduplication, not the `DiagInner` itself, which means the `PartialEq` impl is unused. - The `Hash` impl only considers some of the fields. Some of the ignored fields are clearly deliberate (there are comments) but for some it is unclear if it is deliberate. This commit: - Removes the unused `PartialEq` impl. - Inlines and removes `keys` now that it's not needed for `PartialEq`. - Uses struct deconstruction to ensure no fields can be accidentally ignored. I have preserved existing behaviour by assuming that all the ignored fields are supposed to be ignored. - Renames `hash` as an inherent method `dedup_hash` to indicate that it's not a typical hash function, and simplifies it to just return `Hash128` instead of being generic. - Replaces the unnecessary `collect` on `args` with `as_slice`. - Improves the comment on `emitted_diagnostics`. r? @oli-obk
2 parents e16d8d5 + 9fecc5a commit cecc101

2 files changed

Lines changed: 34 additions & 50 deletions

File tree

‎compiler/rustc_errors/src/diagnostic.rs‎

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
use std::borrow::Cow;
22
use std::fmt::{self, Debug};
3-
use std::hash::{Hash, Hasher};
3+
use std::hash::Hash;
44
use std::ops::{Deref, DerefMut};
55
use std::panic;
66
use std::path::PathBuf;
77
use std::thread::panicking;
88

99
use rustc_ast::attr::version::RustcVersion;
10-
use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg};
10+
use rustc_data_structures::stable_hash::StableHasher;
11+
use rustc_error_messages::{DiagArgMap, DiagArgName, IntoDiagArg};
12+
use rustc_hashes::Hash128;
1113
use rustc_lint_defs::{Applicability, LintExpectationId};
1214
use rustc_macros::{Decodable, Encodable};
1315
use rustc_span::{DUMMY_SP, Span, Spanned, Symbol};
@@ -305,46 +307,31 @@ impl DiagInner {
305307
}
306308
}
307309

308-
/// Fields used for Hash, and PartialEq trait.
309-
fn keys(
310-
&self,
311-
) -> (
312-
&Level,
313-
&[(DiagMessage, Style)],
314-
&Option<ErrCode>,
315-
&MultiSpan,
316-
&[Subdiag],
317-
&Suggestions,
318-
Vec<(&DiagArgName, &DiagArgValue)>,
319-
&Option<IsLint>,
320-
) {
321-
(
322-
&self.level,
323-
&self.messages,
324-
&self.code,
325-
&self.span,
326-
&self.children,
327-
&self.suggestions,
328-
self.args.iter().collect(),
329-
// omit self.sort_span
330-
&self.is_lint,
331-
// omit self.emitted_at
332-
)
333-
}
334-
}
335-
336-
impl Hash for DiagInner {
337-
fn hash<H>(&self, state: &mut H)
338-
where
339-
H: Hasher,
340-
{
341-
self.keys().hash(state);
342-
}
343-
}
344-
345-
impl PartialEq for DiagInner {
346-
fn eq(&self, other: &Self) -> bool {
347-
self.keys() == other.keys()
310+
/// Hash used to determine if two diagnostics are the same. Used by
311+
/// `DiagCtxtInner::emitted_diagnostics`. Some fields are ignored for the hash.
312+
pub(crate) fn dedup_hash(&self) -> Hash128 {
313+
// Deconstruct to ensure all fields are considered.
314+
let DiagInner {
315+
level,
316+
messages,
317+
code,
318+
lint_id: _, // ignore
319+
span,
320+
children,
321+
suggestions,
322+
args,
323+
sort_span: _, // ignore
324+
is_lint,
325+
long_ty_path: _, // ignore
326+
emitted_at: _, // ignore
327+
} = self;
328+
329+
let hashed_parts =
330+
(level, messages, code, span, children, suggestions, args.as_slice(), is_lint);
331+
332+
let mut hasher = StableHasher::new();
333+
hashed_parts.hash(&mut hasher);
334+
hasher.finish()
348335
}
349336
}
350337

‎compiler/rustc_errors/src/lib.rs‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,10 @@ struct DiagCtxtInner {
325325
emitted_diagnostic_codes: FxIndexSet<ErrCode>,
326326

327327
/// This set contains a hash of every diagnostic that has been emitted by
328-
/// this `DiagCtxt`. These hashes is used to avoid emitting the same error
329-
/// twice.
328+
/// this `DiagCtxt`. These hashes are used to avoid emitting the same error
329+
/// twice. (Because we don't store the diagnostics themselves, two
330+
/// different diagnostics with the same hash value will be considered
331+
/// equivalent. Such collisions should be vanishingly rare...)
330332
emitted_diagnostics: FxHashSet<Hash128>,
331333

332334
/// We only want to emit `recursion_depth_exceeding_limit` once per
@@ -1301,12 +1303,7 @@ impl DiagCtxtInner {
13011303
self.emitted_diagnostic_codes.insert(code);
13021304
}
13031305

1304-
let already_emitted = {
1305-
let mut hasher = StableHasher::new();
1306-
diagnostic.hash(&mut hasher);
1307-
let diagnostic_hash = hasher.finish();
1308-
!self.emitted_diagnostics.insert(diagnostic_hash)
1309-
};
1306+
let already_emitted = !self.emitted_diagnostics.insert(diagnostic.dedup_hash());
13101307

13111308
let is_error = diagnostic.is_error();
13121309
let is_lint = diagnostic.is_lint.is_some();

0 commit comments

Comments
 (0)