Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/closure.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/closure.rs | 40 |
1 files changed, 36 insertions, 4 deletions
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 175f0c7bd7..e6cd1b9990 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -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. |