Skip to content

Commit 43a6089

Browse files
committed
Attribute post-monomorphization error notes by emitting thread
collect_items_rec snapshots the error count before processing a mono item and compares it afterwards to decide whether to attach the "the above error was encountered while instantiating ..." note. The mono item graph is walked in parallel, so the global error count can be bumped by an error emitted while collecting a different item on another thread between the two reads, which makes an unrelated item (often lang_start) get blamed. Record the thread that emits each error alongside its ErrorGuaranteed in err_guars, lint_err_guars, and the stashed-diagnostics map, and add err_count_on_current_thread, which counts only the errors whose recorded thread is the calling thread. The collector uses it for the snapshot and comparison, so the delta is not affected by errors emitted on other threads. In serial compilation it is equivalent to a delta of err_count.
1 parent 4a31759 commit 43a6089

2 files changed

Lines changed: 58 additions & 24 deletions

File tree

compiler/rustc_errors/src/lib.rs

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use std::io::Write;
2424
use std::num::NonZero;
2525
use std::ops::DerefMut;
2626
use std::path::{Path, PathBuf};
27+
use std::thread::ThreadId;
2728
use std::{assert_matches, fmt, panic};
2829

2930
use Level::*;
@@ -297,11 +298,12 @@ impl<'a> std::ops::Deref for DiagCtxtHandle<'a> {
297298
struct DiagCtxtInner {
298299
flags: DiagCtxtFlags,
299300

300-
/// The error guarantees from all emitted errors. The length gives the error count.
301-
err_guars: Vec<ErrorGuaranteed>,
302-
/// The error guarantee from all emitted lint errors. The length gives the
303-
/// lint error count.
304-
lint_err_guars: Vec<ErrorGuaranteed>,
301+
/// The error guarantees from all emitted errors, each paired with the
302+
/// thread that emitted it. The length gives the error count.
303+
err_guars: Vec<(ErrorGuaranteed, ThreadId)>,
304+
/// The error guarantee from all emitted lint errors, each paired with the
305+
/// thread that emitted it. The length gives the lint error count.
306+
lint_err_guars: Vec<(ErrorGuaranteed, ThreadId)>,
305307
/// The delayed bugs and their error guarantees.
306308
delayed_bugs: Vec<(DelayedDiagInner, ErrorGuaranteed)>,
307309

@@ -343,7 +345,7 @@ struct DiagCtxtInner {
343345
/// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
344346
/// otherwise an assertion failure will occur.
345347
stashed_diagnostics:
346-
FxIndexMap<StashKey, FxIndexMap<Span, (DiagInner, Option<ErrorGuaranteed>)>>,
348+
FxIndexMap<StashKey, FxIndexMap<Span, (DiagInner, Option<(ErrorGuaranteed, ThreadId)>)>>,
347349

348350
future_breakage_diagnostics: Vec<DiagInner>,
349351

@@ -608,12 +610,10 @@ impl<'a> DiagCtxtHandle<'a> {
608610
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
609611
// if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
610612
// See the PR for a discussion.
611-
self.inner
612-
.borrow_mut()
613-
.stashed_diagnostics
614-
.entry(key)
615-
.or_default()
616-
.insert(span.with_parent(None), (diag, guar));
613+
self.inner.borrow_mut().stashed_diagnostics.entry(key).or_default().insert(
614+
span.with_parent(None),
615+
(diag, guar.map(|guar| (guar, std::thread::current().id()))),
616+
);
617617

618618
guar
619619
}
@@ -714,6 +714,27 @@ impl<'a> DiagCtxtHandle<'a> {
714714
.sum::<usize>()
715715
}
716716

717+
/// The number of errors that have been emitted on the *current thread*.
718+
///
719+
/// Like [`DiagCtxtHandle::err_count`], but only counts errors whose recorded
720+
/// emitting thread is the calling thread. It is meant for code that takes a
721+
/// before/after delta to detect whether a unit of work emitted an error, and
722+
/// must not be misled by errors emitted on other threads at the same time
723+
/// (#154260). In serial compilation it is equivalent to taking a delta of
724+
/// `err_count`.
725+
pub fn err_count_on_current_thread(&self) -> usize {
726+
let inner = self.inner.borrow();
727+
let current = std::thread::current().id();
728+
inner.err_guars.iter().filter(|(_, thread)| *thread == current).count()
729+
+ inner.lint_err_guars.iter().filter(|(_, thread)| *thread == current).count()
730+
+ inner
731+
.stashed_diagnostics
732+
.values()
733+
.flat_map(|stashed| stashed.values())
734+
.filter(|(_, guar)| matches!(guar, Some((_, thread)) if *thread == current))
735+
.count()
736+
}
737+
717738
/// This excludes lint errors and delayed bugs. Unless absolutely
718739
/// necessary, prefer `has_errors` to this method.
719740
pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
@@ -879,7 +900,8 @@ impl<'a> DiagCtxtHandle<'a> {
879900
// This `unchecked_error_guaranteed` is valid. It is where the
880901
// `ErrorGuaranteed` for unused_extern errors originates.
881902
#[allow(deprecated)]
882-
inner.lint_err_guars.push(ErrorGuaranteed::unchecked_error_guaranteed());
903+
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
904+
inner.lint_err_guars.push((guar, std::thread::current().id()));
883905
inner.panic_if_treat_err_as_bug();
884906
}
885907

@@ -1347,13 +1369,17 @@ impl DiagCtxtInner {
13471369
// `ErrorGuaranteed` for errors and lint errors originates.
13481370
#[allow(deprecated)]
13491371
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
1372+
// Record which thread emitted the error so that
1373+
// `err_count_on_current_thread` can attribute it correctly when
1374+
// the mono item graph is collected in parallel (#154260).
1375+
let thread = std::thread::current().id();
13501376
if is_lint {
1351-
self.lint_err_guars.push(guar);
1377+
self.lint_err_guars.push((guar, thread));
13521378
} else {
13531379
if let Some(taint) = taint {
13541380
taint.set(Some(guar));
13551381
}
1356-
self.err_guars.push(guar);
1382+
self.err_guars.push((guar, thread));
13571383
}
13581384
self.panic_if_treat_err_as_bug();
13591385
Some(guar)
@@ -1377,28 +1403,30 @@ impl DiagCtxtInner {
13771403
}
13781404

13791405
fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
1380-
self.err_guars.get(0).copied().or_else(|| {
1406+
self.err_guars.get(0).map(|(guar, _)| *guar).or_else(|| {
13811407
if let Some((_diag, guar)) = self
13821408
.stashed_diagnostics
13831409
.values()
13841410
.flat_map(|stashed_diagnostics| stashed_diagnostics.values())
13851411
.find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
13861412
{
1387-
*guar
1413+
(*guar).map(|(guar, _)| guar)
13881414
} else {
13891415
None
13901416
}
13911417
})
13921418
}
13931419

13941420
fn has_errors(&self) -> Option<ErrorGuaranteed> {
1395-
self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else(
1396-
|| {
1421+
self.err_guars
1422+
.get(0)
1423+
.map(|(guar, _)| *guar)
1424+
.or_else(|| self.lint_err_guars.get(0).map(|(guar, _)| *guar))
1425+
.or_else(|| {
13971426
self.stashed_diagnostics.values().find_map(|stashed_diagnostics| {
1398-
stashed_diagnostics.values().find_map(|(_, guar)| *guar)
1427+
stashed_diagnostics.values().find_map(|(_, guar)| (*guar).map(|(guar, _)| guar))
13991428
})
1400-
},
1401-
)
1429+
})
14021430
}
14031431

14041432
fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,14 @@ fn collect_items_rec<'tcx>(
412412
// error count. If it has changed, a PME occurred, and we trigger some diagnostics about the
413413
// current step of mono items collection.
414414
//
415+
// We use the per-thread error count rather than the global one: the mono item graph is walked
416+
// in parallel (see the `par_for_each_in` in `collect_crate_mono_items`), so the global count
417+
// can be bumped by an unrelated error emitted on another thread between the two reads, which
418+
// would then wrongly blame this item (#154260). The per-thread count is not affected by errors
419+
// emitted on other threads, so the delta reflects work done on this thread.
420+
//
415421
// FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do.
416-
let error_count = tcx.dcx().err_count();
422+
let error_count = tcx.dcx().err_count_on_current_thread();
417423

418424
// In `mentioned_items` we collect items that were mentioned in this MIR but possibly do not
419425
// need to be monomorphized. This is done to ensure that optimizing away function calls does not
@@ -538,7 +544,7 @@ fn collect_items_rec<'tcx>(
538544

539545
// Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
540546
// mono item graph.
541-
if tcx.dcx().err_count() > error_count
547+
if tcx.dcx().err_count_on_current_thread() > error_count
542548
&& starting_item.node.is_generic_fn()
543549
&& starting_item.node.is_user_defined()
544550
{

0 commit comments

Comments
 (0)