mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 02:39:20 +00:00
fix: avoid infinite recursion bugs
This commit is contained in:
parent
2a0998f0cd
commit
b318395a32
10 changed files with 128 additions and 48 deletions
|
@ -797,8 +797,16 @@ impl Context {
|
|||
(TyParam::Type(l), TyParam::Type(r), Variance::Covariant) => self.supertype_of(l, r),
|
||||
(TyParam::Type(l), TyParam::Type(r), Variance::Invariant) => self.same_type_of(l, r),
|
||||
(TyParam::FreeVar(fv), _, _) if fv.is_unbound() => {
|
||||
let fv_t = fv.get_type().unwrap();
|
||||
let rp_t = self.get_tp_t(rp).unwrap();
|
||||
let Some(fv_t) = fv.get_type() else {
|
||||
return false;
|
||||
};
|
||||
let rp_t = match self.get_tp_t(rp) {
|
||||
Ok(t) => t,
|
||||
Err(err) => {
|
||||
log!("supertype_of_tp: {err}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if variance == Variance::Contravariant {
|
||||
self.subtype_of(&fv_t, &rp_t)
|
||||
} else if variance == Variance::Covariant {
|
||||
|
|
|
@ -208,7 +208,7 @@ impl Context {
|
|||
ident.inspect(),
|
||||
self.get_similar_name(ident.inspect()),
|
||||
);
|
||||
self.get_mut_type(ident.inspect())
|
||||
self.rec_get_mut_type(ident.inspect())
|
||||
.map(|(_, ctx)| ctx)
|
||||
.ok_or(err)
|
||||
}
|
||||
|
@ -2445,6 +2445,20 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn rec_get_mut_type(&mut self, name: &str) -> Option<(&Type, &mut Context)> {
|
||||
#[cfg(feature = "py_compatible")]
|
||||
let name = self.erg_to_py_names.get(name).map_or(name, |s| &s[..]);
|
||||
if let Some((t, ctx)) = self.mono_types.get_mut(name) {
|
||||
Some((t, ctx))
|
||||
} else if let Some((t, ctx)) = self.poly_types.get_mut(name) {
|
||||
Some((t, ctx))
|
||||
} else if let Some(outer) = self.outer.as_mut() {
|
||||
outer.rec_get_mut_type(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_type(&self, name: &Str) -> Option<(&Type, &Context)> {
|
||||
if let Some((t, ctx)) = self.rec_local_get_type(name) {
|
||||
return Some((t, ctx));
|
||||
|
@ -2493,18 +2507,6 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut_type(&mut self, name: &str) -> Option<(&Type, &mut Context)> {
|
||||
#[cfg(feature = "py_compatible")]
|
||||
let name = self.erg_to_py_names.get(name).map_or(name, |s| &s[..]);
|
||||
if let Some((t, ctx)) = self.mono_types.get_mut(name) {
|
||||
Some((t, ctx))
|
||||
} else if let Some((t, ctx)) = self.poly_types.get_mut(name) {
|
||||
Some((t, ctx))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn rec_get_patch(&self, name: &str) -> Option<&Context> {
|
||||
if let Some(ctx) = self.patches.get(name) {
|
||||
Some(ctx)
|
||||
|
|
|
@ -35,6 +35,7 @@ pub struct TyVarCache {
|
|||
pub(crate) already_appeared: Set<Str>,
|
||||
pub(crate) tyvar_instances: Dict<Str, Type>,
|
||||
pub(crate) typaram_instances: Dict<Str, TyParam>,
|
||||
pub(crate) structural_inner: bool,
|
||||
}
|
||||
|
||||
impl fmt::Display for TyVarCache {
|
||||
|
@ -54,6 +55,7 @@ impl TyVarCache {
|
|||
already_appeared: Set::new(),
|
||||
tyvar_instances: Dict::new(),
|
||||
typaram_instances: Dict::new(),
|
||||
structural_inner: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,15 +482,28 @@ impl Context {
|
|||
unreachable_error!(TyCheckErrors, TyCheckError, self)
|
||||
}
|
||||
Structural(t) => {
|
||||
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
|
||||
Ok(t.structuralize())
|
||||
// avoid infinite recursion
|
||||
if tmp_tv_cache.structural_inner {
|
||||
Ok(t.structuralize())
|
||||
} else {
|
||||
if t.is_recursive() {
|
||||
tmp_tv_cache.structural_inner = true;
|
||||
}
|
||||
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
|
||||
Ok(t.structuralize())
|
||||
}
|
||||
}
|
||||
FreeVar(fv) => {
|
||||
let (sub, sup) = fv.get_subsup().unwrap();
|
||||
let sub = self.instantiate_t_inner(sub, tmp_tv_cache, loc)?;
|
||||
let sup = self.instantiate_t_inner(sup, tmp_tv_cache, loc)?;
|
||||
let new_constraint = Constraint::new_sandwiched(sub, sup);
|
||||
fv.update_constraint(new_constraint, true);
|
||||
if let Some((sub, sup)) = fv.get_subsup() {
|
||||
let sub = self.instantiate_t_inner(sub, tmp_tv_cache, loc)?;
|
||||
let sup = self.instantiate_t_inner(sup, tmp_tv_cache, loc)?;
|
||||
let new_constraint = Constraint::new_sandwiched(sub, sup);
|
||||
fv.update_constraint(new_constraint, true);
|
||||
} else if let Some(ty) = fv.get_type() {
|
||||
let ty = self.instantiate_t_inner(ty, tmp_tv_cache, loc)?;
|
||||
let new_constraint = Constraint::new_type_of(ty);
|
||||
fv.update_constraint(new_constraint, true);
|
||||
}
|
||||
Ok(FreeVar(fv))
|
||||
}
|
||||
And(l, r) => {
|
||||
|
|
|
@ -1245,9 +1245,15 @@ impl Context {
|
|||
self.level,
|
||||
);
|
||||
for sup in super_classes.into_iter() {
|
||||
let (_, sup_ctx) = self
|
||||
.get_nominal_type_ctx(&sup)
|
||||
.unwrap_or_else(|| todo!("{sup} not found"));
|
||||
let (_, sup_ctx) = self.get_nominal_type_ctx(&sup).ok_or_else(|| {
|
||||
TyCheckErrors::from(TyCheckError::type_not_found(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
ident.loc(),
|
||||
self.caused_by(),
|
||||
&sup,
|
||||
))
|
||||
})?;
|
||||
ctx.register_superclass(sup, sup_ctx);
|
||||
}
|
||||
let mut methods =
|
||||
|
|
|
@ -620,7 +620,7 @@ impl ASTLowerer {
|
|||
} else {
|
||||
ident.inspect().clone()
|
||||
};
|
||||
if let Some((_, ctx)) = self.module.context.get_mut_type(&name) {
|
||||
if let Some((_, ctx)) = self.module.context.rec_get_mut_type(&name) {
|
||||
ctx.register_marker_trait(trait_.clone());
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
|
@ -1981,11 +1981,13 @@ impl ASTLowerer {
|
|||
|
||||
fn check_collision_and_push(&mut self, class: Type) {
|
||||
let methods = self.module.context.pop();
|
||||
let (_, class_root) = self
|
||||
let Some((_, class_root)) = self
|
||||
.module
|
||||
.context
|
||||
.get_mut_nominal_type_ctx(&class)
|
||||
.unwrap_or_else(|| todo!("{class} not found"));
|
||||
.get_mut_nominal_type_ctx(&class) else {
|
||||
log!(err "{class} not found");
|
||||
return;
|
||||
};
|
||||
for (newly_defined_name, vi) in methods.locals.clone().into_iter() {
|
||||
for (_, already_defined_methods) in class_root.methods_list.iter_mut() {
|
||||
// TODO: 特殊化なら同じ名前でもOK
|
||||
|
|
|
@ -22,6 +22,7 @@ impl ModuleGraph {
|
|||
}
|
||||
|
||||
pub fn get_node(&self, path: &Path) -> Option<&Node<PathBuf, ()>> {
|
||||
let path = normalize_path(path.to_path_buf());
|
||||
self.0.iter().find(|n| n.id == path)
|
||||
}
|
||||
|
||||
|
@ -34,6 +35,7 @@ impl ModuleGraph {
|
|||
}
|
||||
|
||||
pub fn inc_ref(&mut self, referrer: &Path, depends_on: PathBuf) {
|
||||
let referrer = normalize_path(referrer.to_path_buf());
|
||||
let depends_on = normalize_path(depends_on);
|
||||
if let Some(node) = self.0.iter_mut().find(|n| n.id == referrer) {
|
||||
if referrer == depends_on {
|
||||
|
|
|
@ -1450,10 +1450,7 @@ impl Type {
|
|||
Self::Callable { .. } => true,
|
||||
Self::Quantified(t) => t.is_procedure(),
|
||||
Self::Subr(subr) if subr.kind == SubrKind::Proc => true,
|
||||
Self::Refinement(refine) =>
|
||||
refine.t.is_procedure() || refine.pred.ands().iter().any(|pred|
|
||||
matches!(pred, Predicate::Equal{ rhs, .. } if pred.mentions(&refine.var) && rhs.qual_name().map(|n| n.ends_with('!')).unwrap_or(false))
|
||||
),
|
||||
Self::Refinement(refine) => refine.t.is_procedure(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -1628,10 +1625,11 @@ impl Type {
|
|||
}
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().contains(target),
|
||||
Self::FreeVar(fv) => fv
|
||||
.get_subsup()
|
||||
.map(|(sub, sup)| sub.contains(target) || sup.contains(target))
|
||||
.unwrap_or(false),
|
||||
Self::FreeVar(fv) => {
|
||||
fv.get_subsup().map_or(false, |(sub, sup)| {
|
||||
sub.contains(target) || sup.contains(target)
|
||||
}) || fv.get_type().map_or(false, |t| t.contains(target))
|
||||
}
|
||||
Self::Record(rec) => rec.iter().any(|(_, t)| t.contains(target)),
|
||||
Self::Poly { params, .. } => params.iter().any(|tp| tp.contains(target)),
|
||||
Self::Quantified(t) => t.contains(target),
|
||||
|
@ -1654,6 +1652,33 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_recursive(&self) -> bool {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_recursive(),
|
||||
Self::FreeVar(fv) => fv
|
||||
.get_subsup()
|
||||
.map(|(sub, sup)| sub.contains(self) || sup.contains(self))
|
||||
.unwrap_or(false),
|
||||
Self::Record(rec) => rec.iter().any(|(_, t)| t.contains(self)),
|
||||
Self::Poly { params, .. } => params.iter().any(|tp| tp.contains(self)),
|
||||
Self::Quantified(t) => t.contains(self),
|
||||
Self::Subr(subr) => subr.contains(self),
|
||||
Self::Refinement(refine) => refine.t.contains(self),
|
||||
Self::Structural(ty) => ty.contains(self),
|
||||
Self::Proj { lhs, .. } => lhs.contains(self),
|
||||
Self::ProjCall { lhs, args, .. } => {
|
||||
lhs.contains(self) || args.iter().any(|t| t.contains(self))
|
||||
}
|
||||
Self::And(lhs, rhs) | Self::Or(lhs, rhs) => lhs.contains(self) || rhs.contains(self),
|
||||
Self::Not(t) => t.contains(self),
|
||||
Self::Ref(t) => t.contains(self),
|
||||
Self::RefMut { before, after } => {
|
||||
before.contains(self) || after.as_ref().map_or(false, |t| t.contains(self))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn args_ownership(&self) -> ArgsOwnership {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().args_ownership(),
|
||||
|
@ -1984,7 +2009,7 @@ impl Type {
|
|||
res_sub || res_sup
|
||||
} else {
|
||||
let opt_t = fv.get_type();
|
||||
opt_t.map(|t| t.has_qvar()).unwrap_or(false)
|
||||
opt_t.map_or(false, |t| t.has_qvar())
|
||||
}
|
||||
}
|
||||
Self::Ref(ty) => ty.has_qvar(),
|
||||
|
|
|
@ -288,6 +288,8 @@ impl PartialEq for TyParam {
|
|||
_ => false,
|
||||
},
|
||||
(Self::Failure, Self::Failure) => true,
|
||||
(Self::Type(l), Self::Value(ValueObj::Type(r))) => l.as_ref() == r.typ(),
|
||||
(Self::Value(ValueObj::Type(l)), Self::Type(r)) => l.typ() == r.as_ref(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -383,6 +385,7 @@ impl CanbeFree for TyParam {
|
|||
match self {
|
||||
TyParam::FreeVar(fv) => fv.unbound_name(),
|
||||
TyParam::Type(t) => t.unbound_name(),
|
||||
TyParam::Value(ValueObj::Type(ty)) => ty.typ().unbound_name(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -391,6 +394,7 @@ impl CanbeFree for TyParam {
|
|||
match self {
|
||||
TyParam::FreeVar(fv) => fv.constraint(),
|
||||
TyParam::Type(t) => t.constraint(),
|
||||
TyParam::Value(ValueObj::Type(ty)) => ty.typ().constraint(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -403,6 +407,9 @@ impl CanbeFree for TyParam {
|
|||
Self::Type(t) => {
|
||||
t.update_constraint(new_constraint, in_instantiation);
|
||||
}
|
||||
Self::Value(ValueObj::Type(ty)) => {
|
||||
ty.typ().update_constraint(new_constraint, in_instantiation);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -591,7 +598,7 @@ impl TryFrom<TyParam> for Type {
|
|||
fn try_from(tp: TyParam) -> Result<Type, ()> {
|
||||
match tp {
|
||||
TyParam::FreeVar(fv) if fv.is_linked() => {
|
||||
Type::try_from(fv.forced_as_ref().linked().unwrap().clone()).map_err(|_| ())
|
||||
Type::try_from(fv.crack().clone()).map_err(|_| ())
|
||||
}
|
||||
TyParam::Type(t) => Ok(*t),
|
||||
TyParam::Value(v) => Type::try_from(v),
|
||||
|
@ -625,6 +632,7 @@ impl HasLevel for TyParam {
|
|||
Self::App { args, .. } => args.iter().filter_map(|tp| tp.level()).min(),
|
||||
Self::UnaryOp { val, .. } => val.level(),
|
||||
Self::BinOp { lhs, rhs, .. } => lhs.level().and_then(|l| rhs.level().map(|r| l.min(r))),
|
||||
Self::Value(ValueObj::Type(ty)) => ty.typ().level(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -673,6 +681,7 @@ impl HasLevel for TyParam {
|
|||
Self::Proj { obj, .. } => {
|
||||
obj.set_level(level);
|
||||
}
|
||||
Self::Value(ValueObj::Type(ty)) => ty.typ().set_level(level),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -754,6 +763,8 @@ impl StructuralEq for TyParam {
|
|||
fv.crack().structural_eq(other)
|
||||
}
|
||||
(Self::FreeVar(l), Self::FreeVar(r)) => l.structural_eq(r),
|
||||
(Self::Type(l), Self::Value(ValueObj::Type(r))) => l.structural_eq(r.typ()),
|
||||
(Self::Value(ValueObj::Type(l)), Self::Type(r)) => l.typ().structural_eq(r),
|
||||
_ => self == other,
|
||||
}
|
||||
}
|
||||
|
@ -852,6 +863,7 @@ impl TyParam {
|
|||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().qual_name(),
|
||||
Self::FreeVar(fv) if fv.is_generalized() => fv.unbound_name(),
|
||||
Self::Mono(name) => Some(name.clone()),
|
||||
Self::Value(ValueObj::Type(t)) => Some(t.typ().qual_name()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -861,6 +873,7 @@ impl TyParam {
|
|||
Self::Type(t) => t.tvar_name(),
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().tvar_name(),
|
||||
Self::FreeVar(fv) => fv.unbound_name(),
|
||||
Self::Value(ValueObj::Type(t)) => t.typ().tvar_name(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -898,6 +911,7 @@ impl TyParam {
|
|||
fv.crack().coerce();
|
||||
}
|
||||
TyParam::Type(t) => t.coerce(),
|
||||
TyParam::Value(ValueObj::Type(t)) => t.typ().coerce(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -933,6 +947,7 @@ impl TyParam {
|
|||
Self::BinOp { lhs, rhs, .. } => lhs.qvars().concat(rhs.qvars()),
|
||||
Self::App { args, .. } => args.iter().fold(set! {}, |acc, p| acc.concat(p.qvars())),
|
||||
Self::Erased(t) => t.qvars(),
|
||||
Self::Value(ValueObj::Type(t)) => t.typ().qvars(),
|
||||
_ => set! {},
|
||||
}
|
||||
}
|
||||
|
@ -940,13 +955,7 @@ impl TyParam {
|
|||
pub fn has_qvar(&self) -> bool {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_generalized() => true,
|
||||
Self::FreeVar(fv) => {
|
||||
if fv.is_unbound() {
|
||||
false
|
||||
} else {
|
||||
fv.crack().has_qvar()
|
||||
}
|
||||
}
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().has_qvar(),
|
||||
Self::Type(t) => t.has_qvar(),
|
||||
Self::Proj { obj, .. } => obj.has_qvar(),
|
||||
Self::Array(tps) | Self::Tuple(tps) => tps.iter().any(|tp| tp.has_qvar()),
|
||||
|
@ -958,6 +967,7 @@ impl TyParam {
|
|||
Self::BinOp { lhs, rhs, .. } => lhs.has_qvar() || rhs.has_qvar(),
|
||||
Self::App { args, .. } => args.iter().any(|p| p.has_qvar()),
|
||||
Self::Erased(t) => t.has_qvar(),
|
||||
Self::Value(ValueObj::Type(t)) => t.typ().has_qvar(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -978,6 +988,7 @@ impl TyParam {
|
|||
Self::UnaryOp { val, .. } => val.contains_tvar(target),
|
||||
Self::BinOp { lhs, rhs, .. } => lhs.contains_tvar(target) || rhs.contains_tvar(target),
|
||||
Self::App { args, .. } => args.iter().any(|p| p.contains_tvar(target)),
|
||||
Self::Value(ValueObj::Type(t)) => t.typ().contains_tvar(target),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -998,12 +1009,18 @@ impl TyParam {
|
|||
Self::UnaryOp { val, .. } => val.contains(target),
|
||||
Self::BinOp { lhs, rhs, .. } => lhs.contains(target) || rhs.contains(target),
|
||||
Self::App { args, .. } => args.iter().any(|p| p.contains(target)),
|
||||
Self::Value(ValueObj::Type(t)) => t.typ().contains(target),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_unbound_var(&self) -> bool {
|
||||
matches!(self, Self::FreeVar(fv) if fv.is_unbound() || fv.crack().is_unbound_var())
|
||||
match self {
|
||||
Self::FreeVar(fv) => fv.is_unbound() || fv.crack().is_unbound_var(),
|
||||
Self::Type(t) => t.is_unbound_var(),
|
||||
Self::Value(ValueObj::Type(t)) => t.typ().is_unbound_var(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_unbound_var(&self) -> bool {
|
||||
|
@ -1028,6 +1045,7 @@ impl TyParam {
|
|||
Self::BinOp { lhs, rhs, .. } => lhs.has_unbound_var() || rhs.has_unbound_var(),
|
||||
Self::App { args, .. } => args.iter().any(|p| p.has_unbound_var()),
|
||||
Self::Erased(t) => t.has_unbound_var(),
|
||||
Self::Value(ValueObj::Type(t)) => t.typ().has_unbound_var(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1057,7 +1057,7 @@ impl ValueObj {
|
|||
pub fn try_sub(self, other: Self) -> Option<Self> {
|
||||
match (self, other) {
|
||||
(Self::Int(l), Self::Int(r)) => Some(Self::Int(l - r)),
|
||||
(Self::Nat(l), Self::Nat(r)) => Some(Self::Int((l - r) as i32)),
|
||||
(Self::Nat(l), Self::Nat(r)) => Some(Self::Int(l as i32 - r as i32)),
|
||||
(Self::Float(l), Self::Float(r)) => Some(Self::Float(l - r)),
|
||||
(Self::Int(l), Self::Nat(r)) => Some(Self::from(l - r as i32)),
|
||||
(Self::Nat(l), Self::Int(r)) => Some(Self::from(l as i32 - r)),
|
||||
|
@ -1263,6 +1263,7 @@ impl ValueObj {
|
|||
(Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 == r)),
|
||||
(Self::Str(l), Self::Str(r)) => Some(Self::from(l == r)),
|
||||
(Self::Bool(l), Self::Bool(r)) => Some(Self::from(l == r)),
|
||||
(Self::Type(l), Self::Type(r)) => Some(Self::from(l == r)),
|
||||
(Self::Mut(m), other) => {
|
||||
{
|
||||
let ref_m = &mut *m.borrow_mut();
|
||||
|
@ -1289,6 +1290,7 @@ impl ValueObj {
|
|||
(Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 != r)),
|
||||
(Self::Str(l), Self::Str(r)) => Some(Self::from(l != r)),
|
||||
(Self::Bool(l), Self::Bool(r)) => Some(Self::from(l != r)),
|
||||
(Self::Type(l), Self::Type(r)) => Some(Self::from(l != r)),
|
||||
(Self::Mut(m), other) => {
|
||||
{
|
||||
let ref_m = &mut *m.borrow_mut();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue