From 82920f36c47fb649858a31caf840e29088dcd8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 26 Feb 2020 23:43:49 +0100 Subject: [PATCH] Don't unwind when hitting the macro expansion recursion limit --- src/librustc_expand/base.rs | 2 ++ src/librustc_expand/expand.rs | 16 ++++++++++++---- src/librustc_interface/passes.rs | 13 +++++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs index 74c304c96b9a4..0fc477bbd0b4c 100644 --- a/src/librustc_expand/base.rs +++ b/src/librustc_expand/base.rs @@ -922,6 +922,7 @@ pub struct ExpansionData { pub struct ExtCtxt<'a> { pub parse_sess: &'a ParseSess, pub ecfg: expand::ExpansionConfig<'a>, + pub reduced_recursion_limit: Option, pub root_path: PathBuf, pub resolver: &'a mut dyn Resolver, pub current_expansion: ExpansionData, @@ -940,6 +941,7 @@ impl<'a> ExtCtxt<'a> { ExtCtxt { parse_sess, ecfg, + reduced_recursion_limit: None, resolver, extern_mod_loaded, root_path: PathBuf::new(), diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs index b6cc192cc33d6..4f568e5456c72 100644 --- a/src/librustc_expand/expand.rs +++ b/src/librustc_expand/expand.rs @@ -17,7 +17,7 @@ use rustc_ast::util::map_in_place::MapInPlace; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; -use rustc_errors::{Applicability, FatalError, PResult}; +use rustc_errors::{Applicability, PResult}; use rustc_feature::Features; use rustc_parse::parser::Parser; use rustc_parse::validate_attr; @@ -645,7 +645,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { )) .emit(); self.cx.trace_macros_diag(); - FatalError.raise(); } /// A macro's expansion does not fit in this fragment kind. @@ -665,8 +664,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> { invoc: Invocation, ext: &SyntaxExtensionKind, ) -> ExpandResult { - if self.cx.current_expansion.depth > self.cx.ecfg.recursion_limit { - self.error_recursion_limit_reached(); + let recursion_limit = + self.cx.reduced_recursion_limit.unwrap_or(self.cx.ecfg.recursion_limit); + if self.cx.current_expansion.depth > recursion_limit { + if self.cx.reduced_recursion_limit.is_none() { + self.error_recursion_limit_reached(); + } + + // Reduce the recursion limit by half each time it triggers. + self.cx.reduced_recursion_limit = Some(recursion_limit / 2); + + return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span())); } let (fragment_kind, span) = (invoc.fragment_kind, invoc.span()); diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index ee323b204b7a0..22089f9de312d 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -311,6 +311,8 @@ fn configure_and_expand_inner<'a>( ecx.parse_sess.missing_fragment_specifiers.borrow().iter().cloned().collect(); missing_fragment_specifiers.sort(); + let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); + for span in missing_fragment_specifiers { let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER; let msg = "missing fragment specifier"; @@ -319,8 +321,15 @@ fn configure_and_expand_inner<'a>( if cfg!(windows) { env::set_var("PATH", &old_path); } - krate - }); + + if recursion_limit_hit { + // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed + // with a large AST + Err(ErrorReported) + } else { + Ok(krate) + } + })?; sess.time("maybe_building_test_harness", || { rustc_builtin_macros::test_harness::inject(