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" ntest = "nextest run"
# +nightly # +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" 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::consts::CASE_SENSITIVE;
use erg_common::normalize_path; 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}; use erg_compiler::erg_parser::token::{Token, TokenStream};
@ -18,6 +18,8 @@ use crate::server::ELSResult;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NormalizedUrl(Url); pub struct NormalizedUrl(Url);
impl Immutable for NormalizedUrl {}
impl fmt::Display for NormalizedUrl { impl fmt::Display for NormalizedUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0) write!(f, "{}", self.0)

View file

@ -5,6 +5,7 @@ use std::sync::Arc;
use crate::dict::Dict; use crate::dict::Dict;
use crate::set::Set; use crate::set::Set;
use crate::shared::Shared; use crate::shared::Shared;
use crate::traits::Immutable;
use crate::{ArcArray, Str}; use crate::{ArcArray, Str};
#[derive(Debug)] #[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]> { pub fn get(&self, q: &[T]) -> Arc<[T]> {
if let Some(cached) = self.0.borrow().get(q) { if let Some(cached) = self.0.borrow().get(q) {
return cached.clone(); 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> pub fn get<Q>(&self, q: &Q) -> Arc<T>
where where
Arc<T>: Borrow<Q>, 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)] #[derive(Debug, Clone)]
pub struct CacheDict<K, V: ?Sized>(Shared<Dict<K, Arc<V>>>); 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>> pub fn get<Q: ?Sized + Hash + Eq>(&self, k: &Q) -> Option<Arc<V>>
where where
K: Borrow<Q>, K: Borrow<Q>,
{ {
self.0.borrow().get(k).cloned() self.0.borrow().get(k).cloned()
} }
}
impl<K: Hash + Eq, V> CacheDict<K, V> {
pub fn insert(&self, k: K, v: V) { pub fn insert(&self, k: K, v: V) {
self.0.borrow_mut().insert(k, Arc::new(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::fxhash::FxHashMap;
use crate::get_hash; use crate::get_hash;
use crate::traits::Immutable;
#[macro_export] #[macro_export]
macro_rules! dict { macro_rules! dict {
@ -25,13 +26,13 @@ pub struct Dict<K, V> {
dict: FxHashMap<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 { fn eq(&self, other: &Dict<K, V>) -> bool {
self.dict == other.dict 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> { impl<K: Hash, V: Hash> Hash for Dict<K, V> {
fn hash<H: Hasher>(&self, state: &mut H) { 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 where
K: Borrow<Q>, K: Borrow<Q>,
Q: Hash + Eq, 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 where
K: Borrow<Q>, K: Borrow<Q>,
Q: Hash + Eq, Q: Hash + Eq,
@ -195,6 +196,15 @@ impl<K, V> Dict<K, V> {
{ {
self.dict.retain(f); 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> { 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] #[inline]
pub fn get<Q>(&self, k: &Q) -> Option<&V> pub fn get<Q>(&self, k: &Q) -> Option<&V>
where where
@ -225,15 +275,6 @@ impl<K: Hash + Eq, V> Dict<K, V> {
self.dict.get(k) 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] #[inline]
pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V> pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where where
@ -260,11 +301,6 @@ impl<K: Hash + Eq, V> Dict<K, V> {
self.dict.contains_key(k) self.dict.contains_key(k)
} }
#[inline]
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
self.dict.insert(k, v)
}
#[inline] #[inline]
pub fn remove<Q>(&mut self, k: &Q) -> Option<V> pub fn remove<Q>(&mut self, k: &Q) -> Option<V>
where where
@ -281,6 +317,13 @@ impl<K: Hash + Eq, V> Dict<K, V> {
{ {
self.dict.remove_entry(k) 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). /// 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. /// 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::consts::PYTHON_MODE;
use crate::env::erg_pkgs_path; use crate::env::erg_pkgs_path;
use crate::traits::Immutable;
use crate::{normalize_path, Str}; use crate::{normalize_path, Str};
/// Guaranteed equivalence path. /// Guaranteed equivalence path.
@ -16,6 +17,8 @@ use crate::{normalize_path, Str};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct NormalizedPathBuf(PathBuf); pub struct NormalizedPathBuf(PathBuf);
impl Immutable for NormalizedPathBuf {}
impl fmt::Display for NormalizedPathBuf { impl fmt::Display for NormalizedPathBuf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display()) write!(f, "{}", self.display())

View file

@ -5,6 +5,7 @@ use std::hash::{Hash, Hasher};
use std::iter::FromIterator; use std::iter::FromIterator;
use crate::fxhash::FxHashSet; use crate::fxhash::FxHashSet;
use crate::traits::Immutable;
use crate::{debug_fmt_iter, fmt_iter, get_hash}; use crate::{debug_fmt_iter, fmt_iter, get_hash};
#[cfg(feature = "pylib")] #[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 { fn eq(&self, other: &Set<T>) -> bool {
self.elems.eq(&other.elems) 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> { impl<T> Default for Set<T> {
fn default() -> Self { fn default() -> Self {
@ -108,9 +109,15 @@ impl<T> Set<T> {
elems: FxHashSet::default(), 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 { pub fn with_capacity(capacity: usize) -> Self {
Self { Self {
elems: FxHashSet::with_capacity_and_hasher(capacity, Default::default()), 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] #[inline]
pub fn get<Q>(&self, value: &Q) -> Option<&T> pub fn get<Q>(&self, value: &Q) -> Option<&T>
where where
@ -173,14 +223,6 @@ impl<T: Hash + Eq> Set<T> {
self.elems.get(value) 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] #[inline]
pub fn contains<Q>(&self, value: &Q) -> bool pub fn contains<Q>(&self, value: &Q) -> bool
where where
@ -190,12 +232,6 @@ impl<T: Hash + Eq> Set<T> {
self.elems.contains(value) self.elems.contains(value)
} }
/// newly inserted: true, already present: false
#[inline]
pub fn insert(&mut self, value: T) -> bool {
self.elems.insert(value)
}
#[inline] #[inline]
pub fn remove<Q>(&mut self, value: &Q) -> bool pub fn remove<Q>(&mut self, value: &Q) -> bool
where where
@ -205,6 +241,42 @@ impl<T: Hash + Eq> Set<T> {
self.elems.remove(value) 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] #[inline]
pub fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { pub fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
self.elems.extend(iter); self.elems.extend(iter);
@ -295,27 +367,6 @@ impl<T: Hash + Eq + Clone> Set<T> {
self.intersection(&iter.collect()) 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> { pub fn difference(&self, other: &Set<T>) -> Set<T> {
let u = self.elems.difference(&other.elems); let u = self.elems.difference(&other.elems);
Self { Self {
@ -331,11 +382,6 @@ impl<T: Hash + Eq + Clone> Set<T> {
self.insert(other); self.insert(other);
self self
} }
pub fn exclude(mut self, other: &T) -> Set<T> {
self.remove(other);
self
}
} }
impl<T: Hash + Ord> Set<T> { impl<T: Hash + Ord> Set<T> {

View file

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

View file

@ -7,7 +7,7 @@ use erg_common::dict::Dict;
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::style::colors::DEBUG_ERROR; use erg_common::style::colors::DEBUG_ERROR;
use erg_common::traits::StructuralEq; 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 erg_common::{Str, Triple};
use crate::context::eval::UndoableLinkedList; use crate::context::eval::UndoableLinkedList;
@ -126,7 +126,6 @@ impl Context {
/// lhs :> rhs ? /// lhs :> rhs ?
pub(crate) fn supertype_of(&self, lhs: &Type, rhs: &Type) -> bool { 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) { let res = match Self::cheap_supertype_of(lhs, rhs) {
(Absolutely, judge) => judge, (Absolutely, judge) => judge,
(Maybe, judge) => { (Maybe, judge) => {
@ -1040,7 +1039,7 @@ impl Context {
} }
for (sub_k, sub_v) in sub_d.iter() { for (sub_k, sub_v) in sub_d.iter() {
if let Some(sup_v) = sup_d if let Some(sup_v) = sup_d
.get(sub_k) .linear_get(sub_k)
.or_else(|| sub_tpdict_get(sup_d, sub_k, self)) .or_else(|| sub_tpdict_get(sup_d, sub_k, self))
{ {
if !self.supertype_of_tp(sup_v, sub_v, variance) { if !self.supertype_of_tp(sup_v, sub_v, variance) {
@ -1794,7 +1793,7 @@ impl Context {
if self.subtype_of(&t, elem) { if self.subtype_of(&t, elem) {
return intersection.clone(); return intersection.clone();
} else if self.supertype_of(&t, elem) { } 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()) and(intersection.clone(), elem.clone())
@ -2024,7 +2023,7 @@ impl Context {
"or" => self.is_sub_pred_of(existing, pred), "or" => self.is_sub_pred_of(existing, pred),
_ => unreachable!(), _ => unreachable!(),
}) { }) {
reduced.remove(old); reduced.linear_remove(old);
} }
// insert if necessary // insert if necessary
if reduced.iter().all(|existing| match mode { 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::set::Set;
use erg_common::shared::Shared; use erg_common::shared::Shared;
use erg_common::traits::{Locational, Stream}; 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 erg_common::{ArcArray, Str};
use OpKind::*; use OpKind::*;
@ -2068,7 +2068,6 @@ impl Context {
level: usize, level: usize,
t_loc: &impl Locational, t_loc: &impl Locational,
) -> Failable<Type> { ) -> Failable<Type> {
set_recursion_limit!(Ok(Failure), 128);
let mut errs = EvalErrors::empty(); let mut errs = EvalErrors::empty();
match substituted { match substituted {
Type::FreeVar(fv) if fv.is_linked() => { Type::FreeVar(fv) if fv.is_linked() => {
@ -3943,8 +3942,8 @@ impl Context {
(TyParam::Erased(l), TyParam::Erased(r)) => l == r, (TyParam::Erased(l), TyParam::Erased(r)) => l == r,
(TyParam::List(l), TyParam::List(r)) => l == r, (TyParam::List(l), TyParam::List(r)) => l == r,
(TyParam::Tuple(l), TyParam::Tuple(r)) => l == r, (TyParam::Tuple(l), TyParam::Tuple(r)) => l == r,
(TyParam::Set(l), TyParam::Set(r)) => l == r, // FIXME: (TyParam::Set(l), TyParam::Set(r)) => l.linear_eq(r),
(TyParam::Dict(l), TyParam::Dict(r)) => l == r, (TyParam::Dict(l), TyParam::Dict(r)) => l.linear_eq(r),
(TyParam::Lambda(l), TyParam::Lambda(r)) => l == r, (TyParam::Lambda(l), TyParam::Lambda(r)) => l == r,
(TyParam::FreeVar { .. }, TyParam::FreeVar { .. }) => true, (TyParam::FreeVar { .. }, TyParam::FreeVar { .. }) => true,
(TyParam::Mono(l), TyParam::Mono(r)) => { (TyParam::Mono(l), TyParam::Mono(r)) => {

View file

@ -100,19 +100,19 @@ impl Generalizer {
let nd_params = lambda let nd_params = lambda
.nd_params .nd_params
.into_iter() .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<_>>(); .collect::<Vec<_>>();
let var_params = lambda let var_params = lambda
.var_params .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 let d_params = lambda
.d_params .d_params
.into_iter() .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<_>>(); .collect::<Vec<_>>();
let kw_var_params = lambda let kw_var_params = lambda
.kw_var_params .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 let body = lambda
.body .body
.into_iter() .into_iter()

View file

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

View file

@ -1235,11 +1235,11 @@ impl Context {
let non_default_params = subr_t let non_default_params = subr_t
.non_default_params .non_default_params
.iter() .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 let default_params = subr_t
.default_params .default_params
.iter() .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( Err(TyCheckError::overload_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,

View file

@ -596,7 +596,7 @@ impl Context {
} }
if let Some(var_params) = &mut params.var_params { if let Some(var_params) = &mut params.var_params {
if let Some(pt) = &subr_t.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) = if let Err(es) =
self.assign_param(var_params, Some(&pt), tmp_tv_cache, ParamKind::VarParams) 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(kw_var_params) = &mut params.kw_var_params {
if let Some(pt) = &subr_t.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( if let Err(es) = self.assign_param(
kw_var_params, kw_var_params,
Some(&pt), Some(&pt),

View file

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

View file

@ -2098,7 +2098,8 @@ impl Params {
pub type Decorator = Expr; 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 struct SubrSignature {
pub decorators: HashSet<Decorator>, pub decorators: HashSet<Decorator>,
pub ident: Identifier, pub ident: Identifier,
@ -2108,6 +2109,19 @@ pub struct SubrSignature {
pub captured_names: Vec<Identifier>, 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 { impl NestedDisplay for SubrSignature {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
write!( 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 let Some(popped_val_t) = union.insert(key.t(), value.t()) {
if PYTHON_MODE { 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); *val_t = self.module.context.union(&mem::take(val_t), &popped_val_t);
} }
} else { } else {

View file

@ -443,6 +443,6 @@ impl SharedGeneralizationCache {
} }
pub fn get(&self, key: &FreeTyVar) -> Option<GeneralizationResult> { 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 { 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] { pub fn vec_to_bytes<const LEN: usize>(vector: Vec<u8>) -> [u8; LEN] {

View file

@ -781,12 +781,12 @@ impl Free<Type> {
} }
/// interior-mut /// 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) self._do_avoiding_recursion(None, f)
} }
/// interior-mut /// 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) 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 { fn structural_eq(&self, other: &Self) -> bool {
if let (Some((l, r)), Some((l2, r2))) = (self.get_subsup(), other.get_subsup()) { if let (Some((l, r)), Some((l2, r2))) = (self.get_subsup(), other.get_subsup()) {
self.dummy_link(); self.dummy_link();
other.dummy_link();
let res = l.structural_eq(&l2) && r.structural_eq(&r2); let res = l.structural_eq(&l2) && r.structural_eq(&r2);
self.undo(); self.undo();
other.undo();
res res
} else if let (Some(l), Some(r)) = (self.get_type(), other.get_type()) { } else if let (Some(l), Some(r)) = (self.get_type(), other.get_type()) {
l.structural_eq(&r) 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 { match self {
Self::Pos(ty) => Self::Pos(f(ty)), Self::Pos(ty) => Self::Pos(f(ty)),
Self::Kw { name, ty } => Self::Kw { name, ty: 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 { match self {
Self::KwWithDefault { name, ty, default } => Self::KwWithDefault { Self::KwWithDefault { name, ty, default } => Self::KwWithDefault {
name, name,
@ -581,7 +581,7 @@ impl SubrType {
|| self.return_t.contains_tp(target) || 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::new(
self.kind, self.kind,
self.non_default_params self.non_default_params
@ -859,25 +859,25 @@ impl SubrType {
let non_default_params = self let non_default_params = self
.non_default_params .non_default_params
.iter() .iter()
.map(|pt| pt.clone().map_type(|t| t.derefine())) .map(|pt| pt.clone().map_type(&mut |t| t.derefine()))
.collect(); .collect();
let var_params = self let var_params = self
.var_params .var_params
.as_ref() .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 let default_params = self
.default_params .default_params
.iter() .iter()
.map(|pt| { .map(|pt| {
pt.clone() pt.clone()
.map_type(|t| t.derefine()) .map_type(&mut |t| t.derefine())
.map_default_type(|t| t.derefine()) .map_default_type(&mut |t| t.derefine())
}) })
.collect(); .collect();
let kw_var_params = self let kw_var_params = self
.kw_var_params .kw_var_params
.as_ref() .as_ref()
.map(|pt| pt.clone().map_type(|t| t.derefine())); .map(|pt| pt.clone().map_type(&mut |t| t.derefine()));
Self::new( Self::new(
self.kind, self.kind,
non_default_params, non_default_params,
@ -1016,13 +1016,34 @@ impl SubrType {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Hash)]
pub enum RefineKind { pub enum RefineKind {
Interval { min: TyParam, max: TyParam }, // e.g. {I: Int | I >= 2; I <= 10} 2..10 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} Enum(Set<TyParam>), // e.g. {I: Int | I == 1 or I == 2} {1, 2}
Complex, 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. /// e.g.
/// ```erg /// ```erg
/// {I: Int | I >= 0} /// {I: Int | I >= 0}
@ -1483,8 +1504,8 @@ impl PartialEq for Type {
(Self::NamedTuple(lhs), Self::NamedTuple(rhs)) => lhs == rhs, (Self::NamedTuple(lhs), Self::NamedTuple(rhs)) => lhs == rhs,
(Self::Refinement(l), Self::Refinement(r)) => l == r, (Self::Refinement(l), Self::Refinement(r)) => l == r,
(Self::Quantified(l), Self::Quantified(r)) => l == r, (Self::Quantified(l), Self::Quantified(r)) => l == r,
(Self::And(_, _), Self::And(_, _)) => self.ands() == other.ands(), (Self::And(_, _), Self::And(_, _)) => self.ands().linear_eq(&other.ands()),
(Self::Or(_, _), Self::Or(_, _)) => self.ors() == other.ors(), (Self::Or(_, _), Self::Or(_, _)) => self.ors().linear_eq(&other.ors()),
(Self::Not(l), Self::Not(r)) => l == r, (Self::Not(l), Self::Not(r)) => l == r,
( (
Self::Poly { Self::Poly {
@ -4160,7 +4181,7 @@ impl Type {
} }
Self::NamedTuple(r) 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 } => { Self::Callable { param_ts, return_t } => {
let param_ts = param_ts let param_ts = param_ts
.into_iter() .into_iter()
@ -4228,20 +4249,20 @@ impl Type {
.iter() .iter()
.map(|pt| { .map(|pt| {
pt.clone() pt.clone()
.map_type(|t| t.replace(&Self::Failure, &Self::Obj)) .map_type(&mut |t| t.replace(&Self::Failure, &Self::Obj))
}) })
.collect(); .collect();
let var_params = subr.var_params.as_ref().map(|pt| { let var_params = subr.var_params.as_ref().map(|pt| {
pt.clone() 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 let default_params = subr
.default_params .default_params
.iter() .iter()
.map(|pt| { .map(|pt| {
pt.clone() pt.clone()
.map_type(|t| t.replace(&Self::Failure, &Self::Obj)) .map_type(&mut |t| t.replace(&Self::Failure, &Self::Obj))
.map_default_type(|t| { .map_default_type(&mut |t| {
let typ = pt.typ().clone().replace(&Self::Failure, &Self::Obj); let typ = pt.typ().clone().replace(&Self::Failure, &Self::Obj);
t.replace(&Self::Failure, &typ) & typ t.replace(&Self::Failure, &typ) & typ
}) })
@ -4249,8 +4270,8 @@ impl Type {
.collect(); .collect();
let kw_var_params = subr.kw_var_params.as_ref().map(|pt| { let kw_var_params = subr.kw_var_params.as_ref().map(|pt| {
pt.clone() pt.clone()
.map_type(|t| t.replace(&Self::Failure, &Self::Obj)) .map_type(&mut |t| t.replace(&Self::Failure, &Self::Obj))
.map_default_type(|t| { .map_default_type(&mut |t| {
let typ = pt.typ().clone().replace(&Self::Failure, &Self::Obj); let typ = pt.typ().clone().replace(&Self::Failure, &Self::Obj);
t.replace(&Self::Failure, &typ) & typ t.replace(&Self::Failure, &typ) & typ
}) })
@ -4285,97 +4306,91 @@ impl Type {
} }
} }
/// Unlike `replace`, this does not make a look-up table. fn map(self, f: &mut impl FnMut(Type) -> Type) -> Type {
fn _replace(mut self, target: &Type, to: &Type) -> Type {
if self.structural_eq(target) {
self = to.clone();
}
match self { 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) => { Self::FreeVar(fv) => {
let fv_clone = fv.deep_clone(); let fv_clone = fv.deep_clone();
if let Some((sub, sup)) = fv_clone.get_subsup() { if let Some((sub, sup)) = fv_clone.get_subsup() {
fv.dummy_link(); fv.dummy_link();
fv_clone.dummy_link(); fv_clone.dummy_link();
let sub = sub._replace(target, to); let sub = sub.map(f);
let sup = sup._replace(target, to); let sup = sup.map(f);
fv.undo(); fv.undo();
fv_clone.undo(); fv_clone.undo();
fv_clone.update_constraint(Constraint::new_sandwiched(sub, sup), true); fv_clone.update_constraint(Constraint::new_sandwiched(sub, sup), true);
} else if let Some(ty) = fv_clone.get_type() { } else if let Some(ty) = fv_clone.get_type() {
fv_clone fv_clone.update_constraint(Constraint::new_type_of(ty.map(f)), true);
.update_constraint(Constraint::new_type_of(ty._replace(target, to)), true);
} }
Self::FreeVar(fv_clone) Self::FreeVar(fv_clone)
} }
Self::Refinement(mut refine) => { Self::Refinement(mut refine) => {
refine.t = Box::new(refine.t._replace(target, to)); refine.t = Box::new(refine.t.map(f));
refine.pred = Box::new(refine.pred._replace_t(target, to)); refine.pred = Box::new(refine.pred.map_t(f));
Self::Refinement(refine) Self::Refinement(refine)
} }
Self::Record(mut rec) => { Self::Record(mut rec) => {
for v in rec.values_mut() { 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::Record(rec)
} }
Self::NamedTuple(mut r) => { Self::NamedTuple(mut r) => {
for (_, v) in r.iter_mut() { 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::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 } => { Self::Callable { param_ts, return_t } => {
let param_ts = param_ts let param_ts = param_ts.into_iter().map(|t| t.map(f)).collect();
.into_iter() let return_t = Box::new(return_t.map(f));
.map(|t| t._replace(target, to))
.collect();
let return_t = Box::new(return_t._replace(target, to));
Self::Callable { param_ts, return_t } 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 } => { Self::Poly { name, params } => {
let params = params let params = params.into_iter().map(|tp| tp.map_t(f)).collect();
.into_iter()
.map(|tp| tp.replace_t(target, to))
.collect();
Self::Poly { name, params } 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 { Self::RefMut { before, after } => Self::RefMut {
before: Box::new(before._replace(target, to)), before: Box::new(before.map(f)),
after: after.map(|t| Box::new(t._replace(target, to))), after: after.map(|t| Box::new(t.map(f))),
}, },
Self::And(l, r) => l._replace(target, to) & r._replace(target, to), Self::And(l, r) => l.map(f) & r.map(f),
Self::Or(l, r) => l._replace(target, to) | r._replace(target, to), Self::Or(l, r) => l.map(f) | r.map(f),
Self::Not(ty) => !ty._replace(target, to), Self::Not(ty) => !ty.map(f),
Self::Proj { lhs, rhs } => lhs._replace(target, to).proj(rhs), Self::Proj { lhs, rhs } => lhs.map(f).proj(rhs),
Self::ProjCall { Self::ProjCall {
lhs, lhs,
attr_name, attr_name,
args, args,
} => { } => {
let args = args let args = args.into_iter().map(|tp| tp.map_t(f)).collect();
.into_iter() proj_call(lhs.map_t(f), attr_name, args)
.map(|tp| tp.replace_t(target, to))
.collect();
proj_call(lhs.replace_t(target, to), 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( Self::Guard(guard) => Self::Guard(GuardType::new(
guard.namespace, guard.namespace,
guard.target.clone(), guard.target.clone(),
guard.to._replace(target, to), guard.to.map(f),
)), )),
Self::Bounded { sub, sup } => Self::Bounded { Self::Bounded { sub, sup } => Self::Bounded {
sub: Box::new(sub._replace(target, to)), sub: Box::new(sub.map(f)),
sup: Box::new(sup._replace(target, to)), sup: Box::new(sup.map(f)),
}, },
mono_type_pattern!() => self, 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 { fn _replace_tp(self, target: &TyParam, to: &TyParam) -> Type {
match self { match self {
Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked()._replace_tp(target, to), 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::List(l), Self::List(r)) => l == r,
(Self::UnsizedList(l), Self::UnsizedList(r)) => l == r, (Self::UnsizedList(l), Self::UnsizedList(r)) => l == r,
(Self::Tuple(l), Self::Tuple(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::Record(l), Self::Record(r)) => l == r,
( (
Self::DataClass { Self::DataClass {
@ -310,7 +310,7 @@ impl PartialEq for TyParam {
fields: rfs, fields: rfs,
}, },
) => ln == rn && lfs == 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::Lambda(l), Self::Lambda(r)) => l == r,
(Self::Mono(l), Self::Mono(r)) => l == r, (Self::Mono(l), Self::Mono(r)) => l == r,
( (

View file

@ -641,7 +641,7 @@ impl Rem for Float {
/// 値オブジェクト /// 値オブジェクト
/// コンパイル時評価ができ、シリアライズも可能(Typeなどはシリアライズ不可) /// コンパイル時評価ができ、シリアライズも可能(Typeなどはシリアライズ不可)
#[derive(Clone, PartialEq, Default, Hash)] #[derive(Clone, Default, Hash)]
pub enum ValueObj { pub enum ValueObj {
Int(i32), Int(i32),
Nat(u64), 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 Eq for ValueObj {}
impl Neg for ValueObj { impl Neg for ValueObj {

View file

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

View file

@ -4,6 +4,7 @@ use std::path::Path;
use erg_common::error::Location; use erg_common::error::Location;
use erg_common::pathutil::NormalizedPathBuf; use erg_common::pathutil::NormalizedPathBuf;
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::traits::Immutable;
use erg_common::{switch_lang, Str}; use erg_common::{switch_lang, Str};
use erg_parser::ast::DefId; use erg_parser::ast::DefId;
@ -141,6 +142,8 @@ pub struct AbsLocation {
pub loc: Location, pub loc: Location,
} }
impl Immutable for AbsLocation {}
impl fmt::Display for AbsLocation { impl fmt::Display for AbsLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(module) = &self.module { 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::io::Input;
use erg_common::set::Set as HashSet; use erg_common::set::Set as HashSet;
// use erg_common::dict::Dict as HashMap; // 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::{ use erg_common::{
fmt_option, fmt_vec, impl_display_for_enum, impl_display_from_nested, fmt_option, fmt_vec, impl_display_for_enum, impl_display_from_nested,
impl_displayable_stream_for_wrapper, impl_from_trait_for_enum, impl_locational, 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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Decorator(pub Expr); pub struct Decorator(pub Expr);
impl Immutable for Decorator {}
impl Decorator { impl Decorator {
pub const fn new(expr: Expr) -> Self { pub const fn new(expr: Expr) -> Self {
Self(expr) Self(expr)
@ -3825,6 +3827,8 @@ impl Decorator {
#[derive(Debug, Clone, Eq)] #[derive(Debug, Clone, Eq)]
pub struct VarName(Token); pub struct VarName(Token);
impl Immutable for VarName {}
impl PartialEq for VarName { impl PartialEq for VarName {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.0 == other.0 self.0 == other.0