From d90922c190fa368738c10e7e12c71788e8940935 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Thu, 26 Sep 2024 16:45:21 +0900 Subject: [PATCH] fix: recursive type bug --- crates/erg_compiler/context/compare.rs | 32 ++++++++++++++++++++--- crates/erg_compiler/context/eval.rs | 3 +++ crates/erg_compiler/context/generalize.rs | 7 ++++- crates/erg_compiler/ty/free.rs | 27 +++++++++++++++---- crates/erg_compiler/ty/mod.rs | 8 +++++- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/crates/erg_compiler/context/compare.rs b/crates/erg_compiler/context/compare.rs index dc8d10531..cc9526d20 100644 --- a/crates/erg_compiler/context/compare.rs +++ b/crates/erg_compiler/context/compare.rs @@ -1410,12 +1410,36 @@ impl Context { (l, r @ (TyParam::Erased(_) | TyParam::FreeVar(_))) => self.try_cmp(r, l).map(|ord| ord.reverse()), (TyParam::App { name, args }, r) => { - self.eval_app(name.clone(), args.clone()).ok() - .and_then(|tp| self.try_cmp(&tp, r)) + let evaled = self.eval_app(name.clone(), args.clone()).ok()?; + if evaled != TyParam::app(name.clone(), args.clone()) { + self.try_cmp(&evaled, r) + } else { + None + } } (l, TyParam::App { name, args }) => { - self.eval_app(name.clone(), args.clone()).ok() - .and_then(|tp| self.try_cmp(l, &tp)) + let evaled = self.eval_app(name.clone(), args.clone()).ok()?; + if evaled != TyParam::app(name.clone(), args.clone()) { + self.try_cmp(l, &evaled) + } else { + None + } + } + (TyParam::Proj { obj, attr }, r) => { + let evaled = self.eval_tp_proj(*obj.clone(), attr.clone(), &()).ok()?; + if evaled != obj.clone().proj(attr.clone()) { + self.try_cmp(&evaled, r) + } else { + None + } + } + (l, TyParam::Proj { obj, attr }) => { + let evaled = self.eval_tp_proj(*obj.clone(), attr.clone(), &()).ok()?; + if evaled != obj.clone().proj(attr.clone()) { + self.try_cmp(l, &evaled) + } else { + None + } } (_l, _r) => { erg_common::fmt_dbg!(_l, _r,); diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index 60d160fcd..fb9a9bc37 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -1823,6 +1823,9 @@ impl Context { } /// args: may be unevaluated + /// + /// NOTE: This function may not evaluate app and return `TyParam::app(name, args)`. + /// Be careful with recursive calls. pub(crate) fn eval_app(&self, name: Str, args: Vec) -> Failable { /*let args = self .eval_type_args(args) diff --git a/crates/erg_compiler/context/generalize.rs b/crates/erg_compiler/context/generalize.rs index 49b343df4..d80388c8c 100644 --- a/crates/erg_compiler/context/generalize.rs +++ b/crates/erg_compiler/context/generalize.rs @@ -835,7 +835,12 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> { match t { FreeVar(fv) if fv.is_linked() => { let t = fv.unwrap_linked(); - self.deref_tyvar(t) + // (((((...))))) == Never + if t.is_recursive() { + Ok(Type::Never) + } else { + self.deref_tyvar(t) + } } FreeVar(mut fv) if fv.is_generalized() && self.qnames.contains(&fv.unbound_name().unwrap()) => diff --git a/crates/erg_compiler/ty/free.rs b/crates/erg_compiler/ty/free.rs index 8e158eec9..bb479851a 100644 --- a/crates/erg_compiler/ty/free.rs +++ b/crates/erg_compiler/ty/free.rs @@ -8,7 +8,7 @@ use erg_common::consts::DEBUG_MODE; use erg_common::shared::Forkable; use erg_common::traits::{LimitedDisplay, StructuralEq}; use erg_common::Str; -use erg_common::{addr_eq, log}; +use erg_common::{addr, addr_eq, log}; use super::typaram::TyParam; use super::Type; @@ -573,7 +573,12 @@ impl Hash for Free { } else if let Some(t) = self.get_type() { t.hash(state); } else if self.is_linked() { - self.crack().hash(state); + let cracked = self.crack(); + if !Type::FreeVar(self.clone()).addr_eq(&cracked) { + cracked.hash(state); + } else { + addr!(self).hash(state); + } } } } @@ -785,7 +790,7 @@ impl Free { let placeholder_ = placeholder .clone() .eliminate_subsup(&target) - .eliminate_and_or_recursion(&target); + .eliminate_recursion(&target); self.undoable_link(&placeholder_); } let res = f(); @@ -983,7 +988,13 @@ impl HasLevel for Free { fn level(&self) -> Option { match &*self.borrow() { FreeKind::Unbound { lev, .. } | FreeKind::NamedUnbound { lev, .. } => Some(*lev), - FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => t.level(), + FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => { + if t.is_recursive() { + None + } else { + t.level() + } + } } } } @@ -1009,7 +1020,13 @@ impl HasLevel for Free { fn level(&self) -> Option { match &*self.borrow() { FreeKind::Unbound { lev, .. } | FreeKind::NamedUnbound { lev, .. } => Some(*lev), - FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => t.level(), + FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => { + if t.is_recursive() { + None + } else { + t.level() + } + } } } } diff --git a/crates/erg_compiler/ty/mod.rs b/crates/erg_compiler/ty/mod.rs index 1fe79288c..886de88c2 100644 --- a/crates/erg_compiler/ty/mod.rs +++ b/crates/erg_compiler/ty/mod.rs @@ -3591,7 +3591,13 @@ impl Type { pub fn undoable_coerce(&self, list: &UndoableLinkedList) { match self { Type::FreeVar(fv) if fv.is_linked() => { - fv.crack().undoable_coerce(list); + let cracked = fv.crack(); + if cracked.is_recursive() { + drop(cracked); + fv.undoable_link(&Type::Never); + } else { + cracked.undoable_coerce(list); + } } Type::FreeVar(fv) if fv.is_unbound_and_sandwiched() => { set_recursion_limit!({}, 128);