Skip to content

Commit

Permalink
fix: infinite recursion bug
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Dec 7, 2024
1 parent 12ff416 commit 43fbb7d
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 28 deletions.
67 changes: 39 additions & 28 deletions crates/erg_compiler/context/inquire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2396,46 +2396,49 @@ 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),
namespace,
)? {
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) {
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand Down
13 changes: 13 additions & 0 deletions crates/erg_compiler/context/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,8 @@ impl<L: Locational> 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(
Expand Down Expand Up @@ -1692,6 +1694,8 @@ impl<L: Locational> 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}")
}
Expand Down Expand Up @@ -1728,6 +1732,8 @@ impl<L: Locational> 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}")
}
Expand Down Expand Up @@ -1943,6 +1949,13 @@ impl<L: Locational> 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.
Expand Down

0 comments on commit 43fbb7d

Please sign in to comment.