1
Fork 0

Auto merge of #72412 - VFLashM:issue-72408-nested-closures-exponential, r=tmandry

Issue 72408 nested closures exponential

This fixes #72408.

Nested closures were resulting in exponential compilation time.

This PR is enhancing asymptotic complexity, but also increasing the constant, so I would love to see perf run results.
This commit is contained in:
bors 2020-09-18 14:08:39 +00:00
commit fdc3405c20
22 changed files with 344 additions and 82 deletions

View file

@ -418,6 +418,29 @@ fn record_accesses<'a, 'tcx: 'a>(
inlining_map.lock_mut().record_accesses(caller, &accesses);
}
// Shrinks string by keeping prefix and suffix of given sizes.
fn shrink(s: String, before: usize, after: usize) -> String {
// An iterator of all byte positions including the end of the string.
let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len()));
let shrunk = format!(
"{before}...{after}",
before = &s[..positions().nth(before).unwrap_or(s.len())],
after = &s[positions().rev().nth(after).unwrap_or(0)..],
);
// Only use the shrunk version if it's really shorter.
// This also avoids the case where before and after slices overlap.
if shrunk.len() < s.len() { shrunk } else { s }
}
// Format instance name that is already known to be too long for rustc.
// Show only the first and last 32 characters to avoid blasting
// the user's terminal with thousands of lines of type-name.
fn shrunk_instance_name(instance: &Instance<'tcx>) -> String {
shrink(instance.to_string(), 32, 32)
}
fn check_recursion_limit<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
@ -440,7 +463,10 @@ fn check_recursion_limit<'tcx>(
// more than the recursion limit is assumed to be causing an
// infinite expansion.
if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) {
let error = format!("reached the recursion limit while instantiating `{}`", instance);
let error = format!(
"reached the recursion limit while instantiating `{}`",
shrunk_instance_name(&instance),
);
let mut err = tcx.sess.struct_span_fatal(span, &error);
err.span_note(
tcx.def_span(def_id),
@ -474,26 +500,9 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
//
// Bail out in these cases to avoid that bad user experience.
if !tcx.sess.type_length_limit().value_within_limit(type_length) {
// The instance name is already known to be too long for rustc.
// Show only the first and last 32 characters to avoid blasting
// the user's terminal with thousands of lines of type-name.
let shrink = |s: String, before: usize, after: usize| {
// An iterator of all byte positions including the end of the string.
let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len()));
let shrunk = format!(
"{before}...{after}",
before = &s[..positions().nth(before).unwrap_or(s.len())],
after = &s[positions().rev().nth(after).unwrap_or(0)..],
);
// Only use the shrunk version if it's really shorter.
// This also avoids the case where before and after slices overlap.
if shrunk.len() < s.len() { shrunk } else { s }
};
let msg = format!(
"reached the type-length limit while instantiating `{}`",
shrink(instance.to_string(), 32, 32)
shrunk_instance_name(&instance),
);
let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg);
diag.note(&format!(