Skip to content

Commit 4b0014e

Browse files
authored
Rollup merge of rust-lang#85633 - lqd:stackless_span_stacks, r=oli-obk
Post-monomorphization errors traces MVP This PR works towards better diagnostics for the errors encountered in rust-lang#85155 and similar. We can encounter post-monomorphization errors (PMEs) when collecting mono items. The current diagnostics are confusing for these cases when they happen in a dependency (but are acceptable when they happen in the local crate). These kinds of errors will be more likely now that `stdarch` uses const generics for its intrinsics' immediate arguments, and validates these const arguments with a mechanism that triggers such PMEs. (Not to mention that the errors happen during codegen, so only when building code that actually uses these code paths. Check builds don't trigger them, neither does unused code) So in this PR, we detect these kinds of errors during the mono item graph walk: if any error happens while collecting a node or its neighbors, we print a diagnostic about the current collection step, so that the user has at least some context of which erroneous code and dependency triggered the error. The diagnostics for issue rust-lang#85155 now have this note showing the source of the erroneous const argument: ``` note: the above error was encountered while instantiating `fn std::arch::x86_64::_mm_blend_ps::<51_i32>` --> issue-85155.rs:11:24 | 11 | let _blended = _mm_blend_ps(a, b, 0x33); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error ``` Note that rust-lang#85155 is a reduced version of a case happening in the wild, to indirect users of the `rustfft` crate, as seen in ejmahler/RustFFT#74. The crate had a few of these out-of-range immediates. Here's how the diagnostics in this PR would have looked on one of its examples before it was fixed: <details> ``` error[E0080]: evaluation of constant value failed --> ./stdarch/crates/core_arch/src/macros.rs:8:9 | 8 | assert!(IMM >= MIN && IMM <= MAX, "IMM value not in expected range"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'IMM value not in expected range', ./stdarch/crates/core_arch/src/macros.rs:8:9 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) note: the above error was encountered while instantiating `fn _mm_blend_ps::<51_i32>` --> /tmp/RustFFT/src/avx/avx_vector.rs:1314:23 | 1314 | let blended = _mm_blend_ps(rows[0], rows[2], 0x33); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn _mm_permute_pd::<5_i32>` --> /tmp/RustFFT/src/avx/avx_vector.rs:1859:9 | 1859 | _mm_permute_pd(self, 0x05) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn _mm_permute_pd::<15_i32>` --> /tmp/RustFFT/src/avx/avx_vector.rs:1863:32 | 1863 | (_mm_movedup_pd(self), _mm_permute_pd(self, 0x0F)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error For more information about this error, try `rustc --explain E0080`. error: could not compile `rustfft` To learn more, run the command again with --verbose. ``` </details> I've developed and discussed this with them, so maybe r? `@oli-obk` -- but feel free to redirect to someone else of course. (I'm not sure we can say that this PR definitely closes issue 85155, as it's still unclear exactly which diagnostics and information would be interesting to report in such cases -- and we've discussed printing backtraces before. I have prototypes of some complete and therefore noisy backtraces I showed Oli, but we decided to not include them in this PR for now)
2 parents f5c5cca + 6f61456 commit 4b0014e

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

compiler/rustc_middle/src/mir/mono.rs

+9
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,15 @@ impl<'tcx> MonoItem<'tcx> {
186186
pub fn codegen_dep_node(&self, tcx: TyCtxt<'tcx>) -> DepNode {
187187
crate::dep_graph::make_compile_mono_item(tcx, self)
188188
}
189+
190+
/// Returns the item's `CrateNum`
191+
pub fn krate(&self) -> CrateNum {
192+
match self {
193+
MonoItem::Fn(ref instance) => instance.def_id().krate,
194+
MonoItem::Static(def_id) => def_id.krate,
195+
MonoItem::GlobalAsm(..) => LOCAL_CRATE,
196+
}
197+
}
189198
}
190199

191200
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for MonoItem<'tcx> {

compiler/rustc_mir/src/monomorphize/collector.rs

+44-2
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
184184
use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator};
185185
use rustc_errors::{ErrorReported, FatalError};
186186
use rustc_hir as hir;
187-
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
187+
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
188188
use rustc_hir::itemlikevisit::ItemLikeVisitor;
189189
use rustc_hir::lang_items::LangItem;
190190
use rustc_index::bit_set::GrowableBitSet;
@@ -342,7 +342,8 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<
342342
.collect()
343343
}
344344

345-
// Collect all monomorphized items reachable from `starting_point`
345+
/// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a
346+
/// post-monorphization error is encountered during a collection step.
346347
fn collect_items_rec<'tcx>(
347348
tcx: TyCtxt<'tcx>,
348349
starting_point: Spanned<MonoItem<'tcx>>,
@@ -359,6 +360,31 @@ fn collect_items_rec<'tcx>(
359360
let mut neighbors = Vec::new();
360361
let recursion_depth_reset;
361362

363+
//
364+
// Post-monomorphization errors MVP
365+
//
366+
// We can encounter errors while monomorphizing an item, but we don't have a good way of
367+
// showing a complete stack of spans ultimately leading to collecting the erroneous one yet.
368+
// (It's also currently unclear exactly which diagnostics and information would be interesting
369+
// to report in such cases)
370+
//
371+
// This leads to suboptimal error reporting: a post-monomorphization error (PME) will be
372+
// shown with just a spanned piece of code causing the error, without information on where
373+
// it was called from. This is especially obscure if the erroneous mono item is in a
374+
// dependency. See for example issue #85155, where, before minimization, a PME happened two
375+
// crates downstream from libcore's stdarch, without a way to know which dependency was the
376+
// cause.
377+
//
378+
// If such an error occurs in the current crate, its span will be enough to locate the
379+
// source. If the cause is in another crate, the goal here is to quickly locate which mono
380+
// item in the current crate is ultimately responsible for causing the error.
381+
//
382+
// To give at least _some_ context to the user: while collecting mono items, we check the
383+
// error count. If it has changed, a PME occurred, and we trigger some diagnostics about the
384+
// current step of mono items collection.
385+
//
386+
let error_count = tcx.sess.diagnostic().err_count();
387+
362388
match starting_point.node {
363389
MonoItem::Static(def_id) => {
364390
let instance = Instance::mono(tcx, def_id);
@@ -411,6 +437,22 @@ fn collect_items_rec<'tcx>(
411437
}
412438
}
413439

440+
// Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
441+
// mono item graph where the PME diagnostics are currently the most problematic (e.g. ones
442+
// involving a dependency, and the lack of context is confusing) in this MVP, we focus on
443+
// diagnostics on edges crossing a crate boundary: the collected mono items which are not
444+
// defined in the local crate.
445+
if tcx.sess.diagnostic().err_count() > error_count && starting_point.node.krate() != LOCAL_CRATE
446+
{
447+
tcx.sess.span_note_without_error(
448+
starting_point.span,
449+
&format!(
450+
"the above error was encountered while instantiating `{}`",
451+
starting_point.node
452+
),
453+
);
454+
}
455+
414456
record_accesses(tcx, starting_point.node, neighbors.iter().map(|i| &i.node), inlining_map);
415457

416458
for neighbour in neighbors {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Auxiliary crate used for testing post-monomorphization errors cross-crate.
2+
// It duplicates the setup used in `stdarch` to validate its intrinsics' const arguments.
3+
4+
struct ValidateConstImm<const IMM: i32, const MIN: i32, const MAX: i32>;
5+
impl<const IMM: i32, const MIN: i32, const MAX: i32> ValidateConstImm<IMM, MIN, MAX> {
6+
pub(crate) const VALID: () = {
7+
let _ = 1 / ((IMM >= MIN && IMM <= MAX) as usize);
8+
};
9+
}
10+
11+
macro_rules! static_assert_imm1 {
12+
($imm:ident) => {
13+
let _ = $crate::ValidateConstImm::<$imm, 0, { (1 << 1) - 1 }>::VALID;
14+
};
15+
}
16+
17+
// This function triggers an error whenever the const argument does not fit in 1-bit.
18+
pub fn stdarch_intrinsic<const IMM1: i32>() {
19+
static_assert_imm1!(IMM1);
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// This is a test with a setup similar to issue 85155, which triggers a const eval error: a const
2+
// argument value is outside the range expected by the `stdarch` intrinsic.
3+
//
4+
// It's not the exact code mentioned in that issue because it depends both on `stdarch` intrinsics
5+
// only available on x64, and internal implementation details of `stdarch`. But mostly because these
6+
// are not important to trigger the diagnostics issue: it's specifically about the lack of context
7+
// in the diagnostics of post-monomorphization errors (PMEs) for consts, happening in a dependency.
8+
// Therefore, its setup is reproduced with an aux crate, which will similarly trigger a PME
9+
// depending on the const argument value, like the `stdarch` intrinsics would.
10+
//
11+
// aux-build: post_monomorphization_error.rs
12+
// build-fail: this is a post-monomorphization error, it passes check runs and requires building
13+
// to actually fail.
14+
15+
extern crate post_monomorphization_error;
16+
17+
fn main() {
18+
// This function triggers a PME whenever the const argument does not fit in 1-bit.
19+
post_monomorphization_error::stdarch_intrinsic::<2>();
20+
//~^ NOTE the above error was encountered while instantiating
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/auxiliary/post_monomorphization_error.rs:7:17
3+
|
4+
LL | let _ = 1 / ((IMM >= MIN && IMM <= MAX) as usize);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to divide `1_usize` by zero
6+
7+
note: the above error was encountered while instantiating `fn stdarch_intrinsic::<2_i32>`
8+
--> $DIR/issue-85155.rs:19:5
9+
|
10+
LL | post_monomorphization_error::stdarch_intrinsic::<2>();
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)