fix: undoable link

This commit is contained in:
Shunsuke Shibayama 2023-08-06 00:16:14 +09:00
parent 0576b27ad5
commit debac5726e
4 changed files with 190 additions and 53 deletions

View file

@ -1639,21 +1639,25 @@ impl Context {
if let ValueObj::Type(quant_projected_t) = obj {
let projected_t = quant_projected_t.into_typ();
let (quant_sub, _) = self.get_type(&sub.qual_name()).unwrap();
if let Some(sup) = opt_sup {
if let Some(quant_sup) = methods.impl_of() {
// T -> Int, M -> 2
self.substitute_typarams(&quant_sup, sup)
.map_err(|errs| {
Self::undo_substitute_typarams(&quant_sup);
errs
})
.ok()?;
}
}
let sup_substituted = if let Some((sup, quant_sup)) = opt_sup.zip(methods.impl_of())
{
// T -> Int, M -> 2
self.substitute_typarams(&quant_sup, sup)
.map_err(|errs| {
Self::undo_substitute_typarams(&quant_sup);
errs
})
.ok()?;
true
} else {
false
};
// T -> Int, N -> 4
self.substitute_typarams(quant_sub, sub)
.map_err(|errs| {
Self::undo_substitute_typarams(quant_sub);
if sup_substituted {
Self::undo_substitute_typarams(&methods.impl_of().unwrap());
}
errs
})
.ok()?;
@ -1664,14 +1668,15 @@ impl Context {
let t = self.detach(t, &mut tv_cache);
// Int -> T, 2 -> M, 4 -> N
Self::undo_substitute_typarams(quant_sub);
if let Some(quant_sup) = methods.impl_of() {
if sup_substituted {
let quant_sup = methods.impl_of().unwrap();
Self::undo_substitute_typarams(&quant_sup);
}
return Some(t);
}
Self::undo_substitute_typarams(quant_sub);
if let Some(quant_sup) = methods.impl_of() {
Self::undo_substitute_typarams(&quant_sup);
if sup_substituted {
Self::undo_substitute_typarams(&methods.impl_of().unwrap());
}
} else {
log!(err "{obj}");
@ -1753,24 +1758,41 @@ impl Context {
log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
return Ok(()); // TODO: e.g. Sub(Int) / Eq and Sub(?T)
}
let mut errs = EvalErrors::empty();
for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) {
self.substitute_typaram(qtp, stp)?;
if let Err(err) = self.substitute_typaram(qtp, stp) {
errs.extend(err);
}
}
if !errs.is_empty() {
Err(errs)
} else {
Ok(())
}
Ok(())
}
pub(crate) fn overwrite_typarams(&self, qt: &Type, st: &Type) -> EvalResult<()> {
let qtps = qt.typarams();
let stps = st.typarams();
if qt.qual_name() != st.qual_name() || qtps.len() != stps.len() {
if let Some(sub) = st.get_sub() {
return self.overwrite_typarams(qt, &sub);
}
log!(err "{qt} / {st}");
log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
return Ok(()); // TODO: e.g. Sub(Int) / Eq and Sub(?T)
}
let mut errs = EvalErrors::empty();
for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) {
self.overwrite_typaram(qtp, stp)?;
if let Err(err) = self.overwrite_typaram(qtp, stp) {
errs.extend(err);
}
}
if !errs.is_empty() {
Err(errs)
} else {
Ok(())
}
Ok(())
}
fn substitute_typaram(&self, qtp: TyParam, stp: TyParam) -> EvalResult<()> {
@ -1779,18 +1801,33 @@ impl Context {
if !stp.is_unbound_var() || !stp.is_generalized() {
qtp.undoable_link(&stp);
}
if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
/*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}
}*/
Ok(())
}
TyParam::Type(qt) => self.substitute_type(stp, *qt),
TyParam::Value(ValueObj::Type(qt)) => self.substitute_type(stp, qt.into_typ()),
TyParam::Type(qt) => self.substitute_type(*qt, stp),
TyParam::Value(ValueObj::Type(qt)) => self.substitute_type(qt.into_typ(), stp),
TyParam::App { name: _, args } => {
let tps = stp.typarams();
debug_assert_eq!(args.len(), tps.len());
let mut errs = EvalErrors::empty();
for (qtp, stp) in args.iter().zip(tps.into_iter()) {
if let Err(err) = self.substitute_typaram(qtp.clone(), stp) {
errs.extend(err);
}
}
if !errs.is_empty() {
Err(errs)
} else {
Ok(())
}
}
_ => Ok(()),
}
}
fn substitute_type(&self, stp: TyParam, qt: Type) -> EvalResult<()> {
fn substitute_type(&self, qt: Type, stp: TyParam) -> EvalResult<()> {
let st = self.convert_tp_into_type(stp).map_err(|tp| {
EvalError::not_a_type_error(
self.cfg.input.clone(),
@ -1822,18 +1859,45 @@ impl Context {
if !stp.is_unbound_var() || !stp.is_generalized() {
qtp.undoable_link(&stp);
}
if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
/*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}
}*/
Ok(())
}
TyParam::Type(qt) => self.overwrite_type(stp, *qt),
TyParam::Value(ValueObj::Type(qt)) => self.overwrite_type(stp, qt.into_typ()),
// NOTE: Rarely, double overwriting occurs.
// Whether this could be a problem is under consideration.
// e.g. `T` of Array(T, N) <: Add(T, M)
TyParam::FreeVar(ref fv) if fv.is_generalized() => {
if !stp.is_unbound_var() || !stp.is_generalized() {
qtp.undoable_link(&stp);
}
/*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}*/
Ok(())
}
TyParam::Type(qt) => self.overwrite_type(*qt, stp),
TyParam::Value(ValueObj::Type(qt)) => self.overwrite_type(qt.into_typ(), stp),
TyParam::App { name: _, args } => {
let tps = stp.typarams();
debug_assert_eq!(args.len(), tps.len());
let mut errs = EvalErrors::empty();
for (qtp, stp) in args.iter().zip(tps.into_iter()) {
if let Err(err) = self.overwrite_typaram(qtp.clone(), stp) {
errs.extend(err);
}
}
if !errs.is_empty() {
Err(errs)
} else {
Ok(())
}
}
_ => Ok(()),
}
}
fn overwrite_type(&self, stp: TyParam, qt: Type) -> EvalResult<()> {
fn overwrite_type(&self, qt: Type, stp: TyParam) -> EvalResult<()> {
let st = self.convert_tp_into_type(stp).map_err(|tp| {
EvalError::not_a_type_error(
self.cfg.input.clone(),
@ -1857,22 +1921,31 @@ impl Context {
pub(crate) fn undo_substitute_typarams(substituted_q: &Type) {
for tp in substituted_q.typarams().into_iter() {
match tp {
TyParam::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(),
TyParam::Type(t) if t.is_free_var() => {
let Ok(subst) = <&FreeTyVar>::try_from(t.as_ref()) else { unreachable!() };
if subst.is_undoable_linked() {
subst.undo();
}
Self::undo_substitute_typaram(tp);
}
}
fn undo_substitute_typaram(substituted_q_tp: TyParam) {
match substituted_q_tp {
TyParam::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(),
TyParam::Type(t) if t.is_free_var() => {
let Ok(subst) = <&FreeTyVar>::try_from(t.as_ref()) else { unreachable!() };
if subst.is_undoable_linked() {
subst.undo();
}
TyParam::Type(t) => {
Self::undo_substitute_typarams(&t);
}
TyParam::Value(ValueObj::Type(t)) => {
Self::undo_substitute_typarams(t.typ());
}
_ => {}
}
TyParam::Type(t) => {
Self::undo_substitute_typarams(&t);
}
TyParam::Value(ValueObj::Type(t)) => {
Self::undo_substitute_typarams(t.typ());
}
TyParam::App { args, .. } => {
for arg in args.into_iter() {
Self::undo_substitute_typaram(arg);
}
}
_ => {}
}
}

View file

@ -46,7 +46,7 @@ pub trait HasLevel {
///
/// __NOTE__: you should use `Free::get_type/get_subsup` instead of deconstructing the constraint by `match`.
/// Constraints may contain cycles, in which case using `match` to get the contents will cause memory destructions.
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Constraint {
// : Type --> (:> Never, <: Obj)
// :> Sub --> (:> Sub, <: Obj)
@ -67,12 +67,6 @@ impl fmt::Display for Constraint {
}
}
impl fmt::Debug for Constraint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.limited_fmt(f, 10)
}
}
impl LimitedDisplay for Constraint {
fn limited_fmt<W: std::fmt::Write>(&self, f: &mut W, limit: isize) -> fmt::Result {
if limit == 0 {
@ -252,6 +246,7 @@ pub enum FreeKind<T> {
UndoableLinked {
t: T,
previous: Box<FreeKind<T>>,
count: usize,
},
Unbound {
id: Id,
@ -488,6 +483,29 @@ impl<T> FreeKind<T> {
pub const fn is_undoable_linked(&self) -> bool {
matches!(self, Self::UndoableLinked { .. })
}
pub fn undo_count(&self) -> usize {
match self {
Self::UndoableLinked { count, .. } => *count,
_ => 0,
}
}
pub fn inc_undo_count(&mut self) {
#[allow(clippy::single_match)]
match self {
Self::UndoableLinked { count, .. } => *count += 1,
_ => {}
}
}
pub fn dec_undo_count(&mut self) {
#[allow(clippy::single_match)]
match self {
Self::UndoableLinked { count, .. } => *count -= 1,
_ => {}
}
}
}
#[derive(Debug, Clone)]
@ -854,19 +872,36 @@ impl<T: Clone + fmt::Debug + Send + Sync + 'static> Free<T> {
let new = FreeKind::UndoableLinked {
t: to.clone(),
previous: Box::new(prev),
count: 0,
};
*self.borrow_mut() = new;
}
/// interior-mut
pub fn undo(&self) {
let prev = match &*self.borrow() {
FreeKind::UndoableLinked { previous, .. } => *previous.clone(),
let prev = match &mut *self.borrow_mut() {
FreeKind::UndoableLinked {
previous, count, ..
} => {
if *count > 0 {
*count -= 1;
return;
}
*previous.clone()
}
_other => panic!("cannot undo"),
};
self.replace(prev);
}
pub fn undo_stack_size(&self) -> usize {
self.borrow().undo_count()
}
pub fn inc_undo_count(&self) {
self.borrow_mut().inc_undo_count();
}
pub fn unwrap_unbound(self) -> (Option<Str>, usize, Constraint) {
match self.clone_inner() {
FreeKind::Linked(_) | FreeKind::UndoableLinked { .. } => panic!("the value is linked"),

View file

@ -3172,8 +3172,11 @@ impl Type {
}
/// interior-mut
///
/// `inc/dec_undo_count` due to the number of `substitute_typarams/undo_typarams` must be matched
pub(crate) fn undoable_link(&self, to: &Type) {
if self.addr_eq(to) {
self.inc_undo_count();
return;
}
match self {
@ -3183,6 +3186,14 @@ impl Type {
}
}
fn inc_undo_count(&self) {
match self {
Self::FreeVar(fv) => fv.inc_undo_count(),
Self::Refinement(refine) => refine.t.inc_undo_count(),
_ => panic!("{self} is not a free variable"),
}
}
pub fn into_bounded(&self) -> Type {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().clone().into_bounded(),

View file

@ -632,7 +632,7 @@ impl TryFrom<TyParam> for ValueObj {
TyParam::Type(t) => Ok(ValueObj::builtin_type(*t)),
TyParam::Value(v) => Ok(v),
_ => {
log!(err "Expected value, got {:?}", tp);
log!(err "Expected value, got {tp} ({tp:?})");
Err(())
}
}
@ -1274,6 +1274,7 @@ impl TyParam {
/// interior-mut
pub(crate) fn undoable_link(&self, to: &TyParam) {
if self.addr_eq(to) {
self.inc_undo_count();
return;
}
if to.contains_tp(self) {
@ -1287,6 +1288,23 @@ impl TyParam {
_ => panic!("{self} is not a free variable"),
}
}
fn inc_undo_count(&self) {
match self {
Self::FreeVar(fv) => fv.inc_undo_count(),
_ => panic!("{self} is not a free variable"),
}
}
pub fn typarams(&self) -> Vec<TyParam> {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().typarams(),
Self::Type(t) => t.typarams(),
Self::Value(ValueObj::Type(t)) => t.typ().typarams(),
Self::App { args, .. } => args.clone(),
_ => vec![],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]