Skip to content

Commit cd6521e

Browse files
Merge #11360
11360: feat: Support `#![recursion_limit]` attribute r=Veykril a=WaffleLapkin ![Peek 2022-01-28 02-33](https://user-images.githubusercontent.com/38225716/151508617-ac802b53-f088-4cac-b260-2cd077f3d32c.gif) Resolves #8640 `@matklad` thanks, for the instructions, they were very helpful :) Co-authored-by: Maybe Waffle <[email protected]>
2 parents 93036aa + 6d18c5b commit cd6521e

File tree

6 files changed

+66
-7
lines changed

6 files changed

+66
-7
lines changed

crates/hir_def/src/body.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,6 @@ pub struct Expander {
5454
recursion_limit: usize,
5555
}
5656

57-
#[cfg(test)]
58-
static EXPANSION_RECURSION_LIMIT: Limit = Limit::new(32);
59-
60-
#[cfg(not(test))]
61-
static EXPANSION_RECURSION_LIMIT: Limit = Limit::new(128);
62-
6357
impl CfgExpander {
6458
pub(crate) fn new(
6559
db: &dyn DefDatabase,
@@ -101,7 +95,7 @@ impl Expander {
10195
db: &dyn DefDatabase,
10296
macro_call: ast::MacroCall,
10397
) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
104-
if EXPANSION_RECURSION_LIMIT.check(self.recursion_limit + 1).is_err() {
98+
if self.recursion_limit(db).check(self.recursion_limit + 1).is_err() {
10599
cov_mark::hit!(your_stack_belongs_to_me);
106100
return Ok(ExpandResult::str_err(
107101
"reached recursion limit during macro expansion".into(),
@@ -222,6 +216,17 @@ impl Expander {
222216
let file_local_id = self.ast_id_map.ast_id(item);
223217
AstId::new(self.current_file_id, file_local_id)
224218
}
219+
220+
fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit {
221+
let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _;
222+
223+
#[cfg(not(test))]
224+
return Limit::new(limit);
225+
226+
// Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
227+
#[cfg(test)]
228+
return Limit::new(std::cmp::min(32, limit));
229+
}
225230
}
226231

227232
#[derive(Debug)]

crates/hir_def/src/body/tests.rs

+18
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,24 @@ fn main() { n_nuple!(1,2,3); }
6161
);
6262
}
6363

64+
#[test]
65+
fn recursion_limit() {
66+
cov_mark::check!(your_stack_belongs_to_me);
67+
68+
lower(
69+
r#"
70+
#![recursion_limit = "2"]
71+
macro_rules! n_nuple {
72+
($e:tt) => ();
73+
($first:tt $($rest:tt)*) => {{
74+
n_nuple!($($rest)*)
75+
}};
76+
}
77+
fn main() { n_nuple!(1,2,3); }
78+
"#,
79+
);
80+
}
81+
6482
#[test]
6583
fn macro_resolve() {
6684
// Regression test for a path resolution bug introduced with inner item handling.

crates/hir_def/src/db.rs

+17
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,26 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
157157

158158
#[salsa::invoke(visibility::function_visibility_query)]
159159
fn function_visibility(&self, def: FunctionId) -> Visibility;
160+
161+
#[salsa::transparent]
162+
fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
160163
}
161164

162165
fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
163166
let _p = profile::span("crate_def_map:wait");
164167
db.crate_def_map_query(krate)
165168
}
169+
170+
pub struct CrateLimits {
171+
/// The maximum depth for potentially infinitely-recursive compile-time operations like macro expansion or auto-dereference.
172+
pub recursion_limit: u32,
173+
}
174+
175+
fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
176+
let def_map = db.crate_def_map(crate_id);
177+
178+
CrateLimits {
179+
// 128 is the default in rustc.
180+
recursion_limit: def_map.recursion_limit().unwrap_or(128),
181+
}
182+
}

crates/hir_def/src/nameres.rs

+7
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub struct DefMap {
114114
registered_tools: Vec<SmolStr>,
115115

116116
edition: Edition,
117+
recursion_limit: Option<u32>,
117118
diagnostics: Vec<DefDiagnostic>,
118119
}
119120

@@ -272,6 +273,7 @@ impl DefMap {
272273
block: None,
273274
krate,
274275
edition,
276+
recursion_limit: None,
275277
extern_prelude: FxHashMap::default(),
276278
exported_proc_macros: FxHashMap::default(),
277279
prelude: None,
@@ -461,6 +463,7 @@ impl DefMap {
461463
registered_tools,
462464
block: _,
463465
edition: _,
466+
recursion_limit: _,
464467
krate: _,
465468
prelude: _,
466469
root: _,
@@ -482,6 +485,10 @@ impl DefMap {
482485
pub fn diagnostics(&self) -> &[DefDiagnostic] {
483486
self.diagnostics.as_slice()
484487
}
488+
489+
pub fn recursion_limit(&self) -> Option<u32> {
490+
self.recursion_limit
491+
}
485492
}
486493

487494
impl ModuleData {

crates/hir_def/src/nameres/collector.rs

+11
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,17 @@ impl DefCollector<'_> {
271271
None => continue,
272272
};
273273

274+
if *attr_name == hir_expand::name![recursion_limit] {
275+
if let Some(input) = &attr.input {
276+
if let AttrInput::Literal(limit) = &**input {
277+
if let Ok(limit) = limit.parse() {
278+
self.def_map.recursion_limit = Some(limit);
279+
}
280+
}
281+
}
282+
continue;
283+
}
284+
274285
let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
275286
|| *attr_name == hir_expand::name![register_tool];
276287
if !attr_is_register_like {

crates/hir_expand/src/name.rs

+1
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ pub mod known {
270270
global_allocator,
271271
test,
272272
test_case,
273+
recursion_limit,
273274
// Safe intrinsics
274275
abort,
275276
add_with_overflow,

0 commit comments

Comments
 (0)