mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 18:58:30 +00:00
fix: undoable link
This commit is contained in:
parent
0576b27ad5
commit
debac5726e
4 changed files with 190 additions and 53 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue