diff --git a/compiler/rustc_interface/src/limits.rs b/compiler/rustc_interface/src/limits.rs index e0fc91f3b723b..8ae0743886ce5 100644 --- a/compiler/rustc_interface/src/limits.rs +++ b/compiler/rustc_interface/src/limits.rs @@ -11,13 +11,13 @@ use rustc_hir::limit::Limit; use rustc_hir::{Attribute, find_attr}; use rustc_middle::query::Providers; -use rustc_session::Limits; +use rustc_session::{Limits, Session}; pub(crate) fn provide(providers: &mut Providers) { providers.limits = |tcx, ()| { let attrs = tcx.hir_krate_attrs(); Limits { - recursion_limit: get_recursion_limit(tcx.hir_krate_attrs()), + recursion_limit: get_recursion_limit(tcx.hir_krate_attrs(), tcx.sess), move_size_limit: find_attr!(attrs, MoveSizeLimit { limit, .. } => *limit) .unwrap_or(Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0))), type_length_limit: find_attr!(attrs, TypeLengthLimit { limit, .. } => *limit) @@ -30,6 +30,13 @@ pub(crate) fn provide(providers: &mut Providers) { } // This one is separate because it must be read prior to macro expansion. -pub(crate) fn get_recursion_limit(attrs: &[Attribute]) -> Limit { - find_attr!(attrs, RecursionLimit { limit, .. } => *limit).unwrap_or(Limit::new(128)) +pub(crate) fn get_recursion_limit(attrs: &[Attribute], sess: &Session) -> Limit { + let limit_from_crate = + find_attr!(attrs, RecursionLimit { limit, .. } => limit.0).unwrap_or(128); + Limit::new( + sess.opts + .unstable_opts + .min_recursion_limit + .map_or(limit_from_crate, |min| min.max(limit_from_crate)), + ) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a280b2a2a6bf7..5cf4b1546320f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1429,5 +1429,5 @@ fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit // So, no lints here to avoid duplicates. ShouldEmit::EarlyFatal { also_emit_lints: false }, ); - crate::limits::get_recursion_limit(attr.as_slice()) + crate::limits::get_recursion_limit(attr.as_slice(), sess) } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 88056a0db966d..6c2924dfe9bd8 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -822,6 +822,7 @@ fn test_unstable_options_tracking_hash() { tracked!(maximal_hir_to_mir_coverage, true); tracked!(merge_functions, Some(MergeFunctions::Disabled)); tracked!(min_function_alignment, Some(Align::EIGHT)); + tracked!(min_recursion_limit, Some(256)); tracked!(mir_emit_retag, true); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); tracked!(mir_opt_level, Some(4)); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 74b3aa11d0d80..0bc432a0ff06c 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2456,6 +2456,8 @@ options! { "the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"), min_function_alignment: Option = (None, parse_align, [TRACKED], "align all functions to at least this many bytes. Must be a power of 2"), + min_recursion_limit: Option = (None, parse_opt_number, [TRACKED], + "set a minimum recursion limit (final limit = max(this, recursion_limit_from_crate))"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), diff --git a/src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md b/src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md new file mode 100644 index 0000000000000..c00cdb95c0320 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md @@ -0,0 +1,12 @@ +# `min-recursion-limit` + +This flag sets a minimum recursion limit for the compiler. The final recursion limit is calculated as `max(min_recursion_limit, recursion_limit_from_crate)`. This cannot ever lower the recursion limit. Unless the current crate has an explicitly low `recursion_limit` attribute, any value less than the current default does not have an effect. + +The recursion limit affects (among other things): + +- macro expansion +- the trait solver +- const evaluation +- query depth + +This flag is particularly useful when using the next trait solver (`-Z next-solver`), which may require a higher recursion limit for crates that were compiled successfully with the old solver. diff --git a/tests/ui/recursion/recursion_limit/min-recursion-limit-attr-lower-than-default.rs b/tests/ui/recursion/recursion_limit/min-recursion-limit-attr-lower-than-default.rs new file mode 100644 index 0000000000000..d9de45b5735e4 --- /dev/null +++ b/tests/ui/recursion/recursion_limit/min-recursion-limit-attr-lower-than-default.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Z min-recursion-limit=0 +//@ check-pass + +// Checks that `min-recursion-limit` cannot lower the default recursion limit + +macro_rules! count { + () => {}; + ($_:tt $($rest:tt)*) => { count!($($rest)*) }; +} + +fn main() { + // 100 + count!( + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + ); +} diff --git a/tests/ui/recursion/recursion_limit/min-recursion-limit-attr-wins.rs b/tests/ui/recursion/recursion_limit/min-recursion-limit-attr-wins.rs new file mode 100644 index 0000000000000..44c9f76f3785b --- /dev/null +++ b/tests/ui/recursion/recursion_limit/min-recursion-limit-attr-wins.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -Z min-recursion-limit=256 +//@ check-pass +#![recursion_limit = "128"] + +macro_rules! count { + () => {}; + ($_:tt $($rest:tt)*) => { count!($($rest)*) }; +} + +fn main() { + // 200 + count!( + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + ); +} diff --git a/tests/ui/recursion/recursion_limit/min-recursion-limit-cli-wins.rs b/tests/ui/recursion/recursion_limit/min-recursion-limit-cli-wins.rs new file mode 100644 index 0000000000000..10dc9cfe06b2f --- /dev/null +++ b/tests/ui/recursion/recursion_limit/min-recursion-limit-cli-wins.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -Z min-recursion-limit=64 +//@ check-pass +#![recursion_limit = "256"] + +macro_rules! count { + () => {}; + ($_:tt $($rest:tt)*) => { count!($($rest)*) }; +} + +fn main() { + // 200 + count!( + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + ); +} diff --git a/tests/ui/recursion/recursion_limit/min-recursion-limit-no-attr.rs b/tests/ui/recursion/recursion_limit/min-recursion-limit-no-attr.rs new file mode 100644 index 0000000000000..f918ec86c1411 --- /dev/null +++ b/tests/ui/recursion/recursion_limit/min-recursion-limit-no-attr.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Z min-recursion-limit=256 +//@ check-pass + +macro_rules! count { + () => {}; + ($_:tt $($rest:tt)*) => { count!($($rest)*) }; +} + +fn main() { + // 200 + count!( + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a a a a + ); +}