Skip to content

Commit

Permalink
fix: type variable bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Sep 16, 2024
1 parent 46c349d commit 93305f2
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 31 deletions.
44 changes: 44 additions & 0 deletions crates/erg_compiler/context/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,16 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
}
let (lsub, lsup) = sub_fv.get_subsup().unwrap();
let (rsub, rsup) = sup_fv.get_subsup().unwrap();
// sub: ?T(:> ?U)
// sup: ?U
// => ?T == ?U
if &lsub == maybe_sup {
maybe_sub.link(maybe_sup, self.undoable);
return Ok(());
} else if &rsup == maybe_sub {
maybe_sup.link(maybe_sub, self.undoable);
return Ok(());
}
// ?T(<: Add(?T))
// ?U(:> {1, 2}, <: Add(?U)) ==> {1, 2}
sup_fv.dummy_link();
Expand Down Expand Up @@ -1117,6 +1127,40 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
}
}
}
(FreeVar(sub_fv), FreeVar(sup_fv))
if sub_fv.constraint_is_sandwiched() && sup_fv.constraint_is_typeof() =>
{
if !self.change_generalized && (sub_fv.is_generalized() || sup_fv.is_generalized())
{
log!(info "generalized:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}");
return Ok(());
}
let (lsub, _lsup) = sub_fv.get_subsup().unwrap();
// sub: ?T(:> ?U(: {Str, Int}))
// sup: ?U(: {Str, Int})
// => ?T == ?U
if &lsub == maybe_sup {
maybe_sub.link(maybe_sup, self.undoable);
return Ok(());
}
let rty = sup_fv.get_type().unwrap();
let Some(rtys) = rty.refinement_values() else {
todo!("{rty}");
};
// sub: ?T(:> Nat)
// sup: ?U(: {Str, Int})
// => ?T(:> Nat, <: Int)
for tp in rtys {
let Ok(ty) = self.ctx.convert_tp_into_type(tp.clone()) else {
todo!("{tp}");
};
if self.ctx.subtype_of(&lsub, &ty) {
sub_fv.update_super(|sup| self.ctx.intersection(&sup, &ty));
return Ok(());
}
}
// REVIEW: unreachable?
}
(
Bounded {
sub: lsub,
Expand Down
81 changes: 58 additions & 23 deletions crates/erg_compiler/ty/free.rs
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,55 @@ impl Free<Type> {
self.crack().has_unbound_var()
}
}

/// interior-mut
/// 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) {
if new_constraint.get_type() == Some(&Type::Never) {
panic!("{new_constraint}");
}
// self: T
// new_constraint: (:> T, <: U) => <: U
if new_constraint.get_sub_sup().is_some_and(|(sub, sup)| {
sub.contains_tvar_in_constraint(self) || sup.contains_tvar_in_constraint(self)
}) {
return;
}
match &mut *self.borrow_mut() {
FreeKind::Unbound {
lev, constraint, ..
}
| FreeKind::NamedUnbound {
lev, constraint, ..
} => {
if !in_inst_or_gen && *lev == GENERIC_LEVEL {
log!(err "cannot update the constraint of a generalized type variable");
return;
}
if addr_eq!(*constraint, new_constraint) {
return;
}
*constraint = new_constraint;
}
FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => {
t.destructive_update_constraint(new_constraint, in_inst_or_gen);
}
}
}

/// interior-mut
pub fn update_sub(&self, f: impl FnOnce(Type) -> Type) {
let (sub, sup) = self.get_subsup().unwrap();
let new_constraint = Constraint::new_sandwiched(f(sub), sup);
self.update_constraint(new_constraint, true);
}

/// interior-mut
pub fn update_super(&self, f: impl FnOnce(Type) -> Type) {
let (sub, sup) = self.get_subsup().unwrap();
let new_constraint = Constraint::new_sandwiched(sub, f(sup));
self.update_constraint(new_constraint, true);
}
}

impl Free<TyParam> {
Expand Down Expand Up @@ -1219,6 +1268,15 @@ impl<T: CanbeFree + Send + Clone> Free<T> {
pub fn constraint_is_uninited(&self) -> bool {
self.constraint().map(|c| c.is_uninited()).unwrap_or(false)
}
}

impl Free<TyParam> {
pub fn map(&self, f: impl Fn(TyParam) -> TyParam) {
if let Some(mut linked) = self.get_linked_refmut() {
let mapped = f(mem::take(&mut *linked));
*linked = mapped;
}
}

/// interior-mut
/// if `in_inst_or_gen` is true, constraint will be updated forcibly
Expand Down Expand Up @@ -1248,36 +1306,13 @@ impl<T: CanbeFree + Send + Clone> Free<T> {
}
}

/// interior-mut
pub fn update_sub(&self, f: impl FnOnce(Type) -> Type) {
let (sub, sup) = self.get_subsup().unwrap();
let new_constraint = Constraint::new_sandwiched(f(sub), sup);
self.update_constraint(new_constraint, true);
}

/// interior-mut
pub fn update_super(&self, f: impl FnOnce(Type) -> Type) {
let (sub, sup) = self.get_subsup().unwrap();
let new_constraint = Constraint::new_sandwiched(sub, f(sup));
self.update_constraint(new_constraint, true);
}

/// interior-mut
pub fn update_type(&self, new_type: Type) {
let new_constraint = Constraint::new_type_of(new_type);
self.update_constraint(new_constraint, true);
}
}

impl Free<TyParam> {
pub fn map(&self, f: impl Fn(TyParam) -> TyParam) {
if let Some(mut linked) = self.get_linked_refmut() {
let mapped = f(mem::take(&mut *linked));
*linked = mapped;
}
}
}

pub type FreeTyVar = Free<Type>;
pub type FreeTyParam = Free<TyParam>;

Expand Down
29 changes: 21 additions & 8 deletions crates/erg_compiler/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1773,13 +1773,6 @@ impl CanbeFree for Type {
let Some(fv) = self.as_free() else {
return;
};
// self: T
// new_constraint: (:> T, <: U) => <: U
if new_constraint.get_sub_sup().is_some_and(|(sub, sup)| {
sub.contains_tvar_in_constraint(fv) || sup.contains_tvar_in_constraint(fv)
}) {
return;
}
fv.update_constraint(new_constraint, in_instantiation);
}
}
Expand Down Expand Up @@ -2884,6 +2877,13 @@ impl Type {
|| fv
.get_subsup()
.map(|(sub, sup)| {
if sub.as_free().is_some_and(|sub_fv| {
ref_addr_eq!(sub_fv.forced_as_ref(), target.forced_as_ref())
}) || sup.as_free().is_some_and(|sup_fv| {
ref_addr_eq!(sup_fv.forced_as_ref(), target.forced_as_ref())
}) {
return true;
}
fv.do_avoiding_recursion(|| {
sub.contains_tvar(target) || sup.contains_tvar(target)
})
Expand Down Expand Up @@ -2991,6 +2991,13 @@ impl Type {
|| fv
.get_subsup()
.map(|(sub, sup)| {
if sub.as_free().is_some_and(|sub_fv| {
ref_addr_eq!(sub_fv.forced_as_ref(), target.forced_as_ref())
}) || sup.as_free().is_some_and(|sup_fv| {
ref_addr_eq!(sup_fv.forced_as_ref(), target.forced_as_ref())
}) {
return true;
}
fv.do_avoiding_recursion(|| {
sub.contains_tvar_in_constraint(target)
|| sup.contains_tvar_in_constraint(target)
Expand Down Expand Up @@ -4217,6 +4224,12 @@ impl Type {
Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked().eliminate_subsup(target),
Self::FreeVar(ref fv) if fv.constraint_is_sandwiched() => {
let (sub, sup) = fv.get_subsup().unwrap();
let sub = if sub.addr_eq(target) {
Type::Never
} else {
sub
};
let sup = if sup.addr_eq(target) { Type::Obj } else { sup };
fv.do_avoiding_recursion(|| {
let sub = sub.eliminate_subsup(target);
let sup = sup.eliminate_subsup(target);
Expand Down Expand Up @@ -4901,7 +4914,7 @@ impl Type {
}
match self {
Self::FreeVar(fv) => {
let to = to.clone().eliminate_subsup(self);
let to = to.clone().eliminate_subsup(self); // FIXME: .eliminate_recursion(self)
fv.undoable_link(&to);
}
Self::Refinement(refine) => refine.t.undoable_link(to, list),
Expand Down

0 comments on commit 93305f2

Please sign in to comment.