From 9f110aeef41f5c3597b56d57de345e011fb1f6a5 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Wed, 12 Apr 2023 12:48:22 +0900 Subject: [PATCH] fix: infinite recursion bug --- crates/erg_compiler/context/eval.rs | 4 ++-- crates/erg_compiler/context/generalize.rs | 16 ++++++++++++---- crates/erg_compiler/context/inquire.rs | 14 +++++++------- crates/erg_compiler/ty/free.rs | 7 +++++++ crates/erg_compiler/ty/mod.rs | 2 +- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index 666da2cab..40b42ad59 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -1119,8 +1119,8 @@ impl Context { level: usize, t_loc: &impl Locational, ) -> EvalResult { - if lhs == Never { - return Ok(Never); + if let Never | Failure = lhs { + return Ok(lhs); } // Currently Erg does not allow projection-types to be evaluated with type variables included. // All type variables will be dereferenced or fail. diff --git a/crates/erg_compiler/context/generalize.rs b/crates/erg_compiler/context/generalize.rs index 4e6e03c90..54eb4db49 100644 --- a/crates/erg_compiler/context/generalize.rs +++ b/crates/erg_compiler/context/generalize.rs @@ -516,10 +516,18 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> { // we need to force linking to avoid infinite loop // e.g. fv == ?T(<: Int, :> Add(?T)) // fv == ?T(:> ?T.Output, <: Add(Int)) - if sub_t.contains(&Type::FreeVar(fv.clone())) { - fv.forced_undoable_link(&super_t); - } else { - fv.forced_undoable_link(&sub_t); + let fv_t = Type::FreeVar(fv.clone()); + match (sub_t.contains(&fv_t), super_t.contains(&fv_t)) { + // REVIEW: to prevent infinite recursion, but this may cause a nonsense error + (true, true) => { + fv.dummy_link(); + } + (true, false) => { + fv.forced_undoable_link(&super_t); + } + (false, true | false) => { + fv.forced_undoable_link(&sub_t); + } } let res = self.validate_subsup(sub_t, super_t); fv.undo(); diff --git a/crates/erg_compiler/context/inquire.rs b/crates/erg_compiler/context/inquire.rs index 69950fb45..162309099 100644 --- a/crates/erg_compiler/context/inquire.rs +++ b/crates/erg_compiler/context/inquire.rs @@ -2786,22 +2786,22 @@ impl Context { } fn get_proj_candidates(&self, lhs: &Type, rhs: &Str) -> Set { - #[allow(clippy::single_match)] match lhs { Type::FreeVar(fv) => { if let Some(sup) = fv.get_super() { if self.is_trait(&sup) { - return self.get_trait_proj_candidates(&sup, rhs); + self.get_trait_proj_candidates(&sup, rhs) } else { - return self - .eval_proj(sup, rhs.clone(), self.level, &()) - .map_or(set! {}, |t| set! {t}); + self.eval_proj(sup, rhs.clone(), self.level, &()) + .map_or(set! {}, |t| set! {t}) } + } else { + set! {} } } - _ => {} + Type::Failure | Type::Never => set! { lhs.clone() }, + _ => set! {}, } - set! {} } fn get_trait_proj_candidates(&self, trait_: &Type, rhs: &Str) -> Set { diff --git a/crates/erg_compiler/ty/free.rs b/crates/erg_compiler/ty/free.rs index f17c95d5b..14aff5a3b 100644 --- a/crates/erg_compiler/ty/free.rs +++ b/crates/erg_compiler/ty/free.rs @@ -864,6 +864,13 @@ impl Free { } } + pub fn get_linked(&self) -> Option { + match &*self.borrow() { + FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => Some(t.clone()), + FreeKind::Unbound { .. } | FreeKind::NamedUnbound { .. } => None, + } + } + pub fn detach(&self) -> Self { match self.clone().unwrap_unbound() { (Some(name), lev, constraint) => Self::new_named_unbound(name, lev, constraint), diff --git a/crates/erg_compiler/ty/mod.rs b/crates/erg_compiler/ty/mod.rs index d30e8c392..37a05ac95 100644 --- a/crates/erg_compiler/ty/mod.rs +++ b/crates/erg_compiler/ty/mod.rs @@ -2248,7 +2248,7 @@ impl Type { Self::FreeVar(fv) if fv.is_generalized() => true, Self::FreeVar(fv) => { if let Some((sub, sup)) = fv.get_subsup() { - fv.undoable_link(&Type::Never); + fv.dummy_link(); let res_sub = sub.has_qvar(); let res_sup = sup.has_qvar(); fv.undo();