From 3724a746490d53fc62b222cd501e152dfa029606 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Tue, 22 Aug 2023 21:45:25 +0900 Subject: [PATCH] fix: undo leak bug & sub-unification bugs --- crates/erg_common/triple.rs | 12 ++ crates/erg_compiler/context/eval.rs | 115 +++++++++--------- crates/erg_compiler/context/generalize.rs | 20 ++- .../context/initialize/const_func.rs | 9 +- crates/erg_compiler/context/inquire.rs | 6 +- crates/erg_compiler/context/unify.rs | 50 +++++--- crates/erg_compiler/ty/mod.rs | 79 +++++++++--- crates/erg_compiler/ty/typaram.rs | 37 +++--- 8 files changed, 211 insertions(+), 117 deletions(-) diff --git a/crates/erg_common/triple.rs b/crates/erg_common/triple.rs index 6755cfd56..404d1d4f8 100644 --- a/crates/erg_common/triple.rs +++ b/crates/erg_common/triple.rs @@ -1,3 +1,5 @@ +use std::fmt; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Triple { Ok(T), @@ -5,6 +7,16 @@ pub enum Triple { None, } +impl fmt::Display for Triple { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Triple::Ok(ok) => write!(f, "Ok({ok})"), + Triple::Err(err) => write!(f, "Err({err})"), + Triple::None => write!(f, "None"), + } + } +} + impl Triple { pub fn none_then(self, err: E) -> Result { match self { diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index eb22b4d5b..d1ae5f7b1 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -7,6 +7,7 @@ use erg_common::error::Location; #[allow(unused)] use erg_common::log; use erg_common::set::Set; +use erg_common::shared::Shared; use erg_common::traits::{Locational, Stream}; use erg_common::{dict, fmt_vec, fn_name, option_enum_unwrap, set, Triple}; use erg_common::{ArcArray, Str}; @@ -22,7 +23,7 @@ use crate::ty::constructors::{ array_t, dict_t, mono, named_free_var, poly, proj, proj_call, ref_, ref_mut, refinement, set_t, subr_t, tp_enum, tuple_t, v_enum, }; -use crate::ty::free::{Constraint, FreeTyVar, HasLevel}; +use crate::ty::free::{Constraint, HasLevel}; use crate::ty::typaram::{OpKind, TyParam}; use crate::ty::value::{GenTypeObj, TypeObj, ValueObj}; use crate::ty::{ConstSubr, HasType, Predicate, SubrKind, Type, UserConstSubr, ValueArgs}; @@ -92,27 +93,52 @@ fn op_to_name(op: OpKind) -> &'static str { } } -#[derive(Debug)] -pub struct Substituter<'c> { - ctx: &'c Context, - qt: Type, - #[allow(unused)] - st: Type, - child: Option>>, +#[derive(Debug, Default)] +pub struct UndoableLinkedList { + tys: Shared>, + tps: Shared>, } -impl Drop for Substituter<'_> { +impl Drop for UndoableLinkedList { fn drop(&mut self) { - Self::undo_substitute_typarams(&self.qt); + for t in self.tys.borrow().iter() { + t.undo(); + } + for tp in self.tps.borrow().iter() { + tp.undo(); + } } } +impl UndoableLinkedList { + pub fn new() -> Self { + Self { + tys: Shared::new(set! {}), + tps: Shared::new(set! {}), + } + } + + pub fn push_t(&self, t: &Type) { + self.tys.borrow_mut().insert(t.clone()); + } + + pub fn push_tp(&self, tp: &TyParam) { + self.tps.borrow_mut().insert(tp.clone()); + } +} + +#[derive(Debug)] +pub struct Substituter<'c> { + ctx: &'c Context, + undoable_linked: UndoableLinkedList, + child: Option>>, +} + impl<'c> Substituter<'c> { - fn new(ctx: &'c Context, qt: Type, st: Type) -> Self { + fn new(ctx: &'c Context) -> Self { Self { ctx, - qt, - st, + undoable_linked: UndoableLinkedList::new(), child: None, } } @@ -142,7 +168,7 @@ impl<'c> Substituter<'c> { log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps)); return Ok(None); // TODO: e.g. Sub(Int) / Eq and Sub(?T) } - let mut self_ = Self::new(ctx, qt.clone(), st.clone()); + let mut self_ = Self::new(ctx); let mut errs = EvalErrors::empty(); for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) { if let Err(err) = self_.substitute_typaram(qtp, stp) { @@ -171,7 +197,7 @@ impl<'c> Substituter<'c> { log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps)); return Ok(None); // TODO: e.g. Sub(Int) / Eq and Sub(?T) } - let mut self_ = Self::new(ctx, qt.clone(), st.clone()); + let mut self_ = Self::new(ctx); let mut errs = EvalErrors::empty(); for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) { if let Err(err) = self_.overwrite_typaram(qtp, stp) { @@ -188,7 +214,7 @@ impl<'c> Substituter<'c> { fn substitute_typaram(&mut self, qtp: TyParam, stp: TyParam) -> EvalResult<()> { match qtp { TyParam::FreeVar(ref fv) if fv.is_generalized() => { - qtp.undoable_link(&stp); + qtp.undoable_link(&stp, &self.undoable_linked); /*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) { log!(err "{errs}"); }*/ @@ -226,13 +252,13 @@ impl<'c> Substituter<'c> { ) })?; if !qt.is_undoable_linked_var() && qt.is_generalized() && qt.is_free_var() { - qt.undoable_link(&st); + qt.undoable_link(&st, &self.undoable_linked); } else if qt.is_undoable_linked_var() && qt != st { // e.g. Array(T, N) <: Add(Array(T, M)) // Array((Int), (3)) <: Add(Array((Int), (4))): OK // Array((Int), (3)) <: Add(Array((Str), (4))): NG if let Some(union) = self.ctx.unify(&qt, &st) { - qt.undoable_link(&union); + qt.undoable_link(&union, &self.undoable_linked); } else { return Err(EvalError::unification_error( self.ctx.cfg.input.clone(), @@ -257,7 +283,10 @@ impl<'c> Substituter<'c> { } else { qt }; - if let Err(errs) = self.ctx.undoable_sub_unify(&st, &qt, &(), None) { + if let Err(errs) = self + .ctx + .undoable_sub_unify(&st, &qt, &(), &self.undoable_linked, None) + { log!(err "{errs}"); } Ok(()) @@ -266,7 +295,7 @@ impl<'c> Substituter<'c> { fn overwrite_typaram(&mut self, qtp: TyParam, stp: TyParam) -> EvalResult<()> { match qtp { TyParam::FreeVar(ref fv) if fv.is_undoable_linked() => { - qtp.undoable_link(&stp); + qtp.undoable_link(&stp, &self.undoable_linked); /*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) { log!(err "{errs}"); }*/ @@ -276,7 +305,7 @@ impl<'c> Substituter<'c> { // 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() => { - qtp.undoable_link(&stp); + qtp.undoable_link(&stp, &self.undoable_linked); /*if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) { log!(err "{errs}"); }*/ @@ -314,7 +343,7 @@ impl<'c> Substituter<'c> { ) })?; if qt.is_undoable_linked_var() { - qt.undoable_link(&st); + qt.undoable_link(&st, &self.undoable_linked); } if !st.is_unbound_var() || !st.is_generalized() { self.child = Self::overwrite_typarams(self.ctx, &qt, &st)?.map(Box::new); @@ -325,42 +354,15 @@ impl<'c> Substituter<'c> { } else { qt }; - if let Err(errs) = self.ctx.undoable_sub_unify(&st, &qt, &(), None) { + if let Err(errs) = self + .ctx + .undoable_sub_unify(&st, &qt, &(), &self.undoable_linked, None) + { log!(err "{errs}"); } Ok(()) } - fn undo_substitute_typarams(substituted_q: &Type) { - for tp in substituted_q.typarams().into_iter() { - 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::App { args, .. } => { - for arg in args.into_iter() { - Self::undo_substitute_typaram(arg); - } - }*/ - _ => {} - } - } - /// ```erg /// substitute_self(Iterable('Self), Int) /// -> Iterable(Int) @@ -372,8 +374,9 @@ impl<'c> Substituter<'c> { && t.get_super() .is_some_and(|sup| ctx.supertype_of(&sup, subtype)) { - t.undoable_link(subtype); - return Some(Self::new(ctx, qt.clone(), subtype.clone())); + let mut _self = Self::new(ctx); + t.undoable_link(subtype, &_self.undoable_linked); + return Some(_self); } } None @@ -2173,7 +2176,7 @@ impl Context { let proj = proj_call(coerced, attr_name, args); self.eval_t_params(proj, level, t_loc) .map(|t| { - lhs.coerce(); + lhs.destructive_coerce(); t }) .map_err(|(_, errs)| errs) diff --git a/crates/erg_compiler/context/generalize.rs b/crates/erg_compiler/context/generalize.rs index a468df153..a7f0597bf 100644 --- a/crates/erg_compiler/context/generalize.rs +++ b/crates/erg_compiler/context/generalize.rs @@ -21,7 +21,7 @@ use crate::{feature_error, hir}; use Type::*; use Variance::*; -use super::eval::Substituter; +use super::eval::{Substituter, UndoableLinkedList}; pub struct Generalizer { level: usize, @@ -557,21 +557,29 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> { // we need to force linking to avoid infinite loop // e.g. fv == ?T(<: Int, :> Add(?T)) // fv == ?T(:> ?T.Output, <: Add(Int)) + let list = UndoableLinkedList::new(); let fv_t = Type::FreeVar(fv.clone()); - match (sub_t.contains_type(&fv_t), super_t.contains_type(&fv_t)) { + let dummy = match (sub_t.contains_type(&fv_t), super_t.contains_type(&fv_t)) { // REVIEW: to prevent infinite recursion, but this may cause a nonsense error (true, true) => { fv.dummy_link(); + true } (true, false) => { - fv_t.undoable_link(&super_t); + fv_t.undoable_link(&super_t, &list); + false } (false, true | false) => { - fv_t.undoable_link(&sub_t); + fv_t.undoable_link(&sub_t, &list); + false } - } + }; let res = self.validate_subsup(sub_t, super_t); - fv.undo(); + if dummy { + fv.undo(); + } else { + drop(list); + } match res { Ok(ty) => { // TODO: T(:> Nat <: Int) -> T(:> Nat, <: Int) ==> Int -> Nat diff --git a/crates/erg_compiler/context/initialize/const_func.rs b/crates/erg_compiler/context/initialize/const_func.rs index 071389025..80c9790d1 100644 --- a/crates/erg_compiler/context/initialize/const_func.rs +++ b/crates/erg_compiler/context/initialize/const_func.rs @@ -4,8 +4,7 @@ use std::mem; use erg_common::dict::Dict; #[allow(unused_imports)] use erg_common::log; -use erg_common::{dict, Str}; -use erg_common::{enum_unwrap, set}; +use erg_common::{dict, enum_unwrap, set}; use crate::context::Context; use crate::feature_error; @@ -482,12 +481,16 @@ pub(crate) fn __named_tuple_getitem__( } /// `NamedTuple({ .x = Int; .y = Str }).union() == Int or Str` +/// `GenericNamedTuple.union() == Obj` pub(crate) fn named_tuple_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult { let slf = args .remove_left_or_key("Self") .ok_or_else(|| not_passed("Self"))?; let fields = match ctx.convert_value_into_type(slf) { Ok(Type::NamedTuple(fields)) => fields, + Ok(Type::Mono(n)) if &n == "GenericNamedTuple" => { + return Ok(ValueObj::builtin_type(Type::Obj).into()); + } Ok(other) => { return Err(type_mismatch("NamedTuple", other, "Self")); } @@ -509,7 +512,7 @@ pub(crate) fn as_dict(mut args: ValueArgs, ctx: &Context) -> EvalValueResult fields, - Ok(Type::Mono(Str::Static("Record"))) => { + Ok(Type::Mono(n)) if &n == "Record" => { let dict = dict! { Type::Obj => Type::Obj }; return Ok(ValueObj::builtin_type(Type::from(dict)).into()); } diff --git a/crates/erg_compiler/context/inquire.rs b/crates/erg_compiler/context/inquire.rs index e82f6d658..619edff44 100644 --- a/crates/erg_compiler/context/inquire.rs +++ b/crates/erg_compiler/context/inquire.rs @@ -765,7 +765,7 @@ impl Context { for ctx in ctxs { match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) { Triple::Ok(vi) => { - obj.ref_t().coerce(); + obj.ref_t().destructive_coerce(); return Triple::Ok(vi); } Triple::Err(e) => { @@ -1133,7 +1133,7 @@ impl Context { .map_err(|mut errs| errs.remove(0))?; if &coerced != obj.ref_t() { let hash = get_hash(obj.ref_t()); - obj.ref_t().coerce(); + obj.ref_t().destructive_coerce(); if get_hash(obj.ref_t()) != hash { return self .search_method_info(obj, attr_name, pos_args, kw_args, input, namespace); @@ -1392,7 +1392,7 @@ impl Context { } if sub != Never { let hash = get_hash(instance); - instance.coerce(); + instance.destructive_coerce(); if instance.is_quantified_subr() { let instance = self.instantiate(instance.clone(), obj)?; self.substitute_call(obj, attr_name, &instance, pos_args, kw_args)?; diff --git a/crates/erg_compiler/context/unify.rs b/crates/erg_compiler/context/unify.rs index 3e91b8ca8..d77b45ed6 100644 --- a/crates/erg_compiler/context/unify.rs +++ b/crates/erg_compiler/context/unify.rs @@ -23,17 +23,23 @@ use Predicate as Pred; use Type::*; use ValueObj::{Inf, NegInf}; +use super::eval::UndoableLinkedList; use super::initialize::const_func::sub_tpdict_get; -pub struct Unifier<'c, 'l, L: Locational> { +pub struct Unifier<'c, 'l, 'u, L: Locational> { ctx: &'c Context, loc: &'l L, - undoable: bool, + undoable: Option<&'u UndoableLinkedList>, param_name: Option, } -impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { - pub fn new(ctx: &'c Context, loc: &'l L, undoable: bool, param_name: Option) -> Self { +impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> { + pub fn new( + ctx: &'c Context, + loc: &'l L, + undoable: Option<&'u UndoableLinkedList>, + param_name: Option, + ) -> Self { Self { ctx, loc, @@ -43,7 +49,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { } } -impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { +impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> { /// ```erg /// occur(?T, ?T) ==> OK /// occur(X -> ?T, ?T) ==> Error @@ -645,7 +651,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { /// sub_unify([?T; 0], Mutate): (/* OK */) /// ``` fn sub_unify(&self, maybe_sub: &Type, maybe_sup: &Type) -> TyCheckResult<()> { - log!(info "trying sub_unify:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}"); + log!(info "trying {}sub_unify:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}", self.undoable.map_or("", |_| "undoable")); // In this case, there is no new information to be gained // この場合、特に新しく得られる情報はない if maybe_sub == &Type::Never || maybe_sup == &Type::Obj || maybe_sup == maybe_sub { @@ -939,7 +945,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { // * sub_unify(Bool, ?T(<: Bool or Y)): (?T == Bool) // * sub_unify(Float, ?T(<: Structural{ .imag = ?U })) ==> ?U == Float if let Some((sub, mut sup)) = sup_fv.get_subsup() { - if sup.is_structural() { + if sup.is_structural() || !sup.is_recursive() { self.sub_unify(maybe_sub, &sup)?; } let new_sub = self.ctx.union(maybe_sub, &sub); @@ -951,7 +957,9 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { { let (l, r) = new_sub.union_pair().unwrap_or((maybe_sub.clone(), sub)); let unified = self.unify(&l, &r); - if unified.is_none() { + if let Some(unified) = unified { + log!("unify({l}, {r}) == {unified}"); + } else { let maybe_sub = self.ctx.readable_type(maybe_sub.clone()); let new_sub = self.ctx.readable_type(new_sub); return Err(TyCheckErrors::from( @@ -989,7 +997,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { if let Some((_, sub_ty)) = sub_fields.get_key_value(&sup_field) { self.sub_unify(sub_ty, &sup_ty)?; } else if !self.ctx.subtype_of(&sub_fv.get_sub().unwrap(), &Never) { - maybe_sub.coerce(); + maybe_sub.coerce(self.undoable); return self.sub_unify(maybe_sub, maybe_sup); } else { // e.g. ?T / Structural({ .method = (self: ?T) -> Int }) @@ -1371,6 +1379,7 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { } } if let Some(sup_ty) = min_compatible { + let _substituter = Substituter::substitute_self(sup_ty, maybe_sub, self.ctx); let sub_instance = self.ctx.instantiate_def_type(sup_ty)?; let variances = self .ctx @@ -1402,17 +1411,27 @@ impl<'c, 'l, L: Locational> Unifier<'c, 'l, L> { /// Unify two types into a single type based on the subtype relation. /// - /// Error if they can't unify without upcasting both types (derefining is allowed) or using the Or type + /// Error if they can't unify without upcasting both types (derefining is allowed) or using Or types /// ```erg /// unify(Int, Nat) == Some(Int) /// unify(Int, Str) == None /// unify({1.2}, Nat) == Some(Float) /// unify(Nat, Int!) == Some(Int) /// unify(Eq, Int) == None + /// unify(Int or Str, Int) == Some(Int or Str) + /// unify(Int or Str, NoneType) == None /// ``` fn unify(&self, lhs: &Type, rhs: &Type) -> Option { #[allow(clippy::single_match)] match (lhs, rhs) { + (Type::Or(l, r), other) | (other, Type::Or(l, r)) => { + if let Some(t) = self.unify(l, other) { + return self.unify(&t, l); + } else if let Some(t) = self.unify(r, other) { + return self.unify(&t, l); + } + return None; + } (Type::FreeVar(fv), _) if fv.is_linked() => return self.unify(&fv.crack(), rhs), (_, Type::FreeVar(fv)) if fv.is_linked() => return self.unify(lhs, &fv.crack()), // TODO: unify(?T, ?U) ? @@ -1459,7 +1478,7 @@ impl Context { maybe_sup: &Type, loc: &impl Locational, ) -> TyCheckResult<()> { - let unifier = Unifier::new(self, loc, false, None); + let unifier = Unifier::new(self, loc, None, None); unifier.occur(maybe_sub, maybe_sup) } @@ -1471,7 +1490,7 @@ impl Context { loc: &impl Locational, is_structural: bool, ) -> TyCheckResult<()> { - let unifier = Unifier::new(self, loc, is_structural, None); + let unifier = Unifier::new(self, loc, None, None); unifier.sub_unify_tp(maybe_sub, maybe_sup, variance, is_structural) } @@ -1482,7 +1501,7 @@ impl Context { loc: &impl Locational, param_name: Option<&Str>, ) -> TyCheckResult<()> { - let unifier = Unifier::new(self, loc, false, param_name.cloned()); + let unifier = Unifier::new(self, loc, None, param_name.cloned()); unifier.sub_unify(maybe_sub, maybe_sup) } @@ -1491,14 +1510,15 @@ impl Context { maybe_sub: &Type, maybe_sup: &Type, loc: &impl Locational, + list: &UndoableLinkedList, param_name: Option<&Str>, ) -> TyCheckResult<()> { - let unifier = Unifier::new(self, loc, true, param_name.cloned()); + let unifier = Unifier::new(self, loc, Some(list), param_name.cloned()); unifier.sub_unify(maybe_sub, maybe_sup) } pub(crate) fn unify(&self, lhs: &Type, rhs: &Type) -> Option { - let unifier = Unifier::new(self, &(), false, None); + let unifier = Unifier::new(self, &(), None, None); unifier.unify(lhs, rhs) } } diff --git a/crates/erg_compiler/ty/mod.rs b/crates/erg_compiler/ty/mod.rs index 727083fdd..f48fd5a30 100644 --- a/crates/erg_compiler/ty/mod.rs +++ b/crates/erg_compiler/ty/mod.rs @@ -43,6 +43,8 @@ pub use value::ValueObj; use value::ValueObj::{Inf, NegInf}; pub use vis::*; +use crate::context::eval::UndoableLinkedList; + use self::constructors::{bounded, free_var, named_free_var, proj_call, subr_t}; pub const STR_OMIT_THRESHOLD: usize = 16; @@ -2558,25 +2560,51 @@ impl Type { /// ```erg /// ?T(:> ?U(:> Int)).coerce(): ?T == ?U == Int /// ``` - pub fn coerce(&self) { + pub fn destructive_coerce(&self) { match self { Type::FreeVar(fv) if fv.is_linked() => { - fv.crack().coerce(); + fv.crack().destructive_coerce(); } Type::FreeVar(fv) if fv.is_unbound() => { let (sub, _sup) = fv.get_subsup().unwrap(); - sub.coerce(); + sub.destructive_coerce(); self.destructive_link(&sub); } Type::And(l, r) | Type::Or(l, r) => { - l.coerce(); - r.coerce(); + l.destructive_coerce(); + r.destructive_coerce(); + } + Type::Not(l) => l.destructive_coerce(), + Type::Poly { params, .. } => { + for p in params { + if let Ok(t) = <&Type>::try_from(p) { + t.destructive_coerce(); + } + } + } + _ => {} + } + } + + pub fn undoable_coerce(&self, list: &UndoableLinkedList) { + match self { + Type::FreeVar(fv) if fv.is_linked() => { + fv.crack().undoable_coerce(list); + } + Type::FreeVar(fv) if fv.is_unbound() => { + let (sub, _sup) = fv.get_subsup().unwrap(); + sub.undoable_coerce(list); + self.undoable_link(&sub, list); + } + Type::And(l, r) | Type::Or(l, r) => { + l.undoable_coerce(list); + r.undoable_coerce(list); } - Type::Not(l) => l.coerce(), + Type::Not(l) => l.undoable_coerce(list), Type::Poly { params, .. } => { for p in params { if let Ok(t) = <&Type>::try_from(p) { - t.coerce(); + t.undoable_coerce(list); } } } @@ -2584,6 +2612,14 @@ impl Type { } } + pub fn coerce(&self, list: Option<&UndoableLinkedList>) { + if let Some(list) = list { + self.undoable_coerce(list); + } else { + self.destructive_coerce(); + } + } + pub fn qvars(&self) -> Set<(Str, Constraint)> { match self { Self::FreeVar(fv) if fv.is_linked() => fv.unsafe_crack().qvars(), @@ -3327,21 +3363,22 @@ 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) { + pub(crate) fn undoable_link(&self, to: &Type, list: &UndoableLinkedList) { + list.push_t(self); if self.addr_eq(to) { self.inc_undo_count(); return; } match self { Self::FreeVar(fv) => fv.undoable_link(to), - Self::Refinement(refine) => refine.t.undoable_link(to), + Self::Refinement(refine) => refine.t.undoable_link(to, list), _ => panic!("{self} is not a free variable"), } } - pub(crate) fn link(&self, to: &Type, undoable: bool) { - if undoable { - self.undoable_link(to); + pub(crate) fn link(&self, to: &Type, list: Option<&UndoableLinkedList>) { + if let Some(list) = list { + self.undoable_link(to, list); } else { self.destructive_link(to); } @@ -3350,7 +3387,7 @@ impl Type { pub(crate) fn undo(&self) { match self { Self::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(), - Self::FreeVar(fv) if fv.constraint_is_sandwiched() => { + /*Self::FreeVar(fv) if fv.constraint_is_sandwiched() => { let (sub, sup) = fv.get_subsup().unwrap(); sub.undo(); sup.undo(); @@ -3359,29 +3396,33 @@ impl Type { for param in params { param.undo(); } - } + }*/ _ => {} } } - pub(crate) fn undoable_update_constraint(&self, new_constraint: Constraint) { + pub(crate) fn undoable_update_constraint( + &self, + new_constraint: Constraint, + list: &UndoableLinkedList, + ) { let level = self.level().unwrap(); let new = if let Some(name) = self.unbound_name() { named_free_var(name, level, new_constraint) } else { free_var(level, new_constraint) }; - self.undoable_link(&new); + self.undoable_link(&new, list); } pub(crate) fn update_constraint( &self, new_constraint: Constraint, - undoable: bool, + list: Option<&UndoableLinkedList>, in_instantiation: bool, ) { - if undoable { - self.undoable_update_constraint(new_constraint); + if let Some(list) = list { + self.undoable_update_constraint(new_constraint, list); } else { self.destructive_update_constraint(new_constraint, in_instantiation); } diff --git a/crates/erg_compiler/ty/typaram.rs b/crates/erg_compiler/ty/typaram.rs index b3b9207a9..8adca5669 100644 --- a/crates/erg_compiler/ty/typaram.rs +++ b/crates/erg_compiler/ty/typaram.rs @@ -11,6 +11,8 @@ use erg_common::{dict, log, ref_addr_eq, set, Str}; use erg_parser::ast::ConstLambda; use erg_parser::token::TokenKind; +use crate::context::eval::UndoableLinkedList; + use super::constructors::int_interval; use super::free::{ CanbeFree, Constraint, FreeKind, FreeTyParam, FreeTyVar, HasLevel, Level, GENERIC_LEVEL, @@ -1090,13 +1092,13 @@ impl TyParam { } } - pub fn coerce(&self) { + pub fn destructive_coerce(&self) { match self { TyParam::FreeVar(fv) if fv.is_linked() => { - fv.crack().coerce(); + fv.crack().destructive_coerce(); } - TyParam::Type(t) => t.coerce(), - TyParam::Value(ValueObj::Type(t)) => t.typ().coerce(), + TyParam::Type(t) => t.destructive_coerce(), + TyParam::Value(ValueObj::Type(t)) => t.typ().destructive_coerce(), _ => {} } } @@ -1398,7 +1400,8 @@ impl TyParam { } /// interior-mut - pub(crate) fn undoable_link(&self, to: &TyParam) { + pub(crate) fn undoable_link(&self, to: &TyParam, list: &UndoableLinkedList) { + list.push_tp(self); if self.addr_eq(to) { self.inc_undo_count(); return; @@ -1409,9 +1412,9 @@ impl TyParam { } } - pub(crate) fn link(&self, to: &TyParam, undoable: bool) { - if undoable { - self.undoable_link(to); + pub(crate) fn link(&self, to: &TyParam, list: Option<&UndoableLinkedList>) { + if let Some(list) = list { + self.undoable_link(to, list); } else { self.destructive_link(to); } @@ -1422,33 +1425,37 @@ impl TyParam { Self::FreeVar(fv) if fv.is_undoable_linked() => fv.undo(), Self::Type(t) => t.undo(), Self::Value(ValueObj::Type(t)) => t.typ().undo(), - Self::App { args, .. } => { + /*Self::App { args, .. } => { for arg in args { arg.undo(); } - } + }*/ _ => {} } } - pub(crate) fn undoable_update_constraint(&self, new_constraint: Constraint) { + pub(crate) fn undoable_update_constraint( + &self, + new_constraint: Constraint, + list: &UndoableLinkedList, + ) { let level = self.level().unwrap(); let new = if let Some(name) = self.unbound_name() { Self::named_free_var(name, level, new_constraint) } else { Self::free_var(level, new_constraint) }; - self.undoable_link(&new); + self.undoable_link(&new, list); } pub(crate) fn update_constraint( &self, new_constraint: Constraint, - undoable: bool, + list: Option<&UndoableLinkedList>, in_instantiation: bool, ) { - if undoable { - self.undoable_update_constraint(new_constraint); + if let Some(list) = list { + self.undoable_update_constraint(new_constraint, list); } else { self.destructive_update_constraint(new_constraint, in_instantiation); }