Skip to content

Commit 995b0a4

Browse files
committed
Bubble up post-monomorphization errors instead of reading the global count
`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. Instead of consulting the global count, bubble up an `ErrorGuaranteed` from the places that actually emit post-monomorphization errors during collection: const-eval failures in `eval_constant` and, through `check_mono_item`, the feature-dependent ABI checks and a `large_assignments` lint that has been escalated to `deny`/`forbid`. `items_of_instance` now returns whether such an error was encountered, and `collect_items_rec` uses that to decide whether to emit the note. This is unaffected by what other threads are doing. `large_assignments` is normally a warning and so does not hand back an `ErrorGuaranteed`. When it is escalated to an error we recover the proof token from the `DiagCtxt` right after emitting, gated on the lint's effective level at the relevant node so the note is only attributed to an instantiation whose own lint became the error.
1 parent 4a31759 commit 995b0a4

6 files changed

Lines changed: 118 additions & 59 deletions

File tree

compiler/rustc_middle/src/queries.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2680,11 +2680,11 @@ rustc_queries! {
26802680
/// Perform monomorphization-time checking on this item.
26812681
/// This is used for lints/errors that can only be checked once the instance is fully
26822682
/// monomorphized.
2683-
query check_mono_item(key: ty::Instance<'tcx>) {
2683+
query check_mono_item(key: ty::Instance<'tcx>) -> Result<(), ErrorGuaranteed> {
26842684
desc { "monomorphization-time checking" }
26852685
}
26862686

2687-
query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> Result<(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]), NormalizationErrorInMono> {
2687+
query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> Result<(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>], Option<ErrorGuaranteed>), NormalizationErrorInMono> {
26882688
desc { "collecting items used by `{}`", key.0 }
26892689
cache_on_disk
26902690
}

compiler/rustc_middle/src/query/erase.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ impl_erasable_for_types_with_no_type_params! {
193193
Result<&'_ traits::ImplSource<'_, ()>, traits::CodegenObligationError>,
194194
Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop>,
195195
Result<(&'_ Steal<thir::Thir<'_>>, thir::ExprId), ErrorGuaranteed>,
196-
Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>]), NormalizationErrorInMono>,
196+
Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>], Option<ErrorGuaranteed>), NormalizationErrorInMono>,
197197
Result<(), ErrorGuaranteed>,
198198
Result<Option<ty::EarlyBinder<'_, ty::Const<'_>>>, ErrorGuaranteed>,
199199
Result<Option<ty::Instance<'_>>, ErrorGuaranteed>,

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ use rustc_middle::ty::{
232232
use rustc_middle::util::Providers;
233233
use rustc_middle::{bug, span_bug};
234234
use rustc_session::config::{DebugInfo, EntryFnType};
235-
use rustc_span::{DUMMY_SP, Span, Spanned, dummy_spanned, respan};
235+
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Spanned, dummy_spanned, respan};
236236
use tracing::{debug, instrument, trace};
237237

238238
use crate::errors::{
@@ -408,12 +408,13 @@ fn collect_items_rec<'tcx>(
408408
// source. If the cause is in another crate, the goal here is to quickly locate which mono
409409
// item in the current crate is ultimately responsible for causing the error.
410410
//
411-
// To give at least _some_ context to the user: while collecting mono items, we check the
412-
// error count. If it has changed, a PME occurred, and we trigger some diagnostics about the
413-
// current step of mono items collection.
414-
//
415-
// 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();
411+
// To give at least _some_ context to the user: while collecting mono items, we track whether a
412+
// post-monomorphization error was encountered while processing this item. Rather than reading
413+
// the global error count (which is racy under the parallel front-end, since errors emitted on
414+
// other threads would be miscounted), we bubble up an `ErrorGuaranteed` from the places that
415+
// actually emit such errors (const-eval, the ABI checks, and a `deny`-level `large_assignments`
416+
// lint) through `items_of_instance`.
417+
let mut encountered_error: Option<ErrorGuaranteed> = None;
417418

418419
// In `mentioned_items` we collect items that were mentioned in this MIR but possibly do not
419420
// need to be monomorphized. This is done to ensure that optimizing away function calls does not
@@ -472,7 +473,8 @@ fn collect_items_rec<'tcx>(
472473
));
473474

474475
rustc_data_structures::stack::ensure_sufficient_stack(|| {
475-
let Ok((used, mentioned)) = tcx.items_of_instance((instance, mode)) else {
476+
let Ok((used, mentioned, used_error)) = tcx.items_of_instance((instance, mode))
477+
else {
476478
// Normalization errors here are usually due to trait solving overflow.
477479
// FIXME: I assume that there are few type errors at post-analysis stage, but not
478480
// entirely sure.
@@ -488,6 +490,7 @@ fn collect_items_rec<'tcx>(
488490
def_path_str,
489491
});
490492
};
493+
encountered_error = encountered_error.or(used_error);
491494
used_items.extend(used.into_iter().copied());
492495
mentioned_items.extend(mentioned.into_iter().copied());
493496
});
@@ -538,7 +541,7 @@ fn collect_items_rec<'tcx>(
538541

539542
// Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
540543
// mono item graph.
541-
if tcx.dcx().err_count() > error_count
544+
if encountered_error.is_some()
542545
&& starting_item.node.is_generic_fn()
543546
&& starting_item.node.is_user_defined()
544547
{
@@ -686,6 +689,10 @@ struct MirUsedCollector<'a, 'tcx> {
686689
/// Note that this contains *not-monomorphized* items!
687690
used_mentioned_items: &'a mut UnordSet<MentionedItem<'tcx>>,
688691
instance: Instance<'tcx>,
692+
/// Set if collection encountered an already-reported post-monomorphization error (e.g. a
693+
/// const-eval failure). Bubbled up so callers can emit the "while instantiating" note without
694+
/// consulting the global error count.
695+
encountered_error: Option<ErrorGuaranteed>,
689696
}
690697

691698
impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
@@ -715,8 +722,9 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
715722
"collection encountered polymorphic constant: {:?}",
716723
const_
717724
),
718-
Err(err @ ErrorHandled::Reported(..)) => {
725+
Err(err @ ErrorHandled::Reported(info, _span)) => {
719726
err.emit_note(self.tcx);
727+
self.encountered_error = self.encountered_error.or(Some(info.into()));
720728
return None;
721729
}
722730
}
@@ -1303,14 +1311,14 @@ fn collect_items_of_instance<'tcx>(
13031311
tcx: TyCtxt<'tcx>,
13041312
instance: Instance<'tcx>,
13051313
mode: CollectionMode,
1306-
) -> Result<(MonoItems<'tcx>, MonoItems<'tcx>), NormalizationErrorInMono> {
1314+
) -> Result<(MonoItems<'tcx>, MonoItems<'tcx>, Option<ErrorGuaranteed>), NormalizationErrorInMono> {
13071315
// This item is getting monomorphized, do mono-time checks.
13081316
let body = tcx.instance_mir(instance.def);
13091317
// Plenty of code paths later assume that everything can be normalized. So we have to check
13101318
// normalization first.
13111319
// We choose to emit the error outside to provide helpful diagnostics.
13121320
check_normalization_error(tcx, instance, body)?;
1313-
tcx.ensure_ok().check_mono_item(instance);
1321+
let mono_check_error = tcx.check_mono_item(instance).err();
13141322

13151323
// Naively, in "used" collection mode, all functions get added to *both* `used_items` and
13161324
// `mentioned_items`. Mentioned items processing will then notice that they have already been
@@ -1331,6 +1339,7 @@ fn collect_items_of_instance<'tcx>(
13311339
used_items: &mut used_items,
13321340
used_mentioned_items: &mut used_mentioned_items,
13331341
instance,
1342+
encountered_error: mono_check_error,
13341343
};
13351344

13361345
if mode == CollectionMode::UsedItems {
@@ -1361,22 +1370,24 @@ fn collect_items_of_instance<'tcx>(
13611370
}
13621371
}
13631372

1364-
Ok((used_items, mentioned_items))
1373+
let encountered_error = collector.encountered_error;
1374+
Ok((used_items, mentioned_items, encountered_error))
13651375
}
13661376

13671377
fn items_of_instance<'tcx>(
13681378
tcx: TyCtxt<'tcx>,
13691379
(instance, mode): (Instance<'tcx>, CollectionMode),
13701380
) -> Result<
1371-
(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]),
1381+
(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>], Option<ErrorGuaranteed>),
13721382
NormalizationErrorInMono,
13731383
> {
1374-
let (used_items, mentioned_items) = collect_items_of_instance(tcx, instance, mode)?;
1384+
let (used_items, mentioned_items, encountered_error) =
1385+
collect_items_of_instance(tcx, instance, mode)?;
13751386

13761387
let used_items = tcx.arena.alloc_from_iter(used_items);
13771388
let mentioned_items = tcx.arena.alloc_from_iter(mentioned_items);
13781389

1379-
Ok((used_items, mentioned_items))
1390+
Ok((used_items, mentioned_items, encountered_error))
13801391
}
13811392

13821393
/// `item` must be already monomorphized.

compiler/rustc_monomorphize/src/mono_checks/abi_check.rs

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_hir::{CRATE_HIR_ID, HirId};
55
use rustc_middle::mir::{self, Location, traversal};
66
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt};
77
use rustc_span::def_id::DefId;
8-
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
8+
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
99
use rustc_target::callconv::{FnAbi, PassMode};
1010

1111
use crate::errors;
@@ -57,7 +57,8 @@ fn do_check_simd_vector_abi<'tcx>(
5757
def_id: DefId,
5858
is_call: bool,
5959
loc: impl Fn() -> (Span, HirId),
60-
) {
60+
) -> Option<ErrorGuaranteed> {
61+
let mut res = None;
6162
let codegen_attrs = tcx.codegen_fn_attrs(def_id);
6263
let have_feature = |feat: Symbol| {
6364
let target_feats = tcx.sess.unstable_target_features.contains(&feat);
@@ -74,23 +75,25 @@ fn do_check_simd_vector_abi<'tcx>(
7475
Some((_, feature)) => feature,
7576
None => {
7677
let (span, _hir_id) = loc();
77-
tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType {
78-
span,
79-
ty: arg_abi.layout.ty,
80-
is_call,
81-
});
78+
res = res.or(Some(tcx.dcx().emit_err(
79+
errors::AbiErrorUnsupportedVectorType {
80+
span,
81+
ty: arg_abi.layout.ty,
82+
is_call,
83+
},
84+
)));
8285
continue;
8386
}
8487
};
8588
if !feature.is_empty() && !have_feature(Symbol::intern(feature)) {
8689
let (span, _hir_id) = loc();
87-
tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
90+
res = res.or(Some(tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
8891
span,
8992
required_feature: feature,
9093
ty: arg_abi.layout.ty,
9194
is_call,
9295
is_scalable: false,
93-
});
96+
})));
9497
}
9598
}
9699
UsesVectorRegisters::ScalableVector => {
@@ -101,13 +104,13 @@ fn do_check_simd_vector_abi<'tcx>(
101104
};
102105
if !required_feature.is_empty() && !have_feature(Symbol::intern(required_feature)) {
103106
let (span, _) = loc();
104-
tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
107+
res = res.or(Some(tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
105108
span,
106109
required_feature,
107110
ty: arg_abi.layout.ty,
108111
is_call,
109112
is_scalable: true,
110-
});
113+
})));
111114
}
112115
}
113116
UsesVectorRegisters::No => {
@@ -118,13 +121,14 @@ fn do_check_simd_vector_abi<'tcx>(
118121
// The `vectorcall` ABI is special in that it requires SSE2 no matter which types are being passed.
119122
if abi.conv == CanonAbi::X86(X86Call::Vectorcall) && !have_feature(sym::sse2) {
120123
let (span, _hir_id) = loc();
121-
tcx.dcx().emit_err(errors::AbiRequiredTargetFeature {
124+
res = res.or(Some(tcx.dcx().emit_err(errors::AbiRequiredTargetFeature {
122125
span,
123126
required_feature: "sse2",
124127
abi: "vectorcall",
125128
is_call,
126-
});
129+
})));
127130
}
131+
res
128132
}
129133

130134
/// Emit an error when a non-rustic ABI has unsized parameters.
@@ -136,42 +140,47 @@ fn do_check_unsized_params<'tcx>(
136140
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
137141
is_call: bool,
138142
loc: impl Fn() -> (Span, HirId),
139-
) {
143+
) -> Option<ErrorGuaranteed> {
140144
// Unsized parameters are allowed with the (unstable) "Rust" (and similar) ABIs.
141145
if fn_abi.conv.is_rustic_abi() {
142-
return;
146+
return None;
143147
}
144148

149+
let mut res = None;
145150
for arg_abi in fn_abi.args.iter() {
146151
if !arg_abi.layout.layout.is_sized() {
147152
let (span, _hir_id) = loc();
148-
tcx.dcx().emit_err(errors::AbiErrorUnsupportedUnsizedParameter {
153+
res = res.or(Some(tcx.dcx().emit_err(errors::AbiErrorUnsupportedUnsizedParameter {
149154
span,
150155
ty: arg_abi.layout.ty,
151156
is_call,
152-
});
157+
})));
153158
}
154159
}
160+
res
155161
}
156162

157163
/// Checks the ABI of an Instance, emitting an error when:
158164
///
159165
/// - a non-rustic ABI uses unsized parameters
160166
/// - the signature requires target features that are not enabled
161-
fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
167+
fn check_instance_abi<'tcx>(
168+
tcx: TyCtxt<'tcx>,
169+
instance: Instance<'tcx>,
170+
) -> Option<ErrorGuaranteed> {
162171
let typing_env = ty::TypingEnv::fully_monomorphized();
163172
let ty = instance.ty(tcx, typing_env);
164173
if ty.is_fn() && ty.fn_sig(tcx).abi() == ExternAbi::Unadjusted {
165174
// We disable all checks for the unadjusted ABI to allow linking to arbitrary LLVM
166175
// intrinsics
167-
return;
176+
return None;
168177
}
169178
let Ok(abi) = tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
170179
else {
171180
// An error will be reported during codegen if we cannot determine the ABI of this
172181
// function.
173182
tcx.dcx().delayed_bug("ABI computation failure should lead to compilation failure");
174-
return;
183+
return None;
175184
};
176185
// Unlike the call-site check, we do also check "Rust" ABI functions here.
177186
// This should never trigger, *except* if we start making use of vector registers
@@ -186,8 +195,11 @@ fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
186195
def_id.as_local().map(|did| tcx.local_def_id_to_hir_id(did)).unwrap_or(CRATE_HIR_ID),
187196
)
188197
};
189-
do_check_unsized_params(tcx, abi, /*is_call*/ false, loc);
190-
do_check_simd_vector_abi(tcx, abi, instance.def_id(), /*is_call*/ false, loc);
198+
// Call both checks unconditionally for their diagnostic side effects before combining.
199+
let unsized_res = do_check_unsized_params(tcx, abi, /*is_call*/ false, loc);
200+
let simd_res =
201+
do_check_simd_vector_abi(tcx, abi, instance.def_id(), /*is_call*/ false, loc);
202+
unsized_res.or(simd_res)
191203
}
192204

193205
/// Check the ABI at a call site, emitting an error when:
@@ -199,14 +211,14 @@ fn check_call_site_abi<'tcx>(
199211
callee: Ty<'tcx>,
200212
caller: InstanceKind<'tcx>,
201213
loc: impl Fn() -> (Span, HirId) + Copy,
202-
) {
214+
) -> Option<ErrorGuaranteed> {
203215
let extern_abi = callee.fn_sig(tcx).abi();
204216
if extern_abi.is_rustic_abi() || extern_abi == ExternAbi::Unadjusted {
205217
// We directly handle the soundness of Rust ABIs -- so let's skip the majority of
206218
// call sites to avoid a perf regression.
207219
// We disable all checks for the unadjusted ABI to allow linking to arbitrary LLVM
208220
// intrinsics
209-
return;
221+
return None;
210222
}
211223
let typing_env = ty::TypingEnv::fully_monomorphized();
212224
let callee_abi = match *callee.kind() {
@@ -216,7 +228,7 @@ fn check_call_site_abi<'tcx>(
216228
ty::FnDef(def_id, args) => {
217229
// Intrinsics are handled separately by the compiler.
218230
if tcx.intrinsic(def_id).is_some() {
219-
return;
231+
return None;
220232
}
221233
let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP);
222234
tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
@@ -228,13 +240,21 @@ fn check_call_site_abi<'tcx>(
228240

229241
let Ok(callee_abi) = callee_abi else {
230242
// ABI failed to compute; this will not get through codegen.
231-
return;
243+
return None;
232244
};
233-
do_check_unsized_params(tcx, callee_abi, /*is_call*/ true, loc);
234-
do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), /*is_call*/ true, loc);
245+
// Call both checks unconditionally for their diagnostic side effects before combining.
246+
let unsized_res = do_check_unsized_params(tcx, callee_abi, /*is_call*/ true, loc);
247+
let simd_res =
248+
do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), /*is_call*/ true, loc);
249+
unsized_res.or(simd_res)
235250
}
236251

237-
fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {
252+
fn check_callees_abi<'tcx>(
253+
tcx: TyCtxt<'tcx>,
254+
instance: Instance<'tcx>,
255+
body: &mir::Body<'tcx>,
256+
) -> Option<ErrorGuaranteed> {
257+
let mut res = None;
238258
// Check all function call terminators.
239259
for (bb, _data) in traversal::mono_reachable(body, tcx, instance) {
240260
let terminator = body.basic_blocks[bb].terminator();
@@ -247,7 +267,7 @@ fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &m
247267
ty::TypingEnv::fully_monomorphized(),
248268
ty::EarlyBinder::bind(callee_ty),
249269
);
250-
check_call_site_abi(tcx, callee_ty, body.source.instance, || {
270+
res = res.or(check_call_site_abi(tcx, callee_ty, body.source.instance, || {
251271
let loc = Location {
252272
block: bb,
253273
statement_index: body.basic_blocks[bb].statements.len(),
@@ -259,18 +279,21 @@ fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &m
259279
.lint_root(&body.source_scopes)
260280
.unwrap_or(CRATE_HIR_ID),
261281
)
262-
});
282+
}));
263283
}
264284
_ => {}
265285
}
266286
}
287+
res
267288
}
268289

269290
pub(crate) fn check_feature_dependent_abi<'tcx>(
270291
tcx: TyCtxt<'tcx>,
271292
instance: Instance<'tcx>,
272293
body: &'tcx mir::Body<'tcx>,
273-
) {
274-
check_instance_abi(tcx, instance);
275-
check_callees_abi(tcx, instance, body);
294+
) -> Option<ErrorGuaranteed> {
295+
// Call both checks unconditionally for their diagnostic side effects before combining.
296+
let instance_res = check_instance_abi(tcx, instance);
297+
let callees_res = check_callees_abi(tcx, instance, body);
298+
instance_res.or(callees_res)
276299
}

0 commit comments

Comments
 (0)