Unnamed repository; edit this file 'description' to name the repository.
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]>
bors[bot] 2022-01-28
parent 93036aa · parent 6d18c5b · commit cd6521e
-rw-r--r--crates/hir_def/src/body.rs19
-rw-r--r--crates/hir_def/src/body/tests.rs18
-rw-r--r--crates/hir_def/src/db.rs17
-rw-r--r--crates/hir_def/src/nameres.rs7
-rw-r--r--crates/hir_def/src/nameres/collector.rs11
-rw-r--r--crates/hir_expand/src/name.rs1
6 files changed, 66 insertions, 7 deletions
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 505b07cc8a..8488b4f0d0 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -54,12 +54,6 @@ pub struct Expander {
recursion_limit: usize,
}
-#[cfg(test)]
-static EXPANSION_RECURSION_LIMIT: Limit = Limit::new(32);
-
-#[cfg(not(test))]
-static EXPANSION_RECURSION_LIMIT: Limit = Limit::new(128);
-
impl CfgExpander {
pub(crate) fn new(
db: &dyn DefDatabase,
@@ -101,7 +95,7 @@ impl Expander {
db: &dyn DefDatabase,
macro_call: ast::MacroCall,
) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
- if EXPANSION_RECURSION_LIMIT.check(self.recursion_limit + 1).is_err() {
+ if self.recursion_limit(db).check(self.recursion_limit + 1).is_err() {
cov_mark::hit!(your_stack_belongs_to_me);
return Ok(ExpandResult::str_err(
"reached recursion limit during macro expansion".into(),
@@ -222,6 +216,17 @@ impl Expander {
let file_local_id = self.ast_id_map.ast_id(item);
AstId::new(self.current_file_id, file_local_id)
}
+
+ fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit {
+ let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _;
+
+ #[cfg(not(test))]
+ return Limit::new(limit);
+
+ // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
+ #[cfg(test)]
+ return Limit::new(std::cmp::min(32, limit));
+ }
}
#[derive(Debug)]
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index 673a753861..1d84da48fb 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -62,6 +62,24 @@ fn main() { n_nuple!(1,2,3); }
}
#[test]
+fn recursion_limit() {
+ cov_mark::check!(your_stack_belongs_to_me);
+
+ lower(
+ r#"
+#![recursion_limit = "2"]
+macro_rules! n_nuple {
+ ($e:tt) => ();
+ ($first:tt $($rest:tt)*) => {{
+ n_nuple!($($rest)*)
+ }};
+}
+fn main() { n_nuple!(1,2,3); }
+"#,
+ );
+}
+
+#[test]
fn macro_resolve() {
// Regression test for a path resolution bug introduced with inner item handling.
lower(
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 7946558311..f9dd935c4b 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -157,9 +157,26 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
#[salsa::invoke(visibility::function_visibility_query)]
fn function_visibility(&self, def: FunctionId) -> Visibility;
+
+ #[salsa::transparent]
+ fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
}
fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
let _p = profile::span("crate_def_map:wait");
db.crate_def_map_query(krate)
}
+
+pub struct CrateLimits {
+ /// The maximum depth for potentially infinitely-recursive compile-time operations like macro expansion or auto-dereference.
+ pub recursion_limit: u32,
+}
+
+fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
+ let def_map = db.crate_def_map(crate_id);
+
+ CrateLimits {
+ // 128 is the default in rustc.
+ recursion_limit: def_map.recursion_limit().unwrap_or(128),
+ }
+}
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 64929c02b4..ca4255c581 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -114,6 +114,7 @@ pub struct DefMap {
registered_tools: Vec<SmolStr>,
edition: Edition,
+ recursion_limit: Option<u32>,
diagnostics: Vec<DefDiagnostic>,
}
@@ -272,6 +273,7 @@ impl DefMap {
block: None,
krate,
edition,
+ recursion_limit: None,
extern_prelude: FxHashMap::default(),
exported_proc_macros: FxHashMap::default(),
prelude: None,
@@ -461,6 +463,7 @@ impl DefMap {
registered_tools,
block: _,
edition: _,
+ recursion_limit: _,
krate: _,
prelude: _,
root: _,
@@ -482,6 +485,10 @@ impl DefMap {
pub fn diagnostics(&self) -> &[DefDiagnostic] {
self.diagnostics.as_slice()
}
+
+ pub fn recursion_limit(&self) -> Option<u32> {
+ self.recursion_limit
+ }
}
impl ModuleData {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index dc75dc4d91..7f7213f4c3 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -271,6 +271,17 @@ impl DefCollector<'_> {
None => continue,
};
+ if *attr_name == hir_expand::name![recursion_limit] {
+ if let Some(input) = &attr.input {
+ if let AttrInput::Literal(limit) = &**input {
+ if let Ok(limit) = limit.parse() {
+ self.def_map.recursion_limit = Some(limit);
+ }
+ }
+ }
+ continue;
+ }
+
let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
|| *attr_name == hir_expand::name![register_tool];
if !attr_is_register_like {
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 4dcda0fcdc..c36bd09e2b 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -270,6 +270,7 @@ pub mod known {
global_allocator,
test,
test_case,
+ recursion_limit,
// Safe intrinsics
abort,
add_with_overflow,