Skip to content

Commit 0831b34

Browse files
authored
Rollup merge of #157282 - xmakro:fix/parallel-pme-note, r=petrochenkov
Fix post-monomorphization error note race in the parallel frontend Fixes #154260. Part of #154314. `collect_items_rec` decides whether to attach the note "the above error was encountered while instantiating `fn ...`" by snapshotting the error count before processing a mono item and comparing it afterwards. The mono item graph is walked in parallel (the `par_for_each_in` in `collect_crate_mono_items`), so the global error count can be bumped by an error emitted while collecting a different item on another thread between the two reads. That makes an unrelated item get blamed, which showed up as a spurious note when using parallel frontend: ``` note: the above error was encountered while instantiating `fn std::rt::lang_start::<()>` ``` This PR records 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`.
2 parents 49a638d + 2e128c7 commit 0831b34

31 files changed

Lines changed: 80 additions & 54 deletions

compiler/rustc_errors/src/lib.rs

Lines changed: 49 additions & 23 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

@@ -613,7 +615,7 @@ impl<'a> DiagCtxtHandle<'a> {
613615
.stashed_diagnostics
614616
.entry(key)
615617
.or_default()
616-
.insert(span.with_parent(None), (diag, guar));
618+
.insert(span.with_parent(None), (diag, guar, std::thread::current().id()));
617619

618620
guar
619621
}
@@ -623,7 +625,7 @@ impl<'a> DiagCtxtHandle<'a> {
623625
/// error.
624626
pub fn steal_non_err(self, span: Span, key: StashKey) -> Option<Diag<'a, ()>> {
625627
// FIXME(#120456) - is `swap_remove` correct?
626-
let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
628+
let (diag, guar, _) = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
627629
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
628630
)?;
629631
assert!(!diag.is_error());
@@ -648,7 +650,7 @@ impl<'a> DiagCtxtHandle<'a> {
648650
let err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
649651
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
650652
);
651-
err.map(|(err, guar)| {
653+
err.map(|(err, guar, _)| {
652654
// The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`.
653655
assert_eq!(err.level, Error);
654656
assert!(guar.is_some());
@@ -673,7 +675,7 @@ impl<'a> DiagCtxtHandle<'a> {
673675
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
674676
);
675677
match old_err {
676-
Some((old_err, guar)) => {
678+
Some((old_err, guar, _)) => {
677679
assert_eq!(old_err.level, Error);
678680
assert!(guar.is_some());
679681
// Because `old_err` has already been counted, it can only be
@@ -710,7 +712,27 @@ impl<'a> DiagCtxtHandle<'a> {
710712
+ inner
711713
.stashed_diagnostics
712714
.values()
713-
.map(|a| a.values().filter(|(_, guar)| guar.is_some()).count())
715+
.map(|a| a.values().filter(|(_, guar, _)| guar.is_some()).count())
716+
.sum::<usize>()
717+
}
718+
719+
/// The number of errors that have been emitted on the *current thread*.
720+
///
721+
/// Like [`DiagCtxtHandle::err_count`], but only counts errors whose recorded
722+
/// emitting thread is the calling thread.
723+
pub fn err_count_on_current_thread(&self) -> usize {
724+
let inner = self.inner.borrow();
725+
let current = std::thread::current().id();
726+
inner.err_guars.iter().filter(|(_, thread)| *thread == current).count()
727+
+ inner.lint_err_guars.iter().filter(|(_, thread)| *thread == current).count()
728+
+ inner
729+
.stashed_diagnostics
730+
.values()
731+
.map(|a| {
732+
a.values()
733+
.filter(|(_, guar, thread)| guar.is_some() && *thread == current)
734+
.count()
735+
})
714736
.sum::<usize>()
715737
}
716738

@@ -879,7 +901,8 @@ impl<'a> DiagCtxtHandle<'a> {
879901
// This `unchecked_error_guaranteed` is valid. It is where the
880902
// `ErrorGuaranteed` for unused_extern errors originates.
881903
#[allow(deprecated)]
882-
inner.lint_err_guars.push(ErrorGuaranteed::unchecked_error_guaranteed());
904+
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
905+
inner.lint_err_guars.push((guar, std::thread::current().id()));
883906
inner.panic_if_treat_err_as_bug();
884907
}
885908

@@ -1178,7 +1201,7 @@ impl DiagCtxtInner {
11781201
let mut guar = None;
11791202
let has_errors = !self.err_guars.is_empty();
11801203
for (_, stashed_diagnostics) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
1181-
for (_, (diag, _guar)) in stashed_diagnostics {
1204+
for (_, (diag, _guar, _thread)) in stashed_diagnostics {
11821205
if !diag.is_error() {
11831206
// Unless they're forced, don't flush stashed warnings when
11841207
// there are errors, to avoid causing warning overload. The
@@ -1347,13 +1370,14 @@ impl DiagCtxtInner {
13471370
// `ErrorGuaranteed` for errors and lint errors originates.
13481371
#[allow(deprecated)]
13491372
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
1373+
let thread = std::thread::current().id();
13501374
if is_lint {
1351-
self.lint_err_guars.push(guar);
1375+
self.lint_err_guars.push((guar, thread));
13521376
} else {
13531377
if let Some(taint) = taint {
13541378
taint.set(Some(guar));
13551379
}
1356-
self.err_guars.push(guar);
1380+
self.err_guars.push((guar, thread));
13571381
}
13581382
self.panic_if_treat_err_as_bug();
13591383
Some(guar)
@@ -1377,12 +1401,12 @@ impl DiagCtxtInner {
13771401
}
13781402

13791403
fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
1380-
self.err_guars.get(0).copied().or_else(|| {
1381-
if let Some((_diag, guar)) = self
1404+
self.err_guars.get(0).map(|(guar, _)| *guar).or_else(|| {
1405+
if let Some((_diag, guar, _)) = self
13821406
.stashed_diagnostics
13831407
.values()
13841408
.flat_map(|stashed_diagnostics| stashed_diagnostics.values())
1385-
.find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
1409+
.find(|(diag, guar, _)| guar.is_some() && diag.is_lint.is_none())
13861410
{
13871411
*guar
13881412
} else {
@@ -1392,13 +1416,15 @@ impl DiagCtxtInner {
13921416
}
13931417

13941418
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-
|| {
1419+
self.err_guars
1420+
.get(0)
1421+
.map(|(guar, _)| *guar)
1422+
.or_else(|| self.lint_err_guars.get(0).map(|(guar, _)| *guar))
1423+
.or_else(|| {
13971424
self.stashed_diagnostics.values().find_map(|stashed_diagnostics| {
1398-
stashed_diagnostics.values().find_map(|(_, guar)| *guar)
1425+
stashed_diagnostics.values().find_map(|(_, guar, _)| *guar)
13991426
})
1400-
},
1401-
)
1427+
})
14021428
}
14031429

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

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ fn collect_items_rec<'tcx>(
413413
// current step of mono items collection.
414414
//
415415
// 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();
416+
let error_count = tcx.dcx().err_count_on_current_thread();
417417

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

539539
// Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
540540
// mono item graph.
541-
if tcx.dcx().err_count() > error_count
541+
if tcx.dcx().err_count_on_current_thread() > error_count
542542
&& starting_item.node.is_generic_fn()
543543
&& starting_item.node.is_user_defined()
544544
{

tests/ui/abi/simd-abi-checks-avx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//@ only-x86_64
22
//@ build-fail
33
//@ compile-flags: -C target-feature=-avx
4-
//@ ignore-parallel-frontend post-monomorphization errors
4+
55
#![feature(portable_simd)]
66
#![feature(simd_ffi)]
77
#![allow(improper_ctypes_definitions)]

tests/ui/consts/const-eval/index-out-of-bounds-never-type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@ build-fail
2-
//@ ignore-parallel-frontend post-monomorphization errors
2+
33
// Regression test for #66975
44
#![warn(unconditional_panic)]
55
#![feature(never_type)]

tests/ui/consts/const-eval/issue-50814-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//@ revisions: normal mir-opt
33
//@ [mir-opt]compile-flags: -Zmir-opt-level=4
44
//@ dont-require-annotations: NOTE
5-
//@ ignore-parallel-frontend post-monomorphization errors
5+
66
trait C {
77
const BOO: usize;
88
}

tests/ui/consts/const-eval/issue-50814.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ build-fail
22
//@ dont-require-annotations: NOTE
3-
//@ ignore-parallel-frontend post-monomorphization errors
3+
44
trait Unsigned {
55
const MAX: u8;
66
}

tests/ui/consts/mono-reachable-invalid-const.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@ build-fail
2-
//@ ignore-parallel-frontend post-monomorphization errors
2+
33
struct Bar<const BITS: usize>;
44

55
impl<const BITS: usize> Bar<BITS> {

tests/ui/consts/required-consts/collect-in-called-fn.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//@[opt] compile-flags: -O
55
//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is
66
//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090)
7-
//@ ignore-parallel-frontend post-monomorphization errors
7+
88
struct Fail<T>(T);
99
impl<T> Fail<T> {
1010
const C: () = panic!(); //~ERROR evaluation panicked: explicit panic

tests/ui/consts/required-consts/collect-in-dead-closure.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//@[noopt] compile-flags: -Copt-level=0
44
//@[opt] compile-flags: -O
55
//! This fails without optimizations, so it should also fail with optimizations.
6-
//@ ignore-parallel-frontend post-monomorphization errors
6+
77
struct Fail<T>(T);
88
impl<T> Fail<T> {
99
const C: () = panic!(); //~ERROR evaluation panicked: explicit panic

tests/ui/consts/required-consts/collect-in-dead-drop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//@[noopt] compile-flags: -Copt-level=0
44
//@[opt] compile-flags: -O
55
//! This fails without optimizations, so it should also fail with optimizations.
6-
//@ ignore-parallel-frontend post-monomorphization errors
6+
77
struct Fail<T>(T);
88
impl<T> Fail<T> {
99
const C: () = panic!(); //~ERROR evaluation panicked: explicit panic

0 commit comments

Comments
 (0)