fix: constraints instantiation bug

This commit is contained in:
Shunsuke Shibayama 2023-03-08 11:06:08 +09:00
parent 5d8506b548
commit 814748a6be
8 changed files with 69 additions and 31 deletions

View file

@ -1287,7 +1287,7 @@ impl Context {
t.clone()
} else {
let tv = Type::FreeVar(new_fv);
tv_cache.push_or_init_tyvar(&name, &tv);
tv_cache.push_or_init_tyvar(&name, &tv, self);
tv
}
}

View file

@ -350,6 +350,7 @@ impl Context {
{
Ok(TyParam::FreeVar(fv))
}
// REVIEW: most likely the result of an error already made
TyParam::FreeVar(_fv) if self.level == 0 => Err(TyCheckErrors::from(
TyCheckError::dummy_infer_error(self.cfg.input.clone(), fn_name!(), line!()),
)),
@ -629,7 +630,11 @@ impl Context {
let res = self.validate_subsup(sub_t, super_t, variance, qnames, loc);
fv.undo();
match res {
Ok(ty) => Ok(ty),
Ok(ty) => {
// TODO: T(:> Nat <: Int) -> T(:> Nat, <: Int) ==> Int -> Nat
// fv.link(&ty);
Ok(ty)
}
Err(errs) => {
fv.link(&Never);
Err(errs)
@ -637,7 +642,6 @@ impl Context {
}
} else {
// no dereference at this point
// drop(constraint);
Ok(Type::FreeVar(fv))
}
}

View file

@ -848,15 +848,23 @@ impl Context {
}
}
}
Err(TyCheckError::no_attr_error(
self.cfg.input.clone(),
line!() as usize,
attr_name.loc(),
namespace.name.to_string(),
obj.ref_t(),
attr_name.inspect(),
self.get_similar_attr(obj.ref_t(), attr_name.inspect()),
))
let coerced = self
.deref_tyvar(obj.t(), Variance::Covariant, &set! {}, obj)
.map_err(|mut errs| errs.remove(0))?;
if &coerced == obj.ref_t() {
Err(TyCheckError::no_attr_error(
self.cfg.input.clone(),
line!() as usize,
attr_name.loc(),
namespace.name.to_string(),
obj.ref_t(),
attr_name.inspect(),
self.get_similar_attr(obj.ref_t(), attr_name.inspect()),
))
} else {
obj.ref_t().coerce();
self.search_method_info(obj, attr_name, input, namespace)
}
}
fn validate_visibility(

View file

@ -121,25 +121,46 @@ impl TyVarCache {
self.already_appeared.insert(name);
}
pub(crate) fn push_or_init_tyvar(&mut self, name: &Str, tv: &Type) {
pub(crate) fn push_or_init_tyvar(&mut self, name: &Str, tv: &Type, ctx: &Context) {
if let Some(inst) = self.tyvar_instances.get(name) {
// T<tv> <: Eq(T<inst>)
// T<inst> is uninitialized
// T<inst>.link(T<tv>);
// T <: Eq(T <: Eq(T <: ...))
let inst = enum_unwrap!(inst, Type::FreeVar);
inst.link(tv);
self.update_tv(inst, tv, ctx);
} else if let Some(inst) = self.typaram_instances.get(name) {
if let TyParam::Type(inst) = inst {
let fv_inst = enum_unwrap!(inst.as_ref(), Type::FreeVar);
fv_inst.link(tv);
self.update_tv(inst, tv, ctx);
} else if let TyParam::FreeVar(fv) = inst {
fv.link(&TyParam::t(tv.clone()));
} else {
unreachable!()
}
} else {
self.tyvar_instances.insert(name.clone(), tv.clone());
}
}
fn update_tv(&self, inst: &Type, tv: &Type, ctx: &Context) {
// T<tv> <: Eq(T<inst>)
// T<inst> is uninitialized
// T<inst>.link(T<tv>);
// T <: Eq(T <: Eq(T <: ...))
let inst = enum_unwrap!(inst, Type::FreeVar);
if inst.constraint_is_uninited() {
inst.link(tv);
} else {
// inst: ?T(<: Int) => old_sub: Never, old_sup: Int
// tv: ?T(:> Nat) => new_sub: Nat, new_sup: Obj
// => ?T(:> Nat, <: Int)
// inst: ?T(:> Str)
// tv: ?T(:> Nat)
// => ?T(:> Nat or Str)
let (old_sub, old_sup) = inst.get_subsup().unwrap();
let tv = enum_unwrap!(tv, Type::FreeVar);
let (new_sub, new_sup) = tv.get_subsup().unwrap();
let new_constraint = Constraint::new_sandwiched(
ctx.union(&old_sub, &new_sub),
ctx.intersection(&old_sup, &new_sup),
);
inst.update_constraint(new_constraint, true);
}
self.tyvar_instances.insert(name.clone(), tv.clone());
}
pub(crate) fn push_or_init_typaram(&mut self, name: &Str, tp: &TyParam) {
@ -147,12 +168,12 @@ impl TyVarCache {
if let Some(_tp) = self.typaram_instances.get(name) {
panic!("{_tp} {tp}");
// return;
}
if let Some(_t) = self.tyvar_instances.get(name) {
} else if let Some(_t) = self.tyvar_instances.get(name) {
panic!("{_t} {tp}");
// return;
} else {
self.typaram_instances.insert(name.clone(), tp.clone());
}
self.typaram_instances.insert(name.clone(), tp.clone());
}
pub(crate) fn appeared(&self, name: &Str) -> bool {
@ -386,7 +407,7 @@ impl Context {
} else {
if tmp_tv_cache.appeared(&name) {
let tyvar = named_free_var(name.clone(), self.level, Constraint::Uninited);
tmp_tv_cache.push_or_init_tyvar(&name, &tyvar);
tmp_tv_cache.push_or_init_tyvar(&name, &tyvar, self);
return Ok(tyvar);
}
if let Some(tv_ctx) = &self.tv_cache {
@ -407,7 +428,7 @@ impl Context {
tmp_tv_cache.push_appeared(name.clone());
let constr = tmp_tv_cache.instantiate_constraint(constr, self, loc)?;
let tyvar = named_free_var(name.clone(), self.level, constr);
tmp_tv_cache.push_or_init_tyvar(&name, &tyvar);
tmp_tv_cache.push_or_init_tyvar(&name, &tyvar, self);
Ok(tyvar)
}
}

View file

@ -124,7 +124,7 @@ impl Context {
// TODO: other than type `Type`
let constr = Constraint::new_type_of(Type);
let tv = named_free_var(name.inspect().clone(), self.level, constr);
tv_cache.push_or_init_tyvar(name.inspect(), &tv);
tv_cache.push_or_init_tyvar(name.inspect(), &tv, self);
Ok(())
}
TypeBoundSpec::NonDefault { lhs, spec } => {
@ -150,7 +150,7 @@ impl Context {
tv_cache.push_or_init_typaram(lhs.inspect(), &tp);
} else {
let tv = named_free_var(lhs.inspect().clone(), self.level, constr);
tv_cache.push_or_init_tyvar(lhs.inspect(), &tv);
tv_cache.push_or_init_tyvar(lhs.inspect(), &tv, self);
}
Ok(())
}
@ -595,7 +595,7 @@ impl Context {
Ok(typ.clone())
} else if not_found_is_qvar {
let tyvar = named_free_var(Str::rc(other), self.level, Constraint::Uninited);
tmp_tv_cache.push_or_init_tyvar(simple.ident.inspect(), &tyvar);
tmp_tv_cache.push_or_init_tyvar(simple.ident.inspect(), &tyvar, self);
Ok(tyvar)
} else {
Err(TyCheckErrors::from(TyCheckError::no_type_error(

View file

@ -838,6 +838,7 @@ impl<T: CanbeFree> Free<T> {
self.constraint().map(|c| c.is_uninited()).unwrap_or(false)
}
/// if `in_inst_or_gen` is true, constraint will be updated forcibly
pub fn update_constraint(&self, new_constraint: Constraint, in_inst_or_gen: bool) {
match unsafe { &mut *self.as_ptr() as &mut FreeKind<T> } {
FreeKind::Unbound {