Skip to content

Commit c09dfb5

Browse files
Improve type length check algorithm
1 parent 725c0bc commit c09dfb5

5 files changed

Lines changed: 50 additions & 25 deletions

File tree

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ fn collect_items_root<'tcx>(
357357
starting_item: Spanned<MonoItem<'tcx>>,
358358
state: &SharedState<'tcx>,
359359
recursion_limit: Limit,
360+
type_length_limit: Limit,
360361
) {
361362
if !state.visited.lock().insert(starting_item.node) {
362363
// We've been here already, no need to search again.
@@ -370,6 +371,7 @@ fn collect_items_root<'tcx>(
370371
&mut recursion_depths,
371372
recursion_limit,
372373
CollectionMode::UsedItems,
374+
type_length_limit,
373375
);
374376
}
375377

@@ -378,14 +380,18 @@ fn collect_items_root<'tcx>(
378380
///
379381
/// `mode` determined whether we are scanning for [used items][CollectionMode::UsedItems]
380382
/// or [mentioned items][CollectionMode::MentionedItems].
381-
#[instrument(skip(tcx, state, recursion_depths, recursion_limit), level = "debug")]
383+
#[instrument(
384+
skip(tcx, state, recursion_depths, recursion_limit, type_length_limit),
385+
level = "debug"
386+
)]
382387
fn collect_items_rec<'tcx>(
383388
tcx: TyCtxt<'tcx>,
384389
starting_item: Spanned<MonoItem<'tcx>>,
385390
state: &SharedState<'tcx>,
386391
recursion_depths: &mut DefIdMap<usize>,
387392
recursion_limit: Limit,
388393
mode: CollectionMode,
394+
type_length_limit: Limit,
389395
) {
390396
let mut used_items = MonoItems::new();
391397
let mut mentioned_items = MonoItems::new();
@@ -463,13 +469,14 @@ fn collect_items_rec<'tcx>(
463469
// Sanity check whether this ended up being collected accidentally
464470
debug_assert!(tcx.should_codegen_locally(instance));
465471

466-
// Keep track of the monomorphization recursion depth
472+
// Check for recursive monomorphization before collecting uses
467473
recursion_depth_reset = Some(check_recursion_limit(
468474
tcx,
469475
instance,
470476
starting_item.span,
471477
recursion_depths,
472478
recursion_limit,
479+
type_length_limit,
473480
));
474481

475482
rustc_data_structures::stack::ensure_sufficient_stack(|| {
@@ -595,6 +602,7 @@ fn collect_items_rec<'tcx>(
595602
recursion_depths,
596603
recursion_limit,
597604
CollectionMode::UsedItems,
605+
type_length_limit,
598606
);
599607
}
600608
}
@@ -609,6 +617,7 @@ fn collect_items_rec<'tcx>(
609617
recursion_depths,
610618
recursion_limit,
611619
CollectionMode::MentionedItems,
620+
type_length_limit,
612621
);
613622
}
614623

@@ -652,6 +661,7 @@ fn check_recursion_limit<'tcx>(
652661
span: Span,
653662
recursion_depths: &mut DefIdMap<usize>,
654663
recursion_limit: Limit,
664+
type_length_limit: Limit,
655665
) -> (DefId, usize) {
656666
let def_id = instance.def_id();
657667
let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0);
@@ -665,17 +675,17 @@ fn check_recursion_limit<'tcx>(
665675
recursion_depth
666676
};
667677

668-
// Rust code can create exponentially-long types using only a
669-
// polynomial recursion depth. Start checking the type length before
670-
// the depth limit is reached, to avoid hanging on enormous instance
671-
// arguments.
672-
let type_length_check_depth = (recursion_limit / 8).0.max(4);
678+
// Recursive monomorphization can grow instance args exponentially with polynomial
679+
// recursion depth. Start checking type lengths around `ilog2(type_length_limit.0)`
680+
// to avoid hanging on enormous instance arguments.
681+
let type_length_check_depth = type_length_limit.0.checked_ilog2().unwrap_or(0).max(4) as usize;
673682
let recursive_type_growth_limit_reached = recursion_depth >= type_length_check_depth
674-
&& !tcx.type_length_limit().value_within_limit(type_length(instance.args));
683+
&& !type_length_limit.value_within_limit(type_length(instance.args));
675684

676-
// Code that needs to instantiate the same function recursively
677-
// more than the recursion limit, or with type arguments that exceed the
678-
// type length limit, is assumed to be causing an infinite expansion.
685+
// Code that needs to instantiate the same function recursively more
686+
// than the recursion limit is assumed to be causing an infinite
687+
// expansion. Bail out earlier if recursive instantiations have already
688+
// produced instance args exceeding the type length limit.
679689
if !recursion_limit.value_within_limit(adjusted_recursion_depth)
680690
|| recursive_type_growth_limit_reached
681691
{
@@ -1848,9 +1858,17 @@ pub(crate) fn collect_crate_mono_items<'tcx>(
18481858
};
18491859
let recursion_limit = tcx.recursion_limit();
18501860

1861+
let type_length_limit = tcx.type_length_limit();
1862+
18511863
tcx.sess.time("monomorphization_collector_graph_walk", || {
18521864
par_for_each_in(roots, |root| {
1853-
collect_items_root(tcx, dummy_spanned(*root), &state, recursion_limit);
1865+
collect_items_root(
1866+
tcx,
1867+
dummy_spanned(*root),
1868+
&state,
1869+
recursion_limit,
1870+
type_length_limit,
1871+
);
18541872
});
18551873
});
18561874

tests/ui/codegen/overflow-during-mono.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
//@ build-fail
22
//@ compile-flags: -Zwrite-long-types-to-disk=yes
33

4-
//~? ERROR reached the recursion limit while instantiating
5-
64
#![recursion_limit = "32"]
75

86
fn quicksort<It: Clone + Iterator<Item = T>, I: IntoIterator<IntoIter = It>, T: Ord>(
@@ -16,6 +14,7 @@ fn quicksort<It: Clone + Iterator<Item = T>, I: IntoIterator<IntoIter = It>, T:
1614
let greater = i.filter(|y| &x <= y);
1715

1816
let mut v = quicksort(less);
17+
//~^ ERROR reached the recursion limit while instantiating
1918
let u = quicksort(greater);
2019
v.push(x);
2120
v.extend(u);

tests/ui/codegen/overflow-during-mono.stderr

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
error: reached the recursion limit while instantiating `<Filter<Filter<_, _>, _> as Iterator>::try_fold::<(), _, _>`
2-
--> $SRC_DIR/core/src/iter/adapters/filter.rs:LL:COL
1+
error: reached the recursion limit while instantiating `quicksort::<Filter<Filter<Filter<Filter<_, _>, _>, _>, _>, _, i32>`
2+
--> $DIR/overflow-during-mono.rs:16:25
33
|
4-
note: `<Filter<I, P> as Iterator>::try_fold` defined here
5-
--> $SRC_DIR/core/src/iter/adapters/filter.rs:LL:COL
4+
LL | let mut v = quicksort(less);
5+
| ^^^^^^^^^^^^^^^
6+
|
7+
note: `quicksort` defined here
8+
--> $DIR/overflow-during-mono.rs:6:1
9+
|
10+
LL | / fn quicksort<It: Clone + Iterator<Item = T>, I: IntoIterator<IntoIter = It>, T: Ord>(
11+
LL | | i: I,
12+
LL | | ) -> Vec<T> {
13+
| |___________^
614
= note: the full name for the type has been written to '$TEST_BUILD_DIR/overflow-during-mono.long-type-$LONG_TYPE_HASH.txt'
715
= note: consider using `--verbose` to print the full type name to the console
816

tests/ui/recursion/infinite-instantiation-via-nested-closures.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ impl C {
3838
pub fn matches<F: Fn()>(&self, f: &F) {
3939
let &C(ref base) = self;
4040
base.matches(&|| C(base.clone()).matches(f))
41-
//~^ ERROR reached the recursion limit while instantiating
4241
}
4342
}
4443

@@ -49,6 +48,7 @@ impl D {
4948
pub fn matches<F: Fn()>(&self, f: &F) {
5049
let &D(ref a) = self;
5150
a.matches(f)
51+
//~^ ERROR reached the recursion limit while instantiating
5252
}
5353
}
5454

tests/ui/recursion/infinite-instantiation-via-nested-closures.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
error: reached the recursion limit while instantiating `D::matches::<{closure@$DIR/infinite-instantiation-via-nested-closures.rs:40:23: 40:25}>`
2-
--> $DIR/infinite-instantiation-via-nested-closures.rs:40:9
1+
error: reached the recursion limit while instantiating `A::matches::<{closure@$DIR/infinite-instantiation-via-nested-closures.rs:40:23: 40:25}>`
2+
--> $DIR/infinite-instantiation-via-nested-closures.rs:50:9
33
|
4-
LL | base.matches(&|| C(base.clone()).matches(f))
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | a.matches(f)
5+
| ^^^^^^^^^^^^
66
|
7-
note: `D::matches` defined here
8-
--> $DIR/infinite-instantiation-via-nested-closures.rs:49:5
7+
note: `A::matches` defined here
8+
--> $DIR/infinite-instantiation-via-nested-closures.rs:13:5
99
|
1010
LL | pub fn matches<F: Fn()>(&self, f: &F) {
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)