Skip to content

Commit

Permalink
Merge pull request #532 from erg-lang/perf3
Browse files Browse the repository at this point in the history
Avoid infinite recursion
  • Loading branch information
mtshiba authored Nov 3, 2024
2 parents 75535a7 + 87fb4cf commit 170f8e3
Show file tree
Hide file tree
Showing 15 changed files with 840 additions and 468 deletions.
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

0 comments on commit 170f8e3

Please sign in to comment.