Skip to content

Commit 82920f3

Browse files
committed
Don't unwind when hitting the macro expansion recursion limit
1 parent 3c6f982 commit 82920f3

File tree

3 files changed

+25
-6
lines changed

3 files changed

+25
-6
lines changed

src/librustc_expand/base.rs

+2
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,7 @@ pub struct ExpansionData {
922922
pub struct ExtCtxt<'a> {
923923
pub parse_sess: &'a ParseSess,
924924
pub ecfg: expand::ExpansionConfig<'a>,
925+
pub reduced_recursion_limit: Option<usize>,
925926
pub root_path: PathBuf,
926927
pub resolver: &'a mut dyn Resolver,
927928
pub current_expansion: ExpansionData,
@@ -940,6 +941,7 @@ impl<'a> ExtCtxt<'a> {
940941
ExtCtxt {
941942
parse_sess,
942943
ecfg,
944+
reduced_recursion_limit: None,
943945
resolver,
944946
extern_mod_loaded,
945947
root_path: PathBuf::new(),

src/librustc_expand/expand.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_ast::util::map_in_place::MapInPlace;
1717
use rustc_ast::visit::{self, AssocCtxt, Visitor};
1818
use rustc_ast_pretty::pprust;
1919
use rustc_attr::{self as attr, is_builtin_attr, HasAttrs};
20-
use rustc_errors::{Applicability, FatalError, PResult};
20+
use rustc_errors::{Applicability, PResult};
2121
use rustc_feature::Features;
2222
use rustc_parse::parser::Parser;
2323
use rustc_parse::validate_attr;
@@ -645,7 +645,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
645645
))
646646
.emit();
647647
self.cx.trace_macros_diag();
648-
FatalError.raise();
649648
}
650649

651650
/// A macro's expansion does not fit in this fragment kind.
@@ -665,8 +664,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
665664
invoc: Invocation,
666665
ext: &SyntaxExtensionKind,
667666
) -> ExpandResult<AstFragment, Invocation> {
668-
if self.cx.current_expansion.depth > self.cx.ecfg.recursion_limit {
669-
self.error_recursion_limit_reached();
667+
let recursion_limit =
668+
self.cx.reduced_recursion_limit.unwrap_or(self.cx.ecfg.recursion_limit);
669+
if self.cx.current_expansion.depth > recursion_limit {
670+
if self.cx.reduced_recursion_limit.is_none() {
671+
self.error_recursion_limit_reached();
672+
}
673+
674+
// Reduce the recursion limit by half each time it triggers.
675+
self.cx.reduced_recursion_limit = Some(recursion_limit / 2);
676+
677+
return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span()));
670678
}
671679

672680
let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());

src/librustc_interface/passes.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ fn configure_and_expand_inner<'a>(
311311
ecx.parse_sess.missing_fragment_specifiers.borrow().iter().cloned().collect();
312312
missing_fragment_specifiers.sort();
313313

314+
let recursion_limit_hit = ecx.reduced_recursion_limit.is_some();
315+
314316
for span in missing_fragment_specifiers {
315317
let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER;
316318
let msg = "missing fragment specifier";
@@ -319,8 +321,15 @@ fn configure_and_expand_inner<'a>(
319321
if cfg!(windows) {
320322
env::set_var("PATH", &old_path);
321323
}
322-
krate
323-
});
324+
325+
if recursion_limit_hit {
326+
// If we hit a recursion limit, exit early to avoid later passes getting overwhelmed
327+
// with a large AST
328+
Err(ErrorReported)
329+
} else {
330+
Ok(krate)
331+
}
332+
})?;
324333

325334
sess.time("maybe_building_test_harness", || {
326335
rustc_builtin_macros::test_harness::inject(

0 commit comments

Comments
 (0)