From 43fbb7dbfdafbbb21616e13099e97ced295c3310 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sat, 7 Dec 2024 21:40:58 +0900 Subject: [PATCH] fix: infinite recursion bug --- crates/erg_compiler/context/inquire.rs | 67 +++++++++++++++----------- crates/erg_compiler/context/unify.rs | 13 +++++ 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/crates/erg_compiler/context/inquire.rs b/crates/erg_compiler/context/inquire.rs index 42693127d..6558b40c2 100644 --- a/crates/erg_compiler/context/inquire.rs +++ b/crates/erg_compiler/context/inquire.rs @@ -2396,11 +2396,38 @@ impl Context { namespace, )?; } - let instance = self.instantiate_def_type(&call_vi.t)?; + let call_instance = self.instantiate_def_type(&call_vi.t)?; + if !call_instance.contains_intersec(instance) { + let instance = match self.substitute_call( + obj, + attr_name, + &call_instance, + pos_args, + kw_args, + (var_args, kw_var_args), + namespace, + )? { + SubstituteResult::__Call__(instance) + | SubstituteResult::Coerced(instance) => instance, + SubstituteResult::Ok => call_instance, + }; + debug_assert!(!instance.is_intersection_type()); + return Ok(SubstituteResult::__Call__(instance)); + } + } + // instance method __call__ + } else if let Some(call_vi) = typ_ctx + .get_method_context_of(&mono("GenericCallable")) + .and_then(|method_ctx| { + method_ctx.get_current_scope_var(&VarName::from_static("__call__")) + }) + { + let call_instance = self.instantiate_def_type(&call_vi.t)?; + if !call_instance.contains_intersec(instance) { let instance = match self.substitute_call( obj, attr_name, - &instance, + &call_instance, pos_args, kw_args, (var_args, kw_var_args), @@ -2408,34 +2435,10 @@ impl Context { )? { SubstituteResult::__Call__(instance) | SubstituteResult::Coerced(instance) => instance, - SubstituteResult::Ok => instance, + SubstituteResult::Ok => call_instance, }; - debug_assert!(!instance.is_intersection_type()); return Ok(SubstituteResult::__Call__(instance)); } - // instance method __call__ - } else if let Some(call_vi) = typ_ctx - .get_method_context_of(&mono("GenericCallable")) - .and_then(|method_ctx| { - method_ctx.get_current_scope_var(&VarName::from_static("__call__")) - }) - { - let instance = self.instantiate_def_type(&call_vi.t)?; - let instance = match self.substitute_call( - obj, - attr_name, - &instance, - pos_args, - kw_args, - (var_args, kw_var_args), - namespace, - )? { - SubstituteResult::__Call__(instance) | SubstituteResult::Coerced(instance) => { - instance - } - SubstituteResult::Ok => instance, - }; - return Ok(SubstituteResult::__Call__(instance)); } } let hint = if self.subtype_of(instance, &ClassType) { @@ -4161,17 +4164,21 @@ impl Context { pub fn is_class(&self, typ: &Type) -> bool { match typ { - Type::And(_, _) => false, Type::Never => true, Type::FreeVar(fv) if fv.is_linked() => self.is_class(&fv.unwrap_linked()), Type::FreeVar(_) => false, + Type::And(_, _) => false, Type::Or(tys) => tys.iter().all(|t| self.is_class(t)), + Type::Not(ty) => self.is_class(ty), Type::Proj { lhs, rhs } => self .get_proj_candidates(lhs, rhs) .iter() .all(|t| self.is_class(t)), Type::Refinement(refine) => self.is_class(&refine.t), Type::Ref(t) | Type::RefMut { before: t, .. } => self.is_class(t), + Type::Structural(_) => false, + Type::Record(_) => true, + Type::Subr(_) => true, _ => { if let Some(ctx) = self.get_nominal_type_ctx(typ) { ctx.kind.is_class() @@ -4190,12 +4197,16 @@ impl Context { Type::FreeVar(_) => false, Type::And(tys, _) => tys.iter().any(|t| self.is_trait(t)), Type::Or(tys) => tys.iter().all(|t| self.is_trait(t)), + Type::Not(ty) => self.is_trait(ty), Type::Proj { lhs, rhs } => self .get_proj_candidates(lhs, rhs) .iter() .all(|t| self.is_trait(t)), Type::Refinement(refine) => self.is_trait(&refine.t), Type::Ref(t) | Type::RefMut { before: t, .. } => self.is_trait(t), + Type::Structural(_) => false, + Type::Record(_) => false, + Type::Subr(_) => false, _ => { if let Some(ctx) = self.get_nominal_type_ctx(typ) { ctx.kind.is_trait() diff --git a/crates/erg_compiler/context/unify.rs b/crates/erg_compiler/context/unify.rs index e07dd528b..de764eddd 100644 --- a/crates/erg_compiler/context/unify.rs +++ b/crates/erg_compiler/context/unify.rs @@ -1640,6 +1640,8 @@ impl Unifier<'_, '_, '_, L> { { // contravariant self.sub_unify(super_pt.typ(), sub_pt.typ())?; + } else if let Some(sub_pt) = sub_subr.kw_var_params.as_ref() { + self.sub_unify(super_pt.typ(), sub_pt.typ())?; } else { let param_name = super_pt.name().map_or("_", |s| &s[..]); let similar_param = erg_common::levenshtein::get_similar_name( @@ -1692,6 +1694,8 @@ impl Unifier<'_, '_, '_, L> { } // contravariant self.sub_unify(super_pt.typ(), sub_pt.typ())?; + } else if let Some(sub_pt) = sub_subr.kw_var_params.as_ref() { + self.sub_unify(super_pt.typ(), sub_pt.typ())?; } else { todo!("{maybe_sub} <: {maybe_super}") } @@ -1728,6 +1732,8 @@ impl Unifier<'_, '_, '_, L> { continue; } self.sub_unify(super_pt.typ(), sub_pt.typ())?; + } else if let Some(sub_pt) = sub_subr.kw_var_params.as_ref() { + self.sub_unify(super_pt.typ(), sub_pt.typ())?; } else { todo!("{maybe_sub} <: {maybe_super}") } @@ -1943,6 +1949,13 @@ impl Unifier<'_, '_, '_, L> { /// TODO: Current implementation is inefficient because coercion is performed twice with `subtype_of` in `sub_unify` fn nominal_sub_unify(&self, maybe_sub: &Type, maybe_super: &Type) -> TyCheckResult<()> { debug_assert_ne!(maybe_sub.qual_name(), maybe_super.qual_name()); + if (maybe_sub.is_dict() || maybe_sub.is_dict_mut()) + && (maybe_super.is_dict() || maybe_super.is_dict_mut()) + { + let sub_dict = maybe_sub.typarams().into_iter().next().unwrap(); + let super_dict = maybe_super.typarams().into_iter().next().unwrap(); + return self.sub_unify_tp(&sub_dict, &super_dict, None, false); + } if let Some(sub_ctx) = self.ctx.get_nominal_type_ctx(maybe_sub) { let sub_def_t = &sub_ctx.typ; // e.g.