Skip to content

Commit bcb4d16

Browse files
Prevent infinite recursion in monomorphization error handling
Signed-off-by: FedericoBruzzone <[email protected]>
1 parent 4cdbbdf commit bcb4d16

File tree

9 files changed

+570
-14
lines changed

9 files changed

+570
-14
lines changed

compiler/rustc_middle/src/query/erase.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ impl EraseType for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
164164
type Result = [u8; size_of::<Result<ty::EarlyBinder<'static, Ty<'_>>, CyclePlaceholder>>()];
165165
}
166166

167+
impl<T> EraseType for Result<(&'_ [T], &'_ [T]), rustc_errors::ErrorGuaranteed> {
168+
type Result =
169+
[u8; size_of::<Result<(&'static [()], &'static [()]), rustc_errors::ErrorGuaranteed>>()];
170+
}
171+
167172
impl<T> EraseType for Option<&'_ T> {
168173
type Result = [u8; size_of::<Option<&'static ()>>()];
169174
}

compiler/rustc_middle/src/query/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2490,7 +2490,12 @@ rustc_queries! {
24902490
desc { "functions to skip for move-size check" }
24912491
}
24922492

2493-
query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> (&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]) {
2493+
query items_of_instance(
2494+
key: (ty::Instance<'tcx>, CollectionMode)
2495+
) -> Result<
2496+
(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]),
2497+
ErrorGuaranteed
2498+
> {
24942499
desc { "collecting items used by `{}`", key.0 }
24952500
cache_on_disk_if { true }
24962501
}

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@
206206
//! regardless of whether it is actually needed or not.
207207
208208
use std::path::PathBuf;
209+
use std::sync::atomic::{AtomicUsize, Ordering};
209210

210211
use rustc_attr_parsing::InlineAttr;
211212
use rustc_data_structures::fx::FxIndexMap;
@@ -233,10 +234,12 @@ use rustc_middle::{bug, span_bug};
233234
use rustc_session::Limit;
234235
use rustc_session::config::EntryFnType;
235236
use rustc_span::source_map::{Spanned, dummy_spanned, respan};
236-
use rustc_span::{DUMMY_SP, Span};
237+
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
237238
use tracing::{debug, instrument, trace};
238239

239-
use crate::errors::{self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit};
240+
use crate::errors::{
241+
self, EncounteredErrorWhileInstantiating, MAX_NUMBER_OF_ERRORS, NoOptimizedMir, RecursionLimit,
242+
};
240243

241244
#[derive(PartialEq)]
242245
pub(crate) enum MonoItemCollectionStrategy {
@@ -253,6 +256,8 @@ struct SharedState<'tcx> {
253256
mentioned: MTLock<UnordSet<MonoItem<'tcx>>>,
254257
/// Which items are being used where, for better errors.
255258
usage_map: MTLock<UsageMap<'tcx>>,
259+
/// Number of errors that have occurred during collection.
260+
error_count: AtomicUsize,
256261
}
257262

258263
pub(crate) struct UsageMap<'tcx> {
@@ -362,6 +367,16 @@ fn collect_items_rec<'tcx>(
362367
recursion_limit: Limit,
363368
mode: CollectionMode,
364369
) {
370+
if state.error_count.load(Ordering::Relaxed) > MAX_NUMBER_OF_ERRORS {
371+
tcx.dcx().span_fatal(
372+
starting_item.span,
373+
format!(
374+
"the limit of {} errors has been reached during monomorphization",
375+
MAX_NUMBER_OF_ERRORS
376+
),
377+
);
378+
}
379+
365380
if mode == CollectionMode::UsedItems {
366381
if !state.visited.lock_mut().insert(starting_item.node) {
367382
// We've been here already, no need to search again.
@@ -466,9 +481,15 @@ fn collect_items_rec<'tcx>(
466481
));
467482

468483
rustc_data_structures::stack::ensure_sufficient_stack(|| {
469-
let (used, mentioned) = tcx.items_of_instance((instance, mode));
470-
used_items.extend(used.into_iter().copied());
471-
mentioned_items.extend(mentioned.into_iter().copied());
484+
match tcx.items_of_instance((instance, mode)) {
485+
Ok((used, mentioned)) => {
486+
used_items.extend(used.into_iter().copied());
487+
mentioned_items.extend(mentioned.into_iter().copied());
488+
}
489+
Err(_) => {
490+
state.error_count.fetch_add(1, Ordering::Relaxed);
491+
}
492+
};
472493
});
473494
}
474495
MonoItem::GlobalAsm(item_id) => {
@@ -660,7 +681,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
660681
),
661682
Err(err @ ErrorHandled::Reported(..)) => {
662683
err.emit_note(self.tcx);
663-
return None;
684+
None
664685
}
665686
}
666687
}
@@ -1211,7 +1232,7 @@ fn collect_items_of_instance<'tcx>(
12111232
tcx: TyCtxt<'tcx>,
12121233
instance: Instance<'tcx>,
12131234
mode: CollectionMode,
1214-
) -> (MonoItems<'tcx>, MonoItems<'tcx>) {
1235+
) -> Result<(MonoItems<'tcx>, MonoItems<'tcx>), ErrorGuaranteed> {
12151236
// This item is getting monomorphized, do mono-time checks.
12161237
tcx.ensure().check_mono_item(instance);
12171238

@@ -1245,9 +1266,12 @@ fn collect_items_of_instance<'tcx>(
12451266

12461267
// Always visit all `required_consts`, so that we evaluate them and abort compilation if any of
12471268
// them errors.
1269+
let mut error_occurred = false;
12481270
for const_op in body.required_consts() {
12491271
if let Some(val) = collector.eval_constant(const_op) {
12501272
collect_const_value(tcx, val, &mut mentioned_items);
1273+
} else {
1274+
error_occurred = true;
12511275
}
12521276
}
12531277

@@ -1260,19 +1284,25 @@ fn collect_items_of_instance<'tcx>(
12601284
}
12611285
}
12621286

1263-
(used_items, mentioned_items)
1287+
if error_occurred {
1288+
let def_span = tcx.def_span(instance.def_id());
1289+
let err = tcx.dcx().span_delayed_bug(def_span, "constant evaluation error");
1290+
return Err(err);
1291+
}
1292+
1293+
Ok((used_items, mentioned_items))
12641294
}
12651295

12661296
fn items_of_instance<'tcx>(
12671297
tcx: TyCtxt<'tcx>,
12681298
(instance, mode): (Instance<'tcx>, CollectionMode),
1269-
) -> (&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]) {
1270-
let (used_items, mentioned_items) = collect_items_of_instance(tcx, instance, mode);
1299+
) -> Result<(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]), ErrorGuaranteed> {
1300+
let (used_items, mentioned_items) = collect_items_of_instance(tcx, instance, mode)?;
12711301

12721302
let used_items = tcx.arena.alloc_from_iter(used_items);
12731303
let mentioned_items = tcx.arena.alloc_from_iter(mentioned_items);
12741304

1275-
(used_items, mentioned_items)
1305+
Ok((used_items, mentioned_items))
12761306
}
12771307

12781308
/// `item` must be already monomorphized.
@@ -1651,6 +1681,7 @@ pub(crate) fn collect_crate_mono_items<'tcx>(
16511681
visited: MTLock::new(UnordSet::default()),
16521682
mentioned: MTLock::new(UnordSet::default()),
16531683
usage_map: MTLock::new(UsageMap::new()),
1684+
error_count: AtomicUsize::new(0),
16541685
};
16551686
let recursion_limit = tcx.recursion_limit();
16561687

compiler/rustc_monomorphize/src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::path::PathBuf;
33
use rustc_macros::{Diagnostic, LintDiagnostic};
44
use rustc_span::{Span, Symbol};
55

6+
pub(crate) const MAX_NUMBER_OF_ERRORS: usize = 100;
7+
68
#[derive(Diagnostic)]
79
#[diag(monomorphize_recursion_limit)]
810
pub(crate) struct RecursionLimit {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ check-pass
2+
3+
fn pass_to_ptr_call<T>(f: fn(T), x: T) {
4+
f(x);
5+
}
6+
7+
trait TrackedTrait {
8+
fn trait_tracked_unit_default(_: ()) {
9+
let location = std::panic::Location::caller();
10+
assert_eq!(location.file(), file!());
11+
}
12+
}
13+
14+
impl TrackedTrait for () {}
15+
16+
fn main() {
17+
pass_to_ptr_call(<() as TrackedTrait>::trait_tracked_unit_default, ());
18+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ check-pass
2+
3+
trait Foo<T> {
4+
fn print<'a>(&'a self) where T: 'a { println!("foo"); }
5+
}
6+
7+
impl<'a> Foo<&'a ()> for () { }
8+
9+
trait Bar: for<'a> Foo<&'a ()> { }
10+
11+
impl Bar for () {}
12+
13+
fn main() {
14+
(&() as &dyn Bar).print();
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ check-pass
2+
3+
// When -Zprint-mono-items=eager option was used, the following
4+
// code previously caused an ICE.
5+
6+
pub struct S();
7+
8+
pub trait X {
9+
fn unused3(var: i32) {
10+
println!("{}", var);
11+
}
12+
}
13+
14+
impl X for S {}
15+
16+
fn main() {}

tests/crashes/114484.rs renamed to tests/ui/too-errors-monomorphization-collector-issue-114484.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
//@ known-bug: #114484
1+
// Avoiding using `don't-check-compiler-stderr` the compiletest tool
2+
// emit `TRUNCATED, SHOWING THE FIRST {} BYTES` because the error message
3+
// is too long.
4+
5+
//@ build-fail
6+
//@ dont-check-compiler-stderr
7+
//@ error-pattern: reached the recursion limit finding the struct tail for `[u8; 256]`
8+
//@ error-pattern: as Pointee>::Metadata` has an unknown layout
9+
210
use std::marker::PhantomData;
311

412
trait MyTrait {
@@ -16,7 +24,7 @@ impl<T, const L: u8> VirtualWrapper<T, L> {
1624
impl<T: MyTrait + 'static, const L: u8> MyTrait for VirtualWrapper<T, L> {
1725
fn virtualize(&self) -> &dyn MyTrait {
1826
unsafe { virtualize_my_trait(L, self) }
19-
// unsafe { virtualize_my_trait(L, &self.0) } // <-- this code fixes the problem
27+
// unsafe { virtualize_my_trait(L, &self.0) } // <-- this code fixes the problem
2028
}
2129
}
2230

0 commit comments

Comments
 (0)