fix: avoid infinite recursion bugs

This commit is contained in:
Shunsuke Shibayama 2023-03-22 01:28:22 +09:00
parent 2a0998f0cd
commit b318395a32
10 changed files with 128 additions and 48 deletions

View file

@ -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 {

View file

@ -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)

View file

@ -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) => {

View file

@ -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 =

View file

@ -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 {

View file

@ -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

View file

@ -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 {

View file

@ -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(),

View file

@ -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,
}
}

View file

@ -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();