Skip to content

Commit 79ba337

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

File tree

10 files changed

+488
-27
lines changed

10 files changed

+488
-27
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 & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,11 @@
206206
//! regardless of whether it is actually needed or not.
207207
208208
use std::path::PathBuf;
209+
use std::sync::atomic::Ordering;
209210

210211
use rustc_attr_parsing::InlineAttr;
211212
use rustc_data_structures::fx::FxIndexMap;
212-
use rustc_data_structures::sync::{LRef, MTLock, par_for_each_in};
213+
use rustc_data_structures::sync::{AtomicU32, LRef, MTLock, par_for_each_in};
213214
use rustc_data_structures::unord::{UnordMap, UnordSet};
214215
use rustc_hir as hir;
215216
use rustc_hir::def::DefKind;
@@ -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: AtomicU32,
256261
}
257262

258263
pub(crate) struct UsageMap<'tcx> {
@@ -466,9 +471,15 @@ fn collect_items_rec<'tcx>(
466471
));
467472

468473
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());
474+
match tcx.items_of_instance((instance, mode)) {
475+
Ok((used, mentioned)) => {
476+
used_items.extend(used.into_iter().copied());
477+
mentioned_items.extend(mentioned.into_iter().copied());
478+
}
479+
Err(_) => {
480+
state.error_count.fetch_add(1, Ordering::Relaxed);
481+
}
482+
};
472483
});
473484
}
474485
MonoItem::GlobalAsm(item_id) => {
@@ -527,6 +538,15 @@ fn collect_items_rec<'tcx>(
527538
span: starting_item.span,
528539
formatted_item,
529540
});
541+
if state.error_count.load(Ordering::Relaxed) > MAX_NUMBER_OF_ERRORS {
542+
tcx.dcx().span_fatal(
543+
starting_item.span,
544+
format!(
545+
"aborting after reaching the limit of {} errors during monomorphization",
546+
MAX_NUMBER_OF_ERRORS
547+
),
548+
);
549+
}
530550
}
531551
// Only updating `usage_map` for used items as otherwise we may be inserting the same item
532552
// multiple times (if it is first 'mentioned' and then later actuall used), and the usage map
@@ -626,6 +646,8 @@ struct MirUsedCollector<'a, 'tcx> {
626646
/// Note that this contains *not-monomorphized* items!
627647
used_mentioned_items: &'a mut UnordSet<MentionedItem<'tcx>>,
628648
instance: Instance<'tcx>,
649+
/// If an error is encountered during const evaluation.
650+
tained_by_errors: Option<ErrorGuaranteed>,
629651
}
630652

631653
impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
@@ -658,9 +680,10 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
658680
"collection encountered polymorphic constant: {:?}",
659681
const_
660682
),
661-
Err(err @ ErrorHandled::Reported(..)) => {
683+
Err(err @ ErrorHandled::Reported(reported, _)) => {
662684
err.emit_note(self.tcx);
663-
return None;
685+
self.tained_by_errors = Some(reported.into());
686+
None
664687
}
665688
}
666689
}
@@ -1211,7 +1234,7 @@ fn collect_items_of_instance<'tcx>(
12111234
tcx: TyCtxt<'tcx>,
12121235
instance: Instance<'tcx>,
12131236
mode: CollectionMode,
1214-
) -> (MonoItems<'tcx>, MonoItems<'tcx>) {
1237+
) -> Result<(MonoItems<'tcx>, MonoItems<'tcx>), ErrorGuaranteed> {
12151238
// This item is getting monomorphized, do mono-time checks.
12161239
tcx.ensure().check_mono_item(instance);
12171240

@@ -1235,6 +1258,7 @@ fn collect_items_of_instance<'tcx>(
12351258
used_items: &mut used_items,
12361259
used_mentioned_items: &mut used_mentioned_items,
12371260
instance,
1261+
tained_by_errors: None,
12381262
};
12391263

12401264
if mode == CollectionMode::UsedItems {
@@ -1260,19 +1284,23 @@ fn collect_items_of_instance<'tcx>(
12601284
}
12611285
}
12621286

1263-
(used_items, mentioned_items)
1287+
if let Some(err) = collector.tained_by_errors {
1288+
return Err(err);
1289+
}
1290+
1291+
Ok((used_items, mentioned_items))
12641292
}
12651293

12661294
fn items_of_instance<'tcx>(
12671295
tcx: TyCtxt<'tcx>,
12681296
(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);
1297+
) -> Result<(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]), ErrorGuaranteed> {
1298+
let (used_items, mentioned_items) = collect_items_of_instance(tcx, instance, mode)?;
12711299

12721300
let used_items = tcx.arena.alloc_from_iter(used_items);
12731301
let mentioned_items = tcx.arena.alloc_from_iter(mentioned_items);
12741302

1275-
(used_items, mentioned_items)
1303+
Ok((used_items, mentioned_items))
12761304
}
12771305

12781306
/// `item` must be already monomorphized.
@@ -1651,6 +1679,7 @@ pub(crate) fn collect_crate_mono_items<'tcx>(
16511679
visited: MTLock::new(UnordSet::default()),
16521680
mentioned: MTLock::new(UnordSet::default()),
16531681
usage_map: MTLock::new(UsageMap::new()),
1682+
error_count: AtomicU32::new(0),
16541683
};
16551684
let recursion_limit = tcx.recursion_limit();
16561685

compiler/rustc_monomorphize/src/errors.rs

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

6+
// This constant should be used to limit the number of post-monomorphization
7+
// errors that are emitted during the monomorphization process preventing the
8+
// process from emitting too many errors, which could cause the process to
9+
// hang indefinitely.
10+
//
11+
// It is set to 10 because it is a reasonable number of
12+
// post-monomorphization errors to emit before stopping
13+
// the process.
14+
pub(crate) const MAX_NUMBER_OF_ERRORS: u32 = 10;
15+
616
#[derive(Diagnostic)]
717
#[diag(monomorphize_recursion_limit)]
818
pub(crate) struct RecursionLimit {

src/librustdoc/html/templates/type_layout.html

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ <h2 id="layout" class="section-header"> {# #}
2828
{% endfor %}
2929
</ul>
3030
{% endif %}
31-
{# This kind of layout error can occur with valid code, e.g. if you try to
32-
get the layout of a generic type such as `Vec<T>`. #}
31+
{# This kind of layout error can occur with valid code.
32+
It can happen for various reasons, e.g., if you try to get
33+
the layout of an array with a length that is not a constant
34+
and it has not a generic parameter such as
35+
`[(); size_of::<<() as Trait>::Assoc>()]`. #}
3336
{% when Err(LayoutError::Unknown(_)) %}
3437
<p> {# #}
35-
<strong>Note:</strong> Unable to compute type layout, {#+ #}
36-
possibly due to this type having generic parameters. {#+ #}
37-
Layout can only be computed for concrete, fully-instantiated types. {# #}
38+
<strong>Note:</strong> Unable to compute type layout. {#+ #}
39+
It can happen for various reasons. {# #}
3840
</p>
3941
{# This kind of error probably can't happen with valid code, but we don't
4042
want to panic and prevent the docs from building, so we just let the
@@ -44,13 +46,13 @@ <h2 id="layout" class="section-header"> {# #}
4446
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
4547
the type was too big. {# #}
4648
</p>
47-
{# This kind of error probably can't happen with valid code, but we don't
48-
want to panic and prevent the docs from building, so we just let the
49-
user know that we couldn't compute the layout. #}
49+
{# This kind of layout error can occur with valid code, e.g. if you try to
50+
get the layout of a generic type such as `Vec<T>`. #}
5051
{% when Err(LayoutError::TooGeneric(_)) %}
5152
<p> {# #}
52-
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
53-
the type was too generic. {# #}
53+
<strong>Note:</strong> Unable to compute type layout, {#+ #}
54+
possibly due to this type having generic parameters. {#+ #}
55+
Layout can only be computed for concrete, fully-instantiated types. {# #}
5456
</p>
5557
{% when Err(LayoutError::ReferencesError(_)) %}
5658
<p> {# #}
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: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
//@ known-bug: #114484
1+
//@ build-fail
2+
23
use std::marker::PhantomData;
34

45
trait MyTrait {
@@ -15,8 +16,8 @@ impl<T, const L: u8> VirtualWrapper<T, L> {
1516

1617
impl<T: MyTrait + 'static, const L: u8> MyTrait for VirtualWrapper<T, L> {
1718
fn virtualize(&self) -> &dyn MyTrait {
18-
unsafe { virtualize_my_trait(L, self) }
19-
// unsafe { virtualize_my_trait(L, &self.0) } // <-- this code fixes the problem
19+
unsafe { virtualize_my_trait(L, self) } //~ ERROR aborting after reaching the limit of 10 errors during monomorphization
20+
// unsafe { virtualize_my_trait(L, &self.0) } // <-- this code fixes the problem
2021
}
2122
}
2223

0 commit comments

Comments
 (0)