Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid infinite recursion #532

Merged
merged 5 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,7 @@ path = "src/lib.rs"

# [profile.release]
# panic = 'abort'

[profile.opt-with-dbg]
inherits = "release"
debug = true
5 changes: 4 additions & 1 deletion crates/erg_common/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,10 @@ impl SubMessage {
gutter_color,
);
cxt.push_str(&" ".repeat(lineno.to_string().len()));
cxt.push_str_with_color(mark.repeat(cmp::max(1, codes[i].len())), err_color);
cxt.push_str_with_color(
mark.repeat(cmp::max(1, codes.get(i).map_or(1, |code| code.len()))),
err_color,
);
cxt.push_str("\n");
}
cxt.push_str("\n");
Expand Down
1 change: 1 addition & 0 deletions crates/erg_common/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ pub trait LimitedDisplay {
self.limited_fmt(&mut s, -1).unwrap();
s
}
const DEFAULT_LIMIT: isize = 10;
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
Expand Down
83 changes: 50 additions & 33 deletions crates/erg_compiler/context/compare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,11 @@ impl Context {
/// 単一化、評価等はここでは行わない、スーパータイプになる **可能性があるか** だけ判定する
/// ので、lhsが(未連携)型変数の場合は単一化せずにtrueを返す
pub(crate) fn structural_supertype_of(&self, lhs: &Type, rhs: &Type) -> bool {
set_recursion_limit!(false, 128);
set_recursion_limit!(
panic,
"recursion limit exceed: structural_supertype_of({lhs}, {rhs})",
128
);
match (lhs, rhs) {
// Proc :> Func if params are compatible
// * default params can be omitted (e.g. (Int, x := Int) -> Int <: (Int) -> Int)
Expand Down Expand Up @@ -409,45 +413,48 @@ impl Context {
.return_t
.clone()
.replace_params(rs.param_names(), ls.param_names());
let return_t_judge = self.supertype_of(&ls.return_t, &rhs_ret); // covariant
let non_defaults_judge = if let Some(r_var) = rs.var_params.as_deref() {
ls.non_default_params
.iter()
.zip(repeat(r_var))
.all(|(l, r)| self.subtype_of(l.typ(), r.typ()))
} else {
let rs_params = if !ls.is_method() && rs.is_method() {
rs.non_default_params
let return_t_judge = || self.supertype_of(&ls.return_t, &rhs_ret); // covariant
let non_defaults_judge = || {
if let Some(r_var) = rs.var_params.as_deref() {
ls.non_default_params
.iter()
.skip(1)
.chain(&rs.default_params)
.zip(repeat(r_var))
.all(|(l, r)| self.subtype_of(l.typ(), r.typ()))
} else {
#[allow(clippy::iter_skip_zero)]
rs.non_default_params
let rs_params = if !ls.is_method() && rs.is_method() {
rs.non_default_params
.iter()
.skip(1)
.chain(&rs.default_params)
} else {
#[allow(clippy::iter_skip_zero)]
rs.non_default_params
.iter()
.skip(0)
.chain(&rs.default_params)
};
ls.non_default_params
.iter()
.skip(0)
.chain(&rs.default_params)
};
ls.non_default_params
.iter()
.zip(rs_params)
.all(|(l, r)| self.subtype_of(l.typ(), r.typ()))
.zip(rs_params)
.all(|(l, r)| self.subtype_of(l.typ(), r.typ()))
}
};
let var_params_judge = || {
ls.var_params
.as_ref()
.zip(rs.var_params.as_ref())
.map(|(l, r)| self.subtype_of(l.typ(), r.typ()))
.unwrap_or(true)
};
let var_params_judge = ls
.var_params
.as_ref()
.zip(rs.var_params.as_ref())
.map(|(l, r)| self.subtype_of(l.typ(), r.typ()))
.unwrap_or(true);
len_judge
&& return_t_judge
&& non_defaults_judge
&& var_params_judge
&& return_t_judge()
&& non_defaults_judge()
&& var_params_judge()
&& default_check() // contravariant
}
// {Int} <: Obj -> Int
(Subr(_) | Quantified(_), Refinement(refine))
if rhs.singleton_value().is_some() && self.subtype_of(&refine.t, &ClassType) =>
(Subr(_) | Quantified(_), Refinement(_refine))
if rhs.singleton_value().is_some() && rhs.is_singleton_refinement_type() =>
{
let Ok(typ) = self.convert_tp_into_type(rhs.singleton_value().unwrap().clone())
else {
Expand All @@ -457,7 +464,8 @@ impl Context {
return false;
};
if let Some((_, __call__)) = ctx.get_class_attr("__call__") {
self.supertype_of(lhs, &__call__.t)
let call_t = __call__.t.clone().undoable_root();
self.supertype_of(lhs, &call_t)
} else {
false
}
Expand Down Expand Up @@ -767,10 +775,14 @@ impl Context {
// Bool :> {2} == false
// [2, 3]: {A: List(Nat) | A.prod() == 6}
// List({1, 2}, _) :> {[3, 4]} == false
// T :> {None} == T :> NoneType
(l, Refinement(r)) => {
// Type / {S: Set(Str) | S == {"a", "b"}}
// TODO: GeneralEq
if let Pred::Equal { rhs, .. } = r.pred.as_ref() {
if rhs.is_none() {
return self.supertype_of(lhs, &NoneType);
}
if self.subtype_of(l, &Type) && self.convert_tp_into_type(rhs.clone()).is_ok() {
return true;
}
Expand Down Expand Up @@ -937,6 +949,11 @@ impl Context {
}
}

/// ```erg
/// Int.fields() == { imag: Int, real: Int, abs: (self: Int) -> Nat, ... }
/// ?T(<: Int).fields() == Int.fields()
/// Structural({ .x = Int }).fields() == { x: Int }
/// ```
pub fn fields(&self, t: &Type) -> Dict<Field, Type> {
match t {
Type::FreeVar(fv) if fv.is_linked() => self.fields(&fv.unwrap_linked()),
Expand Down
43 changes: 40 additions & 3 deletions crates/erg_compiler/context/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2939,6 +2939,34 @@ impl Context {
}
Type::Quantified(quant) => self.convert_singular_type_into_value(*quant),
Type::Subr(subr) => self.convert_singular_type_into_value(*subr.return_t),
Type::Proj { lhs, rhs } => {
let old = lhs.clone().proj(rhs.clone());
let evaled = self.eval_proj(*lhs, rhs, 0, &()).map_err(|_| old.clone())?;
if old != evaled {
self.convert_singular_type_into_value(evaled)
} else {
Err(old)
}
}
Type::ProjCall {
lhs,
attr_name,
args,
} => {
let old = Type::ProjCall {
lhs: lhs.clone(),
attr_name: attr_name.clone(),
args: args.clone(),
};
let evaled = self
.eval_proj_call_t(*lhs, attr_name, args, 0, &())
.map_err(|_| old.clone())?;
if old != evaled {
self.convert_singular_type_into_value(evaled)
} else {
Err(old)
}
}
Type::Failure => Ok(ValueObj::Failure),
_ => Err(typ),
}
Expand Down Expand Up @@ -2993,7 +3021,15 @@ impl Context {
let end = fields["end"].clone();
Ok(closed_range(start.class(), start, end))
}
other => Err(other),
// TODO:
ValueObj::DataClass { .. }
| ValueObj::Int(_)
| ValueObj::Nat(_)
| ValueObj::Bool(_)
| ValueObj::Float(_)
| ValueObj::Code(_)
| ValueObj::Str(_)
| ValueObj::None => Err(val),
}
}

Expand Down Expand Up @@ -3083,7 +3119,8 @@ impl Context {
})
}
ValueObj::Failure => Ok(TyParam::Failure),
_ => Err(TyParam::Value(value)),
ValueObj::Subr(_) => Err(TyParam::Value(value)),
mono_value_pattern!(-Failure) => Err(TyParam::Value(value)),
}
}

Expand All @@ -3094,7 +3131,7 @@ impl Context {
self.convert_type_to_dict_type(t)
}
Type::Refinement(refine) => self.convert_type_to_dict_type(*refine.t),
Type::Poly { name, params } if &name[..] == "Dict" || &name[..] == "Dict!" => {
Type::Poly { params, .. } if ty.is_dict() || ty.is_dict_mut() => {
let dict = Dict::try_from(params[0].clone())?;
let mut new_dict = dict! {};
for (k, v) in dict.into_iter() {
Expand Down
29 changes: 18 additions & 11 deletions crates/erg_compiler/context/generalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::ty::constructors::*;
use crate::ty::free::{CanbeFree, Constraint, Free, HasLevel};
use crate::ty::typaram::{TyParam, TyParamLambda};
use crate::ty::value::ValueObj;
use crate::ty::{HasType, Predicate, SubrType, Type};
use crate::ty::{HasType, Predicate, SharedFrees, SubrType, Type};

use crate::context::{Context, Variance};
use crate::error::{TyCheckError, TyCheckErrors, TyCheckResult};
Expand Down Expand Up @@ -45,10 +45,12 @@ impl<'c> Generalizer<'c> {
fn generalize_tp(&mut self, free: TyParam, uninit: bool) -> TyParam {
match free {
TyParam::Type(t) => TyParam::t(self.generalize_t(*t, uninit)),
TyParam::Value(val) => TyParam::Value(
val.map_t(&mut |t| self.generalize_t(t, uninit))
.map_tp(&mut |tp| self.generalize_tp(tp, uninit)),
),
TyParam::Value(val) => {
TyParam::Value(val.map_t(&mut |t| self.generalize_t(t, uninit)).map_tp(
&mut |tp| self.generalize_tp(tp, uninit),
&SharedFrees::new(),
))
}
TyParam::FreeVar(fv) if fv.is_generalized() => TyParam::FreeVar(fv),
TyParam::FreeVar(fv) if fv.is_linked() => {
let tp = fv.crack().clone();
Expand Down Expand Up @@ -200,13 +202,17 @@ impl<'c> Generalizer<'c> {
// |T :> Int| X -> T ==> X -> Int
self.generalize_t(sub, uninit)
} else {
fv.update_constraint(self.generalize_constraint(&fv), true);
Type::FreeVar(fv)
let constr = self.generalize_constraint(&fv);
let ty = Type::FreeVar(fv);
ty.update_constraint(constr, None, true);
ty
}
} else {
// ?S(: Str) => 'S
fv.update_constraint(self.generalize_constraint(&fv), true);
Type::FreeVar(fv)
let constr = self.generalize_constraint(&fv);
let ty = Type::FreeVar(fv);
ty.update_constraint(constr, None, true);
ty
}
}
FreeVar(_) => free_type,
Expand Down Expand Up @@ -951,8 +957,9 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
} else {
let new_constraint = fv.crack_constraint().clone();
let new_constraint = self.deref_constraint(new_constraint)?;
fv.update_constraint(new_constraint, true);
Ok(Type::FreeVar(fv))
let ty = Type::FreeVar(fv);
ty.update_constraint(new_constraint, None, true);
Ok(ty)
}
}
FreeVar(_) => Ok(t),
Expand Down
Loading
Loading