Prevent panics when there is a cyclic dependency between closures

We didn't include them in the sorted closures list, therefore didn't analyze them, then failed to find them.
This commit is contained in:
Chayim Refael Friedman 2025-04-14 07:12:51 +03:00
parent 8365cf853e
commit cdc5ba30fc
3 changed files with 65 additions and 7 deletions

View File

@ -23,7 +23,7 @@ use hir_def::{
use hir_def::{Lookup, type_ref::TypeRefId};
use hir_expand::name::Name;
use intern::sym;
use rustc_hash::FxHashMap;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{SmallVec, smallvec};
use stdx::{format_to, never};
use syntax::utils::is_raw_identifier;
@ -107,9 +107,7 @@ impl InferenceContext<'_> {
)
.intern(Interner);
self.deferred_closures.entry(closure_id).or_default();
if let Some(c) = self.current_closure {
self.closure_dependencies.entry(c).or_default().push(closure_id);
}
self.add_current_closure_dependency(closure_id);
(Some(closure_id), closure_ty, None)
}
};
@ -1748,8 +1746,42 @@ impl InferenceContext<'_> {
}
}
}
assert!(deferred_closures.is_empty(), "we should have analyzed all closures");
result
}
pub(super) fn add_current_closure_dependency(&mut self, dep: ClosureId) {
if let Some(c) = self.current_closure {
if !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep) {
self.closure_dependencies.entry(c).or_default().push(dep);
}
}
fn dep_creates_cycle(
closure_dependencies: &FxHashMap<ClosureId, Vec<ClosureId>>,
visited: &mut FxHashSet<ClosureId>,
from: ClosureId,
to: ClosureId,
) -> bool {
if !visited.insert(from) {
return false;
}
if from == to {
return true;
}
if let Some(deps) = closure_dependencies.get(&to) {
for dep in deps {
if dep_creates_cycle(closure_dependencies, visited, from, *dep) {
return true;
}
}
}
false
}
}
}
/// Call this only when the last span in the stack isn't a split.

View File

@ -1705,9 +1705,7 @@ impl InferenceContext<'_> {
if let TyKind::Closure(c, _) =
self.table.resolve_completely(callee_ty.clone()).kind(Interner)
{
if let Some(par) = self.current_closure {
self.closure_dependencies.entry(par).or_default().push(*c);
}
self.add_current_closure_dependency(*c);
self.deferred_closures.entry(*c).or_default().push((
derefed_callee.clone(),
callee_ty.clone(),

View File

@ -1004,4 +1004,32 @@ fn foo() {
"#,
);
}
#[test]
fn closure_dependency_cycle_no_panic() {
check(
r#"
fn foo() {
let closure;
// ^^^^^^^ impl Fn()
closure = || {
closure();
};
}
fn bar() {
let closure1;
// ^^^^^^^^ impl Fn()
let closure2;
// ^^^^^^^^ impl Fn()
closure1 = || {
closure2();
};
closure2 = || {
closure1();
};
}
"#,
);
}
}