Skip to content

Commit 9448c2d

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 two places that actually emit post-monomorphization errors during collection: const-eval failures in `eval_constant` and the feature-dependent ABI checks in `check_mono_item`. `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. The `large_assignments` lint in `move_check` is the one collection-time diagnostic that is a lint rather than a hard error, so it never promotes to an `ErrorGuaranteed` and is intentionally not bubbled up; its tests confirm no instantiation note is expected.
1 parent 4a31759 commit 9448c2d

5 files changed

Lines changed: 97 additions & 55 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: 26 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,12 @@ 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 and the ABI checks) through `items_of_instance`.
416+
let mut encountered_error: Option<ErrorGuaranteed> = None;
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
@@ -472,7 +472,8 @@ fn collect_items_rec<'tcx>(
472472
));
473473

474474
rustc_data_structures::stack::ensure_sufficient_stack(|| {
475-
let Ok((used, mentioned)) = tcx.items_of_instance((instance, mode)) else {
475+
let Ok((used, mentioned, used_error)) = tcx.items_of_instance((instance, mode))
476+
else {
476477
// Normalization errors here are usually due to trait solving overflow.
477478
// FIXME: I assume that there are few type errors at post-analysis stage, but not
478479
// entirely sure.
@@ -488,6 +489,7 @@ fn collect_items_rec<'tcx>(
488489
def_path_str,
489490
});
490491
};
492+
encountered_error = encountered_error.or(used_error);
491493
used_items.extend(used.into_iter().copied());
492494
mentioned_items.extend(mentioned.into_iter().copied());
493495
});
@@ -538,7 +540,7 @@ fn collect_items_rec<'tcx>(
538540

539541
// Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
540542
// mono item graph.
541-
if tcx.dcx().err_count() > error_count
543+
if encountered_error.is_some()
542544
&& starting_item.node.is_generic_fn()
543545
&& starting_item.node.is_user_defined()
544546
{
@@ -686,6 +688,10 @@ struct MirUsedCollector<'a, 'tcx> {
686688
/// Note that this contains *not-monomorphized* items!
687689
used_mentioned_items: &'a mut UnordSet<MentionedItem<'tcx>>,
688690
instance: Instance<'tcx>,
691+
/// Set if collection encountered an already-reported post-monomorphization error (e.g. a
692+
/// const-eval failure). Bubbled up so callers can emit the "while instantiating" note without
693+
/// consulting the global error count.
694+
encountered_error: Option<ErrorGuaranteed>,
689695
}
690696

691697
impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
@@ -715,8 +721,9 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
715721
"collection encountered polymorphic constant: {:?}",
716722
const_
717723
),
718-
Err(err @ ErrorHandled::Reported(..)) => {
724+
Err(err @ ErrorHandled::Reported(info, _span)) => {
719725
err.emit_note(self.tcx);
726+
self.encountered_error = self.encountered_error.or(Some(info.into()));
720727
return None;
721728
}
722729
}
@@ -1303,14 +1310,14 @@ fn collect_items_of_instance<'tcx>(
13031310
tcx: TyCtxt<'tcx>,
13041311
instance: Instance<'tcx>,
13051312
mode: CollectionMode,
1306-
) -> Result<(MonoItems<'tcx>, MonoItems<'tcx>), NormalizationErrorInMono> {
1313+
) -> Result<(MonoItems<'tcx>, MonoItems<'tcx>, Option<ErrorGuaranteed>), NormalizationErrorInMono> {
13071314
// This item is getting monomorphized, do mono-time checks.
13081315
let body = tcx.instance_mir(instance.def);
13091316
// Plenty of code paths later assume that everything can be normalized. So we have to check
13101317
// normalization first.
13111318
// We choose to emit the error outside to provide helpful diagnostics.
13121319
check_normalization_error(tcx, instance, body)?;
1313-
tcx.ensure_ok().check_mono_item(instance);
1320+
let mono_check_error = tcx.check_mono_item(instance).err();
13141321

13151322
// Naively, in "used" collection mode, all functions get added to *both* `used_items` and
13161323
// `mentioned_items`. Mentioned items processing will then notice that they have already been
@@ -1331,6 +1338,7 @@ fn collect_items_of_instance<'tcx>(
13311338
used_items: &mut used_items,
13321339
used_mentioned_items: &mut used_mentioned_items,
13331340
instance,
1341+
encountered_error: mono_check_error,
13341342
};
13351343

13361344
if mode == CollectionMode::UsedItems {
@@ -1361,22 +1369,24 @@ fn collect_items_of_instance<'tcx>(
13611369
}
13621370
}
13631371

1364-
Ok((used_items, mentioned_items))
1372+
let encountered_error = collector.encountered_error;
1373+
Ok((used_items, mentioned_items, encountered_error))
13651374
}
13661375

13671376
fn items_of_instance<'tcx>(
13681377
tcx: TyCtxt<'tcx>,
13691378
(instance, mode): (Instance<'tcx>, CollectionMode),
13701379
) -> Result<
1371-
(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]),
1380+
(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>], Option<ErrorGuaranteed>),
13721381
NormalizationErrorInMono,
13731382
> {
1374-
let (used_items, mentioned_items) = collect_items_of_instance(tcx, instance, mode)?;
1383+
let (used_items, mentioned_items, encountered_error) =
1384+
collect_items_of_instance(tcx, instance, mode)?;
13751385

13761386
let used_items = tcx.arena.alloc_from_iter(used_items);
13771387
let mentioned_items = tcx.arena.alloc_from_iter(mentioned_items);
13781388

1379-
Ok((used_items, mentioned_items))
1389+
Ok((used_items, mentioned_items, encountered_error))
13801390
}
13811391

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

compiler/rustc_monomorphize/src/mono_checks/abi_check.rs

Lines changed: 56 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,10 @@ 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 = do_check_simd_vector_abi(tcx, abi, instance.def_id(), /*is_call*/ false, loc);
201+
unsized_res.or(simd_res)
191202
}
192203

193204
/// Check the ABI at a call site, emitting an error when:
@@ -199,14 +210,14 @@ fn check_call_site_abi<'tcx>(
199210
callee: Ty<'tcx>,
200211
caller: InstanceKind<'tcx>,
201212
loc: impl Fn() -> (Span, HirId) + Copy,
202-
) {
213+
) -> Option<ErrorGuaranteed> {
203214
let extern_abi = callee.fn_sig(tcx).abi();
204215
if extern_abi.is_rustic_abi() || extern_abi == ExternAbi::Unadjusted {
205216
// We directly handle the soundness of Rust ABIs -- so let's skip the majority of
206217
// call sites to avoid a perf regression.
207218
// We disable all checks for the unadjusted ABI to allow linking to arbitrary LLVM
208219
// intrinsics
209-
return;
220+
return None;
210221
}
211222
let typing_env = ty::TypingEnv::fully_monomorphized();
212223
let callee_abi = match *callee.kind() {
@@ -216,7 +227,7 @@ fn check_call_site_abi<'tcx>(
216227
ty::FnDef(def_id, args) => {
217228
// Intrinsics are handled separately by the compiler.
218229
if tcx.intrinsic(def_id).is_some() {
219-
return;
230+
return None;
220231
}
221232
let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP);
222233
tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
@@ -228,13 +239,21 @@ fn check_call_site_abi<'tcx>(
228239

229240
let Ok(callee_abi) = callee_abi else {
230241
// ABI failed to compute; this will not get through codegen.
231-
return;
242+
return None;
232243
};
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);
244+
// Call both checks unconditionally for their diagnostic side effects before combining.
245+
let unsized_res = do_check_unsized_params(tcx, callee_abi, /*is_call*/ true, loc);
246+
let simd_res =
247+
do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), /*is_call*/ true, loc);
248+
unsized_res.or(simd_res)
235249
}
236250

237-
fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {
251+
fn check_callees_abi<'tcx>(
252+
tcx: TyCtxt<'tcx>,
253+
instance: Instance<'tcx>,
254+
body: &mir::Body<'tcx>,
255+
) -> Option<ErrorGuaranteed> {
256+
let mut res = None;
238257
// Check all function call terminators.
239258
for (bb, _data) in traversal::mono_reachable(body, tcx, instance) {
240259
let terminator = body.basic_blocks[bb].terminator();
@@ -247,7 +266,7 @@ fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &m
247266
ty::TypingEnv::fully_monomorphized(),
248267
ty::EarlyBinder::bind(callee_ty),
249268
);
250-
check_call_site_abi(tcx, callee_ty, body.source.instance, || {
269+
res = res.or(check_call_site_abi(tcx, callee_ty, body.source.instance, || {
251270
let loc = Location {
252271
block: bb,
253272
statement_index: body.basic_blocks[bb].statements.len(),
@@ -259,18 +278,21 @@ fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &m
259278
.lint_root(&body.source_scopes)
260279
.unwrap_or(CRATE_HIR_ID),
261280
)
262-
});
281+
}));
263282
}
264283
_ => {}
265284
}
266285
}
286+
res
267287
}
268288

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

0 commit comments

Comments
 (0)