fix: infinite recursion bug

add `Immutable` trait (Type: !Immutable)
This commit is contained in:
Shunsuke Shibayama 2024-09-04 20:38:46 +09:00
parent de92e295dc
commit f9eb562848
26 changed files with 415 additions and 163 deletions

View file

@ -83,5 +83,5 @@ dinst = "install --path . --features debug --features els"
ntest = "nextest run"
# +nightly
# you must specify the --target option
# you must specify the --target option (e.g. x86_64-unknown-linux-gnu, aarch64-apple-darwin, x86_64-pc-windows-msvc)
drc_r = "r -Zbuild-std -Zbuild-std-features=core/debug_refcell"

View file

@ -5,7 +5,7 @@ use std::str::FromStr;
use erg_common::consts::CASE_SENSITIVE;
use erg_common::normalize_path;
use erg_common::traits::{DequeStream, Locational};
use erg_common::traits::{DequeStream, Immutable, Locational};
use erg_compiler::erg_parser::token::{Token, TokenStream};
@ -18,6 +18,8 @@ use crate::server::ELSResult;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NormalizedUrl(Url);
impl Immutable for NormalizedUrl {}
impl fmt::Display for NormalizedUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)

View file

@ -5,6 +5,7 @@ use std::sync::Arc;
use crate::dict::Dict;
use crate::set::Set;
use crate::shared::Shared;
use crate::traits::Immutable;
use crate::{ArcArray, Str};
#[derive(Debug)]
@ -45,7 +46,7 @@ impl CacheSet<str> {
}
}
impl<T: Hash + Eq + Clone> CacheSet<[T]> {
impl<T: Hash + Eq + Clone + Immutable> CacheSet<[T]> {
pub fn get(&self, q: &[T]) -> Arc<[T]> {
if let Some(cached) = self.0.borrow().get(q) {
return cached.clone();
@ -56,7 +57,18 @@ impl<T: Hash + Eq + Clone> CacheSet<[T]> {
}
}
impl<T: Hash + Eq> CacheSet<T> {
impl<T: Hash + Eq + Clone> CacheSet<[T]> {
pub fn linear_get(&self, q: &[T]) -> Arc<[T]> {
if let Some(cached) = self.0.borrow().linear_get(q) {
return cached.clone();
} // &self.0 is dropped
let s = ArcArray::from(q);
self.0.borrow_mut().insert(s.clone());
s
}
}
impl<T: Hash + Eq + Immutable> CacheSet<T> {
pub fn get<Q>(&self, q: &Q) -> Arc<T>
where
Arc<T>: Borrow<Q>,
@ -71,6 +83,21 @@ impl<T: Hash + Eq> CacheSet<T> {
}
}
impl<T: Hash + Eq> CacheSet<T> {
pub fn linear_get<Q>(&self, q: &Q) -> Arc<T>
where
Arc<T>: Borrow<Q>,
Q: ?Sized + Eq + ToOwned<Owned = T>,
{
if let Some(cached) = self.0.borrow().linear_get(q) {
return cached.clone();
} // &self.0 is dropped
let s = Arc::from(q.to_owned());
self.0.borrow_mut().insert(s.clone());
s
}
}
#[derive(Debug, Clone)]
pub struct CacheDict<K, V: ?Sized>(Shared<Dict<K, Arc<V>>>);
@ -86,15 +113,26 @@ impl<K: Hash + Eq, V: ?Sized> CacheDict<K, V> {
}
}
impl<K: Hash + Eq, V> CacheDict<K, V> {
impl<K: Hash + Eq + Immutable, V> CacheDict<K, V> {
pub fn get<Q: ?Sized + Hash + Eq>(&self, k: &Q) -> Option<Arc<V>>
where
K: Borrow<Q>,
{
self.0.borrow().get(k).cloned()
}
}
impl<K: Hash + Eq, V> CacheDict<K, V> {
pub fn insert(&self, k: K, v: V) {
self.0.borrow_mut().insert(k, Arc::new(v));
}
}
impl<K: Eq, V> CacheDict<K, V> {
pub fn linear_get<Q: ?Sized + Eq>(&self, k: &Q) -> Option<Arc<V>>
where
K: Borrow<Q>,
{
self.0.borrow().linear_get(k).cloned()
}
}

View file

@ -9,6 +9,7 @@ use std::ops::{Index, IndexMut};
use crate::fxhash::FxHashMap;
use crate::get_hash;
use crate::traits::Immutable;
#[macro_export]
macro_rules! dict {
@ -25,13 +26,13 @@ pub struct Dict<K, V> {
dict: FxHashMap<K, V>,
}
impl<K: Hash + Eq, V: Hash + Eq> PartialEq for Dict<K, V> {
impl<K: Hash + Eq + Immutable, V: Hash + Eq> PartialEq for Dict<K, V> {
fn eq(&self, other: &Dict<K, V>) -> bool {
self.dict == other.dict
}
}
impl<K: Hash + Eq, V: Hash + Eq> Eq for Dict<K, V> {}
impl<K: Hash + Eq + Immutable, V: Hash + Eq> Eq for Dict<K, V> {}
impl<K: Hash, V: Hash> Hash for Dict<K, V> {
fn hash<H: Hasher>(&self, state: &mut H) {
@ -82,7 +83,7 @@ impl<K: Hash + Eq, V> From<Vec<(K, V)>> for Dict<K, V> {
}
}
impl<K: Hash + Eq, V, Q: ?Sized> Index<&Q> for Dict<K, V>
impl<K: Hash + Eq + Immutable, V, Q: ?Sized> Index<&Q> for Dict<K, V>
where
K: Borrow<Q>,
Q: Hash + Eq,
@ -94,7 +95,7 @@ where
}
}
impl<K: Hash + Eq, V, Q: ?Sized> IndexMut<&Q> for Dict<K, V>
impl<K: Hash + Eq + Immutable, V, Q: ?Sized> IndexMut<&Q> for Dict<K, V>
where
K: Borrow<Q>,
Q: Hash + Eq,
@ -195,6 +196,15 @@ impl<K, V> Dict<K, V> {
{
self.dict.retain(f);
}
pub fn get_by(&self, k: &K, cmp: impl Fn(&K, &K) -> bool) -> Option<&V> {
for (k_, v) in self.dict.iter() {
if cmp(k, k_) {
return Some(v);
}
}
None
}
}
impl<K, V> IntoIterator for Dict<K, V> {
@ -215,7 +225,47 @@ impl<'a, K, V> IntoIterator for &'a Dict<K, V> {
}
}
impl<K: Hash + Eq, V> Dict<K, V> {
impl<K: Eq, V> Dict<K, V> {
/// K: interior-mutable
pub fn linear_get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Eq + ?Sized,
{
self.dict
.iter()
.find(|(k, _)| (*k).borrow() == key)
.map(|(_, v)| v)
}
pub fn linear_get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Eq + ?Sized,
{
self.dict
.iter_mut()
.find(|(k, _)| (*k).borrow() == key)
.map(|(_, v)| v)
}
}
impl<K: Eq, V: Eq> Dict<K, V> {
/// K: interior-mutable
pub fn linear_eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
for (k, v) in self.iter() {
if other.linear_get(k) != Some(v) {
return false;
}
}
true
}
}
impl<K: Hash + Eq + Immutable, V> Dict<K, V> {
#[inline]
pub fn get<Q>(&self, k: &Q) -> Option<&V>
where
@ -225,15 +275,6 @@ impl<K: Hash + Eq, V> Dict<K, V> {
self.dict.get(k)
}
pub fn get_by(&self, k: &K, cmp: impl Fn(&K, &K) -> bool) -> Option<&V> {
for (k_, v) in self.dict.iter() {
if cmp(k, k_) {
return Some(v);
}
}
None
}
#[inline]
pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
@ -260,11 +301,6 @@ impl<K: Hash + Eq, V> Dict<K, V> {
self.dict.contains_key(k)
}
#[inline]
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
self.dict.insert(k, v)
}
#[inline]
pub fn remove<Q>(&mut self, k: &Q) -> Option<V>
where
@ -281,6 +317,13 @@ impl<K: Hash + Eq, V> Dict<K, V> {
{
self.dict.remove_entry(k)
}
}
impl<K: Hash + Eq, V> Dict<K, V> {
#[inline]
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
self.dict.insert(k, v)
}
/// NOTE: This method does not consider pairing with values and keys. That is, a value may be paired with a different key (can be considered equal).
/// If you need to consider the pairing of the keys and values, use `guaranteed_extend` instead.

View file

@ -6,6 +6,7 @@ use std::path::{Component, Path, PathBuf};
use crate::consts::PYTHON_MODE;
use crate::env::erg_pkgs_path;
use crate::traits::Immutable;
use crate::{normalize_path, Str};
/// Guaranteed equivalence path.
@ -16,6 +17,8 @@ use crate::{normalize_path, Str};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct NormalizedPathBuf(PathBuf);
impl Immutable for NormalizedPathBuf {}
impl fmt::Display for NormalizedPathBuf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display())

View file

@ -5,6 +5,7 @@ use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
use crate::fxhash::FxHashSet;
use crate::traits::Immutable;
use crate::{debug_fmt_iter, fmt_iter, get_hash};
#[cfg(feature = "pylib")]
@ -46,13 +47,13 @@ where
}
}
impl<T: Hash + Eq> PartialEq for Set<T> {
impl<T: Hash + Eq + Immutable> PartialEq for Set<T> {
fn eq(&self, other: &Set<T>) -> bool {
self.elems.eq(&other.elems)
}
}
impl<T: Hash + Eq> Eq for Set<T> {}
impl<T: Hash + Eq + Immutable> Eq for Set<T> {}
impl<T> Default for Set<T> {
fn default() -> Self {
@ -108,9 +109,15 @@ impl<T> Set<T> {
elems: FxHashSet::default(),
}
}
}
impl<T: Hash> Set<T> {
pub fn get_by<Q>(&self, value: &Q, cmp: impl Fn(&Q, &Q) -> bool) -> Option<&T>
where
T: Borrow<Q>,
Q: ?Sized,
{
self.elems.iter().find(|&v| cmp(v.borrow(), value))
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
elems: FxHashSet::with_capacity_and_hasher(capacity, Default::default()),
@ -163,7 +170,50 @@ impl<'a, T> IntoIterator for &'a Set<T> {
}
}
impl<T: Hash + Eq> Set<T> {
impl<T: Eq> Set<T> {
pub fn linear_get<Q>(&self, value: &Q) -> Option<&T>
where
T: Borrow<Q>,
Q: ?Sized + Eq,
{
self.elems.iter().find(|x| (*x).borrow() == value)
}
pub fn linear_contains<Q>(&self, value: &Q) -> bool
where
T: Borrow<Q>,
Q: ?Sized + Eq,
{
self.elems.iter().any(|x| (*x).borrow() == value)
}
pub fn linear_eq(&self, other: &Set<T>) -> bool {
self.len() == other.len() && self.iter().all(|x| other.linear_contains(x))
}
pub fn linear_remove<Q>(&mut self, value: &Q) -> bool
where
T: Borrow<Q>,
Q: ?Sized + Eq,
{
let mut found = false;
self.elems.retain(|x| {
let eq = (*x).borrow() == value;
if eq {
found = true;
}
!eq
});
found
}
pub fn linear_exclude(mut self, other: &T) -> Set<T> {
self.linear_remove(other);
self
}
}
impl<T: Hash + Eq + Immutable> Set<T> {
#[inline]
pub fn get<Q>(&self, value: &Q) -> Option<&T>
where
@ -173,14 +223,6 @@ impl<T: Hash + Eq> Set<T> {
self.elems.get(value)
}
pub fn get_by<Q>(&self, value: &Q, cmp: impl Fn(&Q, &Q) -> bool) -> Option<&T>
where
T: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.elems.iter().find(|&v| cmp(v.borrow(), value))
}
#[inline]
pub fn contains<Q>(&self, value: &Q) -> bool
where
@ -190,12 +232,6 @@ impl<T: Hash + Eq> Set<T> {
self.elems.contains(value)
}
/// newly inserted: true, already present: false
#[inline]
pub fn insert(&mut self, value: T) -> bool {
self.elems.insert(value)
}
#[inline]
pub fn remove<Q>(&mut self, value: &Q) -> bool
where
@ -205,6 +241,42 @@ impl<T: Hash + Eq> Set<T> {
self.elems.remove(value)
}
pub fn exclude(mut self, other: &T) -> Set<T> {
self.remove(other);
self
}
}
impl<T: Hash + Eq + Clone + Immutable> Set<T> {
/// ```
/// # use erg_common::set;
/// # use erg_common::set::Set;
/// assert_eq!(Set::multi_intersection([set!{1, 3}, set!{1, 2}].into_iter()), set!{1});
/// assert_eq!(Set::multi_intersection([set!{1, 3}, set!{1, 2}, set!{2}].into_iter()), set!{1, 2});
/// assert_eq!(Set::multi_intersection([set!{1, 3}, set!{1, 2}, set!{2, 3}].into_iter()), set!{1, 2, 3});
/// ```
pub fn multi_intersection<I>(mut i: I) -> Set<T>
where
I: Iterator<Item = Set<T>> + Clone,
{
let mut res = set! {};
while let Some(s) = i.next() {
res = res.union_from_iter(
s.into_iter()
.filter(|x| i.clone().any(|set| set.contains(x))),
);
}
res
}
}
impl<T: Hash + Eq> Set<T> {
/// newly inserted: true, already present: false
#[inline]
pub fn insert(&mut self, value: T) -> bool {
self.elems.insert(value)
}
#[inline]
pub fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
self.elems.extend(iter);
@ -295,27 +367,6 @@ impl<T: Hash + Eq + Clone> Set<T> {
self.intersection(&iter.collect())
}
/// ```
/// # use erg_common::set;
/// # use erg_common::set::Set;
/// assert_eq!(Set::multi_intersection([set!{1, 3}, set!{1, 2}].into_iter()), set!{1});
/// assert_eq!(Set::multi_intersection([set!{1, 3}, set!{1, 2}, set!{2}].into_iter()), set!{1, 2});
/// assert_eq!(Set::multi_intersection([set!{1, 3}, set!{1, 2}, set!{2, 3}].into_iter()), set!{1, 2, 3});
/// ```
pub fn multi_intersection<I>(mut i: I) -> Set<T>
where
I: Iterator<Item = Set<T>> + Clone,
{
let mut res = set! {};
while let Some(s) = i.next() {
res = res.union_from_iter(
s.into_iter()
.filter(|x| i.clone().any(|set| set.contains(x))),
);
}
res
}
pub fn difference(&self, other: &Set<T>) -> Set<T> {
let u = self.elems.difference(&other.elems);
Self {
@ -331,11 +382,6 @@ impl<T: Hash + Eq + Clone> Set<T> {
self.insert(other);
self
}
pub fn exclude(mut self, other: &T) -> Set<T> {
self.remove(other);
self
}
}
impl<T: Hash + Ord> Set<T> {

View file

@ -1371,3 +1371,42 @@ impl<T: Clone> OptionalTranspose for Option<Vec<T>> {
pub trait New {
fn new(cfg: ErgConfig) -> Self;
}
/// Indicates that the type has no interior mutability
// TODO: auto trait
pub trait Immutable {}
impl Immutable for () {}
impl Immutable for bool {}
impl Immutable for char {}
impl Immutable for u8 {}
impl Immutable for u16 {}
impl Immutable for u32 {}
impl Immutable for u64 {}
impl Immutable for u128 {}
impl Immutable for usize {}
impl Immutable for i8 {}
impl Immutable for i16 {}
impl Immutable for i32 {}
impl Immutable for i64 {}
impl Immutable for i128 {}
impl Immutable for isize {}
impl Immutable for f32 {}
impl Immutable for f64 {}
impl Immutable for str {}
impl Immutable for String {}
impl Immutable for crate::Str {}
impl Immutable for std::path::PathBuf {}
impl Immutable for std::path::Path {}
impl Immutable for std::ffi::OsString {}
impl Immutable for std::ffi::OsStr {}
impl Immutable for std::time::Duration {}
impl Immutable for std::time::SystemTime {}
impl Immutable for std::time::Instant {}
impl<T: Immutable + ?Sized> Immutable for &T {}
impl<T: Immutable> Immutable for Option<T> {}
impl<T: Immutable> Immutable for Vec<T> {}
impl<T: Immutable> Immutable for [T] {}
impl<T: Immutable + ?Sized> Immutable for Box<T> {}
impl<T: Immutable + ?Sized> Immutable for std::rc::Rc<T> {}
impl<T: Immutable + ?Sized> Immutable for std::sync::Arc<T> {}

View file

@ -1,6 +1,7 @@
//! Topological sort
use crate::dict::Dict;
use crate::set::Set;
use crate::traits::Immutable;
use std::fmt::Debug;
use std::hash::Hash;
@ -48,13 +49,13 @@ impl TopoSortError {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node<T: Eq + Hash, U> {
pub struct Node<T: Eq + Hash + Immutable, U> {
pub id: T,
pub data: U,
pub depends_on: Set<T>,
}
impl<T: Eq + Hash, U> Node<T, U> {
impl<T: Eq + Hash + Immutable, U> Node<T, U> {
pub const fn new(id: T, data: U, depends_on: Set<T>) -> Self {
Node {
id,
@ -93,12 +94,12 @@ fn _reorder_by_idx<T>(mut v: Vec<T>, idx: Vec<usize>) -> Vec<T> {
v
}
fn reorder_by_key<T: Eq + Hash, U>(mut g: Graph<T, U>, idx: Vec<T>) -> Graph<T, U> {
fn reorder_by_key<T: Eq + Hash + Immutable, U>(mut g: Graph<T, U>, idx: Vec<T>) -> Graph<T, U> {
g.sort_by_key(|node| idx.iter().position(|k| k == &node.id).unwrap());
g
}
fn dfs<T: Eq + Hash + Clone + Debug, U: Debug>(
fn dfs<T: Eq + Hash + Clone + Debug + Immutable, U: Debug>(
g: &Graph<T, U>,
v: T,
used: &mut Set<T>,
@ -125,7 +126,7 @@ fn dfs<T: Eq + Hash + Clone + Debug, U: Debug>(
/// perform topological sort on a graph
#[allow(clippy::result_unit_err)]
pub fn tsort<T: Eq + Hash + Clone + Debug, U: Debug>(
pub fn tsort<T: Eq + Hash + Clone + Debug + Immutable, U: Debug>(
g: Graph<T, U>,
) -> Result<Graph<T, U>, TopoSortError> {
let n = g.len();

View file

@ -7,7 +7,7 @@ use erg_common::dict::Dict;
use erg_common::set::Set;
use erg_common::style::colors::DEBUG_ERROR;
use erg_common::traits::StructuralEq;
use erg_common::{assume_unreachable, log, set_recursion_limit};
use erg_common::{assume_unreachable, log};
use erg_common::{Str, Triple};
use crate::context::eval::UndoableLinkedList;
@ -126,7 +126,6 @@ impl Context {
/// lhs :> rhs ?
pub(crate) fn supertype_of(&self, lhs: &Type, rhs: &Type) -> bool {
set_recursion_limit!(false, 128);
let res = match Self::cheap_supertype_of(lhs, rhs) {
(Absolutely, judge) => judge,
(Maybe, judge) => {
@ -1040,7 +1039,7 @@ impl Context {
}
for (sub_k, sub_v) in sub_d.iter() {
if let Some(sup_v) = sup_d
.get(sub_k)
.linear_get(sub_k)
.or_else(|| sub_tpdict_get(sup_d, sub_k, self))
{
if !self.supertype_of_tp(sup_v, sub_v, variance) {
@ -1794,7 +1793,7 @@ impl Context {
if self.subtype_of(&t, elem) {
return intersection.clone();
} else if self.supertype_of(&t, elem) {
return constructors::ands(ands.exclude(&t).include(elem.clone()));
return constructors::ands(ands.linear_exclude(&t).include(elem.clone()));
}
}
and(intersection.clone(), elem.clone())
@ -2024,7 +2023,7 @@ impl Context {
"or" => self.is_sub_pred_of(existing, pred),
_ => unreachable!(),
}) {
reduced.remove(old);
reduced.linear_remove(old);
}
// insert if necessary
if reduced.iter().all(|existing| match mode {

View file

@ -9,7 +9,7 @@ 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, set_recursion_limit, Triple};
use erg_common::{dict, fmt_vec, fn_name, option_enum_unwrap, set, Triple};
use erg_common::{ArcArray, Str};
use OpKind::*;
@ -2068,7 +2068,6 @@ impl Context {
level: usize,
t_loc: &impl Locational,
) -> Failable<Type> {
set_recursion_limit!(Ok(Failure), 128);
let mut errs = EvalErrors::empty();
match substituted {
Type::FreeVar(fv) if fv.is_linked() => {
@ -3943,8 +3942,8 @@ impl Context {
(TyParam::Erased(l), TyParam::Erased(r)) => l == r,
(TyParam::List(l), TyParam::List(r)) => l == r,
(TyParam::Tuple(l), TyParam::Tuple(r)) => l == r,
(TyParam::Set(l), TyParam::Set(r)) => l == r, // FIXME:
(TyParam::Dict(l), TyParam::Dict(r)) => l == r,
(TyParam::Set(l), TyParam::Set(r)) => l.linear_eq(r),
(TyParam::Dict(l), TyParam::Dict(r)) => l.linear_eq(r),
(TyParam::Lambda(l), TyParam::Lambda(r)) => l == r,
(TyParam::FreeVar { .. }, TyParam::FreeVar { .. }) => true,
(TyParam::Mono(l), TyParam::Mono(r)) => {

View file

@ -100,19 +100,19 @@ impl Generalizer {
let nd_params = lambda
.nd_params
.into_iter()
.map(|pt| pt.map_type(|t| self.generalize_t(t, uninit)))
.map(|pt| pt.map_type(&mut |t| self.generalize_t(t, uninit)))
.collect::<Vec<_>>();
let var_params = lambda
.var_params
.map(|pt| pt.map_type(|t| self.generalize_t(t, uninit)));
.map(|pt| pt.map_type(&mut |t| self.generalize_t(t, uninit)));
let d_params = lambda
.d_params
.into_iter()
.map(|pt| pt.map_type(|t| self.generalize_t(t, uninit)))
.map(|pt| pt.map_type(&mut |t| self.generalize_t(t, uninit)))
.collect::<Vec<_>>();
let kw_var_params = lambda
.kw_var_params
.map(|pt| pt.map_type(|t| self.generalize_t(t, uninit)));
.map(|pt| pt.map_type(&mut |t| self.generalize_t(t, uninit)));
let body = lambda
.body
.into_iter()

View file

@ -342,7 +342,10 @@ pub(crate) fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueR
let index = args
.remove_left_or_key("Index")
.ok_or_else(|| not_passed("Index"))?;
if let Some(v) = slf.get(&index).or_else(|| sub_vdict_get(&slf, &index, ctx)) {
if let Some(v) = slf
.linear_get(&index)
.or_else(|| sub_vdict_get(&slf, &index, ctx))
{
Ok(v.clone().into())
} else if let Some(v) = sub_vdict_get(&homogenize_dict(&slf, ctx), &index, ctx).cloned() {
Ok(v.into())

View file

@ -1235,11 +1235,11 @@ impl Context {
let non_default_params = subr_t
.non_default_params
.iter()
.map(|pt| pt.clone().map_type(|t| self.readable_type(t)));
.map(|pt| pt.clone().map_type(&mut |t| self.readable_type(t)));
let default_params = subr_t
.default_params
.iter()
.map(|pt| pt.clone().map_type(|t| self.readable_type(t)));
.map(|pt| pt.clone().map_type(&mut |t| self.readable_type(t)));
Err(TyCheckError::overload_error(
self.cfg.input.clone(),
line!() as usize,

View file

@ -596,7 +596,7 @@ impl Context {
}
if let Some(var_params) = &mut params.var_params {
if let Some(pt) = &subr_t.var_params {
let pt = pt.clone().map_type(unknown_len_list_t);
let pt = pt.clone().map_type(&mut unknown_len_list_t);
if let Err(es) =
self.assign_param(var_params, Some(&pt), tmp_tv_cache, ParamKind::VarParams)
{
@ -620,7 +620,7 @@ impl Context {
}
if let Some(kw_var_params) = &mut params.kw_var_params {
if let Some(pt) = &subr_t.var_params {
let pt = pt.clone().map_type(str_dict_t);
let pt = pt.clone().map_type(&mut str_dict_t);
if let Err(es) = self.assign_param(
kw_var_params,
Some(&pt),

View file

@ -302,7 +302,7 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
return Ok(());
}
for (sub_k, sub_v) in sub.iter() {
if let Some(sup_v) = sup.get(sub_k) {
if let Some(sup_v) = sup.linear_get(sub_k) {
self.sub_unify_value(sub_v, sup_v)?;
} else {
log!(err "{sup} does not have key {sub_k}");
@ -628,7 +628,7 @@ impl<'c, 'l, 'u, L: Locational> Unifier<'c, 'l, 'u, L> {
(TyParam::Dict(sub), TyParam::Dict(sup)) => {
for (sub_k, sub_v) in sub.iter() {
if let Some(sup_v) = sup
.get(sub_k)
.linear_get(sub_k)
.or_else(|| sub_tpdict_get(sup, sub_k, self.ctx))
{
// self.sub_unify_tp(sub_k, sup_k, _variance, loc, allow_divergence)?;

View file

@ -2098,7 +2098,8 @@ impl Params {
pub type Decorator = Expr;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
#[derive(Debug, Clone, Hash)]
pub struct SubrSignature {
pub decorators: HashSet<Decorator>,
pub ident: Identifier,
@ -2108,6 +2109,19 @@ pub struct SubrSignature {
pub captured_names: Vec<Identifier>,
}
impl PartialEq for SubrSignature {
fn eq(&self, other: &Self) -> bool {
self.ident == other.ident
&& self.bounds == other.bounds
&& self.params == other.params
&& self.return_t_spec == other.return_t_spec
&& self.captured_names == other.captured_names
&& self.decorators.linear_eq(&other.decorators)
}
}
impl Eq for SubrSignature {}
impl NestedDisplay for SubrSignature {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
write!(

View file

@ -874,7 +874,7 @@ impl<A: ASTBuildable> GenericASTLowerer<A> {
};
if let Some(popped_val_t) = union.insert(key.t(), value.t()) {
if PYTHON_MODE {
if let Some(val_t) = union.get_mut(key.ref_t()) {
if let Some(val_t) = union.linear_get_mut(key.ref_t()) {
*val_t = self.module.context.union(&mem::take(val_t), &popped_val_t);
}
} else {

View file

@ -443,6 +443,6 @@ impl SharedGeneralizationCache {
}
pub fn get(&self, key: &FreeTyVar) -> Option<GeneralizationResult> {
self.0.borrow().get(key).cloned()
self.0.borrow().linear_get(key).cloned()
}
}

View file

@ -140,7 +140,7 @@ impl Deserializer {
}
fn get_cached_arr(&mut self, arr: &[ValueObj]) -> ValueObj {
ValueObj::List(self.arr_cache.get(arr))
ValueObj::List(self.arr_cache.linear_get(arr))
}
pub fn vec_to_bytes<const LEN: usize>(vector: Vec<u8>) -> [u8; LEN] {

View file

@ -781,12 +781,12 @@ impl Free<Type> {
}
/// interior-mut
pub fn do_avoiding_recursion<O, F: FnOnce() -> O>(&self, f: F) -> O {
pub fn do_avoiding_recursion<O>(&self, f: impl FnOnce() -> O) -> O {
self._do_avoiding_recursion(None, f)
}
/// interior-mut
pub fn do_avoiding_recursion_with<O, F: FnOnce() -> O>(&self, placeholder: &Type, f: F) -> O {
pub fn do_avoiding_recursion_with<O>(&self, placeholder: &Type, f: impl FnOnce() -> O) -> O {
self._do_avoiding_recursion(Some(placeholder), f)
}
@ -868,8 +868,10 @@ impl<T: StructuralEq + CanbeFree + Clone + Default + fmt::Debug + Send + Sync +
fn structural_eq(&self, other: &Self) -> bool {
if let (Some((l, r)), Some((l2, r2))) = (self.get_subsup(), other.get_subsup()) {
self.dummy_link();
other.dummy_link();
let res = l.structural_eq(&l2) && r.structural_eq(&r2);
self.undo();
other.undo();
res
} else if let (Some(l), Some(r)) = (self.get_type(), other.get_type()) {
l.structural_eq(&r)

View file

@ -288,7 +288,7 @@ impl ParamTy {
}
}
pub fn map_type(self, f: impl FnOnce(Type) -> Type) -> Self {
pub fn map_type(self, f: &mut impl FnMut(Type) -> Type) -> Self {
match self {
Self::Pos(ty) => Self::Pos(f(ty)),
Self::Kw { name, ty } => Self::Kw { name, ty: f(ty) },
@ -300,7 +300,7 @@ impl ParamTy {
}
}
pub fn map_default_type(self, f: impl FnOnce(Type) -> Type) -> Self {
pub fn map_default_type(self, f: &mut impl FnMut(Type) -> Type) -> Self {
match self {
Self::KwWithDefault { name, ty, default } => Self::KwWithDefault {
name,
@ -581,7 +581,7 @@ impl SubrType {
|| self.return_t.contains_tp(target)
}
pub fn map(self, f: impl Fn(Type) -> Type + Copy) -> Self {
pub fn map(self, f: &mut impl FnMut(Type) -> Type) -> Self {
Self::new(
self.kind,
self.non_default_params
@ -859,25 +859,25 @@ impl SubrType {
let non_default_params = self
.non_default_params
.iter()
.map(|pt| pt.clone().map_type(|t| t.derefine()))
.map(|pt| pt.clone().map_type(&mut |t| t.derefine()))
.collect();
let var_params = self
.var_params
.as_ref()
.map(|pt| pt.clone().map_type(|t| t.derefine()));
.map(|pt| pt.clone().map_type(&mut |t| t.derefine()));
let default_params = self
.default_params
.iter()
.map(|pt| {
pt.clone()
.map_type(|t| t.derefine())
.map_default_type(|t| t.derefine())
.map_type(&mut |t| t.derefine())
.map_default_type(&mut |t| t.derefine())
})
.collect();
let kw_var_params = self
.kw_var_params
.as_ref()
.map(|pt| pt.clone().map_type(|t| t.derefine()));
.map(|pt| pt.clone().map_type(&mut |t| t.derefine()));
Self::new(
self.kind,
non_default_params,
@ -1016,13 +1016,34 @@ impl SubrType {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Hash)]
pub enum RefineKind {
Interval { min: TyParam, max: TyParam }, // e.g. {I: Int | I >= 2; I <= 10} 2..10
Enum(Set<TyParam>), // e.g. {I: Int | I == 1 or I == 2} {1, 2}
Complex,
}
impl PartialEq for RefineKind {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::Interval {
min: lmin,
max: lmax,
},
Self::Interval {
min: rmin,
max: rmax,
},
) => lmin == rmin && lmax == rmax,
(Self::Enum(lset), Self::Enum(rset)) => lset.linear_eq(rset),
(Self::Complex, Self::Complex) => true,
_ => false,
}
}
}
impl Eq for RefineKind {}
/// e.g.
/// ```erg
/// {I: Int | I >= 0}
@ -1483,8 +1504,8 @@ impl PartialEq for Type {
(Self::NamedTuple(lhs), Self::NamedTuple(rhs)) => lhs == rhs,
(Self::Refinement(l), Self::Refinement(r)) => l == r,
(Self::Quantified(l), Self::Quantified(r)) => l == r,
(Self::And(_, _), Self::And(_, _)) => self.ands() == other.ands(),
(Self::Or(_, _), Self::Or(_, _)) => self.ors() == other.ors(),
(Self::And(_, _), Self::And(_, _)) => self.ands().linear_eq(&other.ands()),
(Self::Or(_, _), Self::Or(_, _)) => self.ors().linear_eq(&other.ors()),
(Self::Not(l), Self::Not(r)) => l == r,
(
Self::Poly {
@ -4160,7 +4181,7 @@ impl Type {
}
Self::NamedTuple(r)
}
Self::Subr(subr) => Self::Subr(subr.map(|t| t.eliminate_recursion(target))),
Self::Subr(subr) => Self::Subr(subr.map(&mut |t| t.eliminate_recursion(target))),
Self::Callable { param_ts, return_t } => {
let param_ts = param_ts
.into_iter()
@ -4228,20 +4249,20 @@ impl Type {
.iter()
.map(|pt| {
pt.clone()
.map_type(|t| t.replace(&Self::Failure, &Self::Obj))
.map_type(&mut |t| t.replace(&Self::Failure, &Self::Obj))
})
.collect();
let var_params = subr.var_params.as_ref().map(|pt| {
pt.clone()
.map_type(|t| t.replace(&Self::Failure, &Self::Obj))
.map_type(&mut |t| t.replace(&Self::Failure, &Self::Obj))
});
let default_params = subr
.default_params
.iter()
.map(|pt| {
pt.clone()
.map_type(|t| t.replace(&Self::Failure, &Self::Obj))
.map_default_type(|t| {
.map_type(&mut |t| t.replace(&Self::Failure, &Self::Obj))
.map_default_type(&mut |t| {
let typ = pt.typ().clone().replace(&Self::Failure, &Self::Obj);
t.replace(&Self::Failure, &typ) & typ
})
@ -4249,8 +4270,8 @@ impl Type {
.collect();
let kw_var_params = subr.kw_var_params.as_ref().map(|pt| {
pt.clone()
.map_type(|t| t.replace(&Self::Failure, &Self::Obj))
.map_default_type(|t| {
.map_type(&mut |t| t.replace(&Self::Failure, &Self::Obj))
.map_default_type(&mut |t| {
let typ = pt.typ().clone().replace(&Self::Failure, &Self::Obj);
t.replace(&Self::Failure, &typ) & typ
})
@ -4285,97 +4306,91 @@ impl Type {
}
}
/// Unlike `replace`, this does not make a look-up table.
fn _replace(mut self, target: &Type, to: &Type) -> Type {
if self.structural_eq(target) {
self = to.clone();
}
fn map(self, f: &mut impl FnMut(Type) -> Type) -> Type {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked()._replace(target, to),
Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked().map(f),
Self::FreeVar(fv) => {
let fv_clone = fv.deep_clone();
if let Some((sub, sup)) = fv_clone.get_subsup() {
fv.dummy_link();
fv_clone.dummy_link();
let sub = sub._replace(target, to);
let sup = sup._replace(target, to);
let sub = sub.map(f);
let sup = sup.map(f);
fv.undo();
fv_clone.undo();
fv_clone.update_constraint(Constraint::new_sandwiched(sub, sup), true);
} else if let Some(ty) = fv_clone.get_type() {
fv_clone
.update_constraint(Constraint::new_type_of(ty._replace(target, to)), true);
fv_clone.update_constraint(Constraint::new_type_of(ty.map(f)), true);
}
Self::FreeVar(fv_clone)
}
Self::Refinement(mut refine) => {
refine.t = Box::new(refine.t._replace(target, to));
refine.pred = Box::new(refine.pred._replace_t(target, to));
refine.t = Box::new(refine.t.map(f));
refine.pred = Box::new(refine.pred.map_t(f));
Self::Refinement(refine)
}
Self::Record(mut rec) => {
for v in rec.values_mut() {
*v = std::mem::take(v)._replace(target, to);
*v = std::mem::take(v).map(f);
}
Self::Record(rec)
}
Self::NamedTuple(mut r) => {
for (_, v) in r.iter_mut() {
*v = std::mem::take(v)._replace(target, to);
*v = std::mem::take(v).map(f);
}
Self::NamedTuple(r)
}
Self::Subr(subr) => Self::Subr(subr._replace(target, to)),
Self::Subr(subr) => Self::Subr(subr.map(f)),
Self::Callable { param_ts, return_t } => {
let param_ts = param_ts
.into_iter()
.map(|t| t._replace(target, to))
.collect();
let return_t = Box::new(return_t._replace(target, to));
let param_ts = param_ts.into_iter().map(|t| t.map(f)).collect();
let return_t = Box::new(return_t.map(f));
Self::Callable { param_ts, return_t }
}
Self::Quantified(quant) => quant._replace(target, to).quantify(),
Self::Quantified(quant) => quant.map(f).quantify(),
Self::Poly { name, params } => {
let params = params
.into_iter()
.map(|tp| tp.replace_t(target, to))
.collect();
let params = params.into_iter().map(|tp| tp.map_t(f)).collect();
Self::Poly { name, params }
}
Self::Ref(t) => Self::Ref(Box::new(t._replace(target, to))),
Self::Ref(t) => Self::Ref(Box::new(t.map(f))),
Self::RefMut { before, after } => Self::RefMut {
before: Box::new(before._replace(target, to)),
after: after.map(|t| Box::new(t._replace(target, to))),
before: Box::new(before.map(f)),
after: after.map(|t| Box::new(t.map(f))),
},
Self::And(l, r) => l._replace(target, to) & r._replace(target, to),
Self::Or(l, r) => l._replace(target, to) | r._replace(target, to),
Self::Not(ty) => !ty._replace(target, to),
Self::Proj { lhs, rhs } => lhs._replace(target, to).proj(rhs),
Self::And(l, r) => l.map(f) & r.map(f),
Self::Or(l, r) => l.map(f) | r.map(f),
Self::Not(ty) => !ty.map(f),
Self::Proj { lhs, rhs } => lhs.map(f).proj(rhs),
Self::ProjCall {
lhs,
attr_name,
args,
} => {
let args = args
.into_iter()
.map(|tp| tp.replace_t(target, to))
.collect();
proj_call(lhs.replace_t(target, to), attr_name, args)
let args = args.into_iter().map(|tp| tp.map_t(f)).collect();
proj_call(lhs.map_t(f), attr_name, args)
}
Self::Structural(ty) => ty._replace(target, to).structuralize(),
Self::Structural(ty) => ty.map(f).structuralize(),
Self::Guard(guard) => Self::Guard(GuardType::new(
guard.namespace,
guard.target.clone(),
guard.to._replace(target, to),
guard.to.map(f),
)),
Self::Bounded { sub, sup } => Self::Bounded {
sub: Box::new(sub._replace(target, to)),
sup: Box::new(sup._replace(target, to)),
sub: Box::new(sub.map(f)),
sup: Box::new(sup.map(f)),
},
mono_type_pattern!() => self,
}
}
/// Unlike `replace`, this does not make a look-up table.
fn _replace(mut self, target: &Type, to: &Type) -> Type {
if self.structural_eq(target) {
self = to.clone();
}
self.map(&mut |t| t._replace(target, to))
}
fn _replace_tp(self, target: &TyParam, to: &TyParam) -> Type {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked()._replace_tp(target, to),

View file

@ -298,7 +298,7 @@ impl PartialEq for TyParam {
(Self::List(l), Self::List(r)) => l == r,
(Self::UnsizedList(l), Self::UnsizedList(r)) => l == r,
(Self::Tuple(l), Self::Tuple(r)) => l == r,
(Self::Dict(l), Self::Dict(r)) => l == r,
(Self::Dict(l), Self::Dict(r)) => l.linear_eq(r),
(Self::Record(l), Self::Record(r)) => l == r,
(
Self::DataClass {
@ -310,7 +310,7 @@ impl PartialEq for TyParam {
fields: rfs,
},
) => ln == rn && lfs == rfs,
(Self::Set(l), Self::Set(r)) => l == r,
(Self::Set(l), Self::Set(r)) => l.linear_eq(r),
(Self::Lambda(l), Self::Lambda(r)) => l == r,
(Self::Mono(l), Self::Mono(r)) => l == r,
(

View file

@ -641,7 +641,7 @@ impl Rem for Float {
/// 値オブジェクト
/// コンパイル時評価ができ、シリアライズも可能(Typeなどはシリアライズ不可)
#[derive(Clone, PartialEq, Default, Hash)]
#[derive(Clone, Default, Hash)]
pub enum ValueObj {
Int(i32),
Nat(u64),
@ -970,6 +970,44 @@ impl LimitedDisplay for ValueObj {
}
}
impl PartialEq for ValueObj {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Int(i1), Self::Int(i2)) => i1 == i2,
(Self::Nat(n1), Self::Nat(n2)) => n1 == n2,
(Self::Float(f1), Self::Float(f2)) => f1 == f2,
(Self::Str(s1), Self::Str(s2)) => s1 == s2,
(Self::Bool(b1), Self::Bool(b2)) => b1 == b2,
(Self::List(l1), Self::List(l2)) => l1 == l2,
(Self::UnsizedList(l1), Self::UnsizedList(l2)) => l1 == l2,
(Self::Set(s1), Self::Set(s2)) => s1.linear_eq(s2),
(Self::Dict(d1), Self::Dict(d2)) => d1.linear_eq(d2),
(Self::Tuple(t1), Self::Tuple(t2)) => t1 == t2,
(Self::Record(r1), Self::Record(r2)) => r1 == r2,
(
Self::DataClass {
name: n1,
fields: f1,
},
Self::DataClass {
name: n2,
fields: f2,
},
) => n1 == n2 && f1 == f2,
(Self::Code(c1), Self::Code(c2)) => c1 == c2,
(Self::Subr(s1), Self::Subr(s2)) => s1 == s2,
(Self::Type(t1), Self::Type(t2)) => t1 == t2,
(Self::None, Self::None)
| (Self::Ellipsis, Self::Ellipsis)
| (Self::NotImplemented, Self::NotImplemented)
| (Self::NegInf, Self::NegInf)
| (Self::Inf, Self::Inf)
| (Self::Failure, Self::Failure) => true,
_ => false,
}
}
}
impl Eq for ValueObj {}
impl Neg for ValueObj {

View file

@ -4,6 +4,7 @@ use std::fmt;
#[allow(unused_imports)]
use erg_common::log;
use erg_common::set::Set;
use erg_common::traits::Immutable;
use erg_common::{switch_lang, Str};
use erg_parser::ast::AccessModifier;
@ -170,6 +171,8 @@ pub struct Field {
pub symbol: Str,
}
impl Immutable for Field {}
impl PartialEq for Field {
fn eq(&self, other: &Self) -> bool {
self.symbol == other.symbol

View file

@ -4,6 +4,7 @@ use std::path::Path;
use erg_common::error::Location;
use erg_common::pathutil::NormalizedPathBuf;
use erg_common::set::Set;
use erg_common::traits::Immutable;
use erg_common::{switch_lang, Str};
use erg_parser::ast::DefId;
@ -141,6 +142,8 @@ pub struct AbsLocation {
pub loc: Location,
}
impl Immutable for AbsLocation {}
impl fmt::Display for AbsLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(module) = &self.module {

View file

@ -8,7 +8,7 @@ use erg_common::error::Location;
use erg_common::io::Input;
use erg_common::set::Set as HashSet;
// use erg_common::dict::Dict as HashMap;
use erg_common::traits::{Locational, NestedDisplay, Stream};
use erg_common::traits::{Immutable, Locational, NestedDisplay, Stream};
use erg_common::{
fmt_option, fmt_vec, impl_display_for_enum, impl_display_from_nested,
impl_displayable_stream_for_wrapper, impl_from_trait_for_enum, impl_locational,
@ -3806,6 +3806,8 @@ impl Locational for TypeBoundSpecs {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Decorator(pub Expr);
impl Immutable for Decorator {}
impl Decorator {
pub const fn new(expr: Expr) -> Self {
Self(expr)
@ -3825,6 +3827,8 @@ impl Decorator {
#[derive(Debug, Clone, Eq)]
pub struct VarName(Token);
impl Immutable for VarName {}
impl PartialEq for VarName {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0