diff --git a/crates/erg_common/error.rs b/crates/erg_common/error.rs index 9cd9e1b7..dbf62cc0 100644 --- a/crates/erg_common/error.rs +++ b/crates/erg_common/error.rs @@ -792,7 +792,7 @@ impl SubMessage { cxt.to_string() } Location::Unknown => match &e.input().kind { - InputKind::File(_) => "\n".to_string(), + InputKind::File { .. } => "\n".to_string(), _other => { let (_, vbar) = chars.gutters(); let mut cxt = StyledStrings::default(); diff --git a/crates/erg_common/io.rs b/crates/erg_common/io.rs index 9cfb02dd..f4999c67 100644 --- a/crates/erg_common/io.rs +++ b/crates/erg_common/io.rs @@ -13,6 +13,7 @@ use crate::env::{ use crate::pathutil::{add_postfix_foreach, remove_postfix}; use crate::random::random; use crate::stdin::GLOBAL_STDIN; +use crate::traits::Immutable; use crate::vfs::VFS; use crate::{normalize_path, power_assert}; @@ -59,7 +60,10 @@ impl DummyStdin { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum InputKind { - File(PathBuf), + File { + path: PathBuf, + project_root: Option, + }, REPL, // use Box to reduce the size DummyREPL(Box), @@ -77,7 +81,7 @@ impl InputKind { pub fn path(&self) -> &Path { match self { - Self::File(filename) => filename.as_path(), + Self::File { path, .. } => path.as_path(), Self::REPL | Self::Pipe(_) => Path::new(""), Self::DummyREPL(_stdin) => Path::new(""), Self::Str(_) => Path::new(""), @@ -87,7 +91,7 @@ impl InputKind { pub fn as_str(&self) -> &str { match self { - Self::File(filename) => filename.to_str().unwrap_or("_"), + Self::File { path, .. } => path.to_str().unwrap_or("_"), Self::REPL | Self::DummyREPL(_) | Self::Pipe(_) => "", Self::Str(_) => "", Self::Dummy => "", @@ -95,7 +99,7 @@ impl InputKind { } pub fn dir(&self) -> PathBuf { - if let Self::File(path) = self { + if let Self::File { path, .. } = self { let mut path = path.clone(); path.pop(); if path.ends_with("__pycache__") { @@ -111,17 +115,10 @@ impl InputKind { } } - pub fn project_root(&self) -> Option { - if let Self::File(path) = self { - let mut parent = path.clone(); - while parent.pop() { - if parent.join("package.er").exists() { - return Some(parent); - } - } - None - } else { - None + pub fn project_root(&self) -> Option<&PathBuf> { + match self { + Self::File { project_root, .. } => project_root.as_ref(), + _ => None, } } } @@ -147,13 +144,25 @@ impl From<&Path> for Input { } } +impl Immutable for Input {} + impl Input { pub const fn new(kind: InputKind, id: u64) -> Self { Self { kind, id } } pub fn file(path: PathBuf) -> Self { - Self::new(InputKind::File(path), random()) + fn project_root(path: &Path) -> Option { + let mut parent = path.to_path_buf(); + while parent.pop() { + if parent.join("package.er").exists() { + return Some(parent); + } + } + None + } + let project_root = project_root(&path); + Self::new(InputKind::File { path, project_root }, random()) } pub fn pipe(src: String) -> Self { @@ -188,7 +197,7 @@ impl Input { self.kind.dir() } - pub fn project_root(&self) -> Option { + pub fn project_root(&self) -> Option<&PathBuf> { self.kind.project_root() } @@ -218,7 +227,7 @@ impl Input { pub fn file_stem(&self) -> String { match &self.kind { - InputKind::File(filename) => filename + InputKind::File { path, .. } => path .file_stem() .and_then(|f| f.to_str()) .unwrap_or("_") @@ -233,14 +242,14 @@ impl Input { pub fn full_path(&self) -> PathBuf { match &self.kind { - InputKind::File(filename) => filename.clone(), + InputKind::File { path, .. } => path.clone(), _ => PathBuf::from(self.file_stem()), } } pub fn filename(&self) -> String { match &self.kind { - InputKind::File(filename) => filename + InputKind::File { path, .. } => path .file_name() .and_then(|f| f.to_str()) .unwrap_or("_") @@ -257,13 +266,13 @@ impl Input { pub fn module_name(&self) -> String { match &self.kind { - InputKind::File(filename) => { - let file_stem = if filename.file_stem() == Some(OsStr::new("__init__")) - || filename.file_stem() == Some(OsStr::new("__init__.d")) + InputKind::File { path, .. } => { + let file_stem = if path.file_stem() == Some(OsStr::new("__init__")) + || path.file_stem() == Some(OsStr::new("__init__.d")) { - filename.parent().and_then(|p| p.file_stem()) + path.parent().and_then(|p| p.file_stem()) } else { - filename.file_stem() + path.file_stem() }; file_stem .and_then(|f| f.to_str()) @@ -280,13 +289,13 @@ impl Input { pub fn read(&mut self) -> String { match &mut self.kind { - InputKind::File(filename) => match VFS.read(filename.as_path()) { + InputKind::File { path, .. } => match VFS.read(path.as_path()) { Ok(s) => s, Err(e) => { let code = e.raw_os_error().unwrap_or(1); println!( "cannot read '{}': [Errno {code}] {e}", - filename.to_string_lossy() + path.to_string_lossy() ); process::exit(code); } @@ -300,7 +309,7 @@ impl Input { pub fn source_exists(&self) -> bool { match &self.kind { - InputKind::File(filename) => filename.exists(), + InputKind::File { path, .. } => path.exists(), InputKind::Dummy => false, _ => true, } @@ -308,7 +317,7 @@ impl Input { pub fn try_read(&mut self) -> std::io::Result { match &mut self.kind { - InputKind::File(filename) => VFS.read(filename), + InputKind::File { path, .. } => VFS.read(path), InputKind::Pipe(s) | InputKind::Str(s) => Ok(s.clone()), InputKind::REPL => Ok(GLOBAL_STDIN.read()), InputKind::DummyREPL(dummy) => Ok(dummy.read_line()), @@ -318,13 +327,13 @@ impl Input { pub fn read_non_dummy(&self) -> String { match &self.kind { - InputKind::File(filename) => match VFS.read(filename) { + InputKind::File { path, .. } => match VFS.read(path) { Ok(s) => s, Err(e) => { let code = e.raw_os_error().unwrap_or(1); println!( "cannot read '{}': [Errno {code}] {e}", - filename.to_string_lossy() + path.to_string_lossy() ); process::exit(code); } @@ -338,7 +347,7 @@ impl Input { pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec { power_assert!(ln_begin, >=, 1); match &self.kind { - InputKind::File(filename) => match VFS.read(filename) { + InputKind::File { path, .. } => match VFS.read(path) { Ok(code) => { let mut codes = vec![]; let mut lines = code.lines().map(ToString::to_string).skip(ln_begin - 1); @@ -368,7 +377,7 @@ impl Input { pub fn reread(&self) -> String { match &self.kind { - InputKind::File(path) => VFS.read(path).unwrap(), + InputKind::File { path, .. } => VFS.read(path).unwrap(), InputKind::Pipe(s) | InputKind::Str(s) => s.clone(), InputKind::REPL => GLOBAL_STDIN.reread().trim_end().to_owned(), InputKind::DummyREPL(dummy) => dummy.reread().unwrap_or_default(), @@ -410,12 +419,13 @@ impl Input { path: &Path, ) -> Result { if path == Path::new("") { - let path = dir + let result = dir .join("__init__.d.er") .canonicalize() .or_else(|_| dir.join("__pycache__").join("__init__.d.er").canonicalize()) .or_else(|_| dir.canonicalize())?; - return Ok(path); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(result.clone())); + return Ok(result); } let mut comps = path.components(); let last = comps @@ -425,7 +435,7 @@ impl Input { dir.push(comps); dir.push(last_path); dir.set_extension("d.er"); // {path/to}.d.er - let path = dir + let result = dir .canonicalize() .or_else(|_| { dir.pop(); // {path/to}.d.er -> {path} @@ -449,7 +459,9 @@ impl Input { dir.push("__init__.d.er"); // -> {path/to}/__pycache__/__init__.d.er dir.canonicalize() })?; - Ok(normalize_path(path)) + let result = normalize_path(result); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(result.clone())); + Ok(result) } fn resolve_local_py(&self, path: &Path) -> Result { @@ -466,21 +478,34 @@ impl Input { } pub fn resolve_py(&self, path: &Path) -> Result { - if let Ok(path) = self.resolve_local_py(path) { - return Ok(path); + if let Some(opt_path) = VFS.get_cached_path(self.clone(), path.to_path_buf()) { + return opt_path.ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("cannot find module `{}`", path.display()), + ) + }); + } + if let Ok(resolved) = self.resolve_local_py(path) { + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Ok(resolved); } for sys_path in python_sys_path() { let mut dir = sys_path.clone(); dir.push(path); dir.set_extension("py"); if dir.exists() { - return Ok(normalize_path(dir)); + let resolved = normalize_path(dir); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Ok(resolved); } dir.pop(); dir.push(path); dir.push("__init__.py"); if dir.exists() { - return Ok(normalize_path(dir)); + let resolved = normalize_path(dir); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Ok(resolved); } if !EXPERIMENTAL_MODE { break; @@ -491,13 +516,17 @@ impl Input { dir.push(path); dir.set_extension("py"); if dir.exists() { - return Ok(normalize_path(dir)); + let resolved = normalize_path(dir); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Ok(resolved); } dir.pop(); dir.push(path); dir.push("__init__.py"); if dir.exists() { - return Ok(normalize_path(dir)); + let resolved = normalize_path(dir); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Ok(resolved); } } Err(std::io::Error::new( @@ -518,21 +547,31 @@ impl Input { /// 4. `std/{path/to}/__init__.er` /// 5. `pkgs/{path/to}/src/lib.er` pub fn resolve_real_path(&self, path: &Path, cfg: &ErgConfig) -> Option { - if let Ok(path) = self.resolve_local(path) { - Some(path) - } else if let Ok(path) = erg_std_path() + if let Some(opt_path) = VFS.get_cached_path(self.clone(), path.to_path_buf()) { + return opt_path; + } + if let Ok(resolved) = self.resolve_local(path) { + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + Some(resolved) + } else if let Ok(resolved) = erg_std_path() .join(format!("{}.er", path.display())) .canonicalize() { - Some(normalize_path(path)) - } else if let Ok(path) = erg_std_path() + let resolved = normalize_path(resolved); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + Some(resolved) + } else if let Ok(resolved) = erg_std_path() .join(format!("{}", path.display())) .join("__init__.er") .canonicalize() { - Some(normalize_path(path)) + let resolved = normalize_path(resolved); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + Some(resolved) } else if let Some(pkg) = self.resolve_project_dep_path(path, cfg, false) { - Some(normalize_path(pkg)) + let resolved = normalize_path(pkg); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + Some(resolved) } else if path == Path::new("unsound") { Some(PathBuf::from("unsound")) } else { @@ -597,8 +636,12 @@ impl Input { /// 10. `site-packages/{path}/__pycache__/{to}.d.er` /// 11. `site-packages/{path/to}/__pycache__/__init__.d.er` pub fn resolve_decl_path(&self, path: &Path, cfg: &ErgConfig) -> Option { - if let Ok(path) = self.resolve_local_decl(self.dir(), path) { - return Some(path); + if let Some(opt_path) = VFS.get_cached_path(self.clone(), path.to_path_buf()) { + return opt_path; + } + if let Ok(resolved) = self.resolve_local_decl(self.dir(), path) { + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Some(resolved); } // e.g. // root: lib/external/pandas.d, path: pandas/core/frame @@ -609,28 +652,34 @@ impl Input { // -> NO if let Some((root, first)) = self.project_root().zip(path.components().next()) { if root.ends_with(first) || remove_postfix(root.clone(), ".d").ends_with(first) { - let path = path.iter().skip(1).collect::(); - if let Ok(path) = self.resolve_local_decl(root, &path) { - return Some(path); + let path_buf = path.iter().skip(1).collect::(); + if let Ok(resolved) = self.resolve_local_decl(root.clone(), &path_buf) { + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Some(resolved); } } } - if let Some(path) = Self::resolve_std_decl_path(erg_pystd_path(), path) { - return Some(path); + if let Some(resolved) = Self::resolve_std_decl_path(erg_pystd_path(), path) { + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Some(resolved); } if let Some(pkg) = self.resolve_project_dep_path(path, cfg, true) { - return Some(normalize_path(pkg)); + let resolved = normalize_path(pkg); + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Some(resolved); } for site_packages in python_site_packages() { - if let Some(path) = Self::resolve_site_pkgs_decl_path(site_packages, path) { - return Some(path); + if let Some(resolved) = Self::resolve_site_pkgs_decl_path(site_packages, path) { + VFS.cache_path(self.clone(), path.to_path_buf(), Some(resolved.clone())); + return Some(resolved); } } if PYTHON_MODE { - if let Ok(path) = self.resolve_py(path) { - return Some(path); + if let Ok(resolved) = self.resolve_py(path) { + return Some(resolved); } } + VFS.cache_path(self.clone(), path.to_path_buf(), None); None } diff --git a/crates/erg_common/traits.rs b/crates/erg_common/traits.rs index 4984c180..409118a3 100644 --- a/crates/erg_common/traits.rs +++ b/crates/erg_common/traits.rs @@ -762,7 +762,7 @@ pub trait Runnable: Sized + Default + New { let mut num_errors = 0; let mut instance = Self::new(cfg); let res = match &instance.input().kind { - InputKind::File(_) | InputKind::Pipe(_) | InputKind::Str(_) => instance.exec(), + InputKind::File { .. } | InputKind::Pipe(_) | InputKind::Str(_) => instance.exec(), InputKind::REPL | InputKind::DummyREPL(_) => { let output = stdout(); let mut output = BufWriter::new(output.lock()); diff --git a/crates/erg_common/vfs.rs b/crates/erg_common/vfs.rs index 0f69f5ab..923cc8e5 100644 --- a/crates/erg_common/vfs.rs +++ b/crates/erg_common/vfs.rs @@ -1,7 +1,8 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::OnceLock; use crate::dict::Dict; +use crate::io::Input; use crate::pathutil::NormalizedPathBuf; use crate::shared::Shared; @@ -10,12 +11,14 @@ use crate::shared::Shared; #[derive(Debug, Default)] pub struct VirtualFileSystem { cache: Shared>, + path_cache: Shared>>, } impl VirtualFileSystem { pub fn new() -> Self { Self { cache: Shared::new(Dict::new()), + path_cache: Shared::new(Dict::new()), } } @@ -24,6 +27,14 @@ impl VirtualFileSystem { self.cache.borrow_mut().insert(path, contents); } + pub fn cache_path(&self, input: Input, path: PathBuf, result: Option) { + self.path_cache.borrow_mut().insert((input, path), result); + } + + pub fn get_cached_path(&self, input: Input, path: PathBuf) -> Option> { + self.path_cache.borrow().get(&(input, path)).cloned() + } + pub fn remove(&self, path: impl AsRef) { let path = NormalizedPathBuf::from(path.as_ref()); self.cache.borrow_mut().remove(&path); @@ -69,6 +80,18 @@ impl SharedVFS { pub fn remove(&self, path: impl AsRef) { self.0.get_or_init(VirtualFileSystem::new).remove(path) } + + pub fn cache_path(&self, input: Input, path: PathBuf, result: Option) { + self.0 + .get_or_init(VirtualFileSystem::new) + .cache_path(input, path, result) + } + + pub fn get_cached_path(&self, input: Input, path: PathBuf) -> Option> { + self.0 + .get_or_init(VirtualFileSystem::new) + .get_cached_path(input, path) + } } pub static VFS: SharedVFS = SharedVFS(OnceLock::new()); diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index 9907a856..f0a59f26 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -177,6 +177,9 @@ impl<'c> Substituter<'c> { qt: &Type, st: &Type, ) -> EvalResult> { + if qt == st { + return Ok(None); + } let mut qtps = qt.typarams(); let mut stps = st.typarams(); // Or, And are commutative, choose fitting order @@ -251,6 +254,9 @@ impl<'c> Substituter<'c> { qt: &Type, st: &Type, ) -> EvalResult> { + if qt == st { + return Ok(None); + } let mut qtps = qt.typarams(); let mut stps = st.typarams(); if qt.qual_name() == st.qual_name() { diff --git a/crates/erg_compiler/context/test.rs b/crates/erg_compiler/context/test.rs index db662162..8ebe8275 100644 --- a/crates/erg_compiler/context/test.rs +++ b/crates/erg_compiler/context/test.rs @@ -93,7 +93,7 @@ impl Context { inst.lift(); let quantified_again = self.generalize_t(inst); println!("quantified_again: {quantified_again}"); - assert_eq!(quantified, quantified_again); + assert!(quantified.structural_eq(&quantified_again)); Ok(()) } diff --git a/crates/erg_compiler/ty/free.rs b/crates/erg_compiler/ty/free.rs index d868109b..322e3bd0 100644 --- a/crates/erg_compiler/ty/free.rs +++ b/crates/erg_compiler/ty/free.rs @@ -269,6 +269,7 @@ impl Constraint { pub trait CanbeFree { fn unbound_name(&self) -> Option; + fn unbound_id(&self) -> Option; fn constraint(&self) -> Option; fn destructive_update_constraint(&self, constraint: Constraint, in_instantiation: bool); } @@ -278,6 +279,10 @@ impl Free { self.borrow().unbound_name() } + pub fn unbound_id(&self) -> Option { + self.borrow().unbound_id() + } + pub fn constraint(&self) -> Option { self.borrow().constraint() } @@ -298,6 +303,7 @@ pub enum FreeKind { }, NamedUnbound { name: Str, + id: Id, lev: Level, constraint: Constraint, }, @@ -307,23 +313,8 @@ impl Hash for FreeKind { fn hash(&self, state: &mut H) { match self { Self::Linked(t) | Self::UndoableLinked { t, .. } => t.hash(state), - Self::Unbound { - id, - lev, - constraint, - } => { + Self::Unbound { id, .. } | Self::NamedUnbound { id, .. } => { id.hash(state); - lev.hash(state); - constraint.hash(state); - } - Self::NamedUnbound { - name, - lev, - constraint, - } => { - name.hash(state); - lev.hash(state); - constraint.hash(state); } } } @@ -336,30 +327,10 @@ impl PartialEq for FreeKind { Self::Linked(t1) | Self::UndoableLinked { t: t1, .. }, Self::Linked(t2) | Self::UndoableLinked { t: t2, .. }, ) => t1 == t2, - ( - Self::Unbound { - id: id1, - lev: lev1, - constraint: c1, - }, - Self::Unbound { - id: id2, - lev: lev2, - constraint: c2, - }, - ) => id1 == id2 && lev1 == lev2 && c1 == c2, - ( - Self::NamedUnbound { - name: n1, - lev: l1, - constraint: c1, - }, - Self::NamedUnbound { - name: n2, - lev: l2, - constraint: c2, - }, - ) => n1 == n2 && l1 == l2 && c1 == c2, + (Self::Unbound { id: id1, .. }, Self::Unbound { id: id2, .. }) + | (Self::NamedUnbound { id: id1, .. }, Self::NamedUnbound { id: id2, .. }) => { + id1 == id2 + } _ => false, } } @@ -374,6 +345,13 @@ impl FreeKind { } } + pub fn unbound_id(&self) -> Option { + match self { + FreeKind::NamedUnbound { id, .. } | FreeKind::Unbound { id, .. } => Some(*id), + FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => t.unbound_id(), + } + } + pub fn constraint(&self) -> Option { match self { FreeKind::Unbound { constraint, .. } | FreeKind::NamedUnbound { constraint, .. } => { @@ -407,6 +385,7 @@ impl LimitedDisplay for FreeKind { } Self::NamedUnbound { name, + id: _, lev, constraint, } => { @@ -473,9 +452,10 @@ impl FreeKind { } } - pub const fn named_unbound(name: Str, lev: Level, constraint: Constraint) -> Self { + pub const fn named_unbound(name: Str, id: usize, lev: Level, constraint: Constraint) -> Self { Self::NamedUnbound { name, + id, lev, constraint, } @@ -563,19 +543,8 @@ pub struct Free(Forkable>); impl Hash for Free { fn hash(&self, state: &mut H) { - if let Some(name) = self.unbound_name() { - name.hash(state); - } - if let Some(lev) = self.level() { - lev.hash(state); - } - if let Some((sub, sup)) = self.get_subsup() { - self.do_avoiding_recursion(|| { - sub.hash(state); - sup.hash(state); - }); - } else if let Some(t) = self.get_type() { - t.hash(state); + if let Some(id) = self.unbound_id() { + id.hash(state); } else if self.is_linked() { let cracked = self.crack(); if !Type::FreeVar(self.clone()).addr_eq(&cracked) { @@ -589,14 +558,8 @@ impl Hash for Free { impl Hash for Free { fn hash(&self, state: &mut H) { - if let Some(name) = self.unbound_name() { - name.hash(state); - } - if let Some(lev) = self.level() { - lev.hash(state); - } - if let Some(t) = self.get_type() { - t.hash(state); + if let Some(id) = self.unbound_id() { + id.hash(state); } else if self.is_linked() { self.crack().hash(state); } @@ -617,38 +580,9 @@ impl PartialEq for Free { } else { other }; - if let Some(self_name) = this.unbound_name() { - if let Some(other_name) = other.unbound_name() { - if self_name != other_name { - return false; - } - } else { - return false; - } - } - if let Some(self_lev) = this.level() { - if let Some(other_lev) = other.level() { - if self_lev != other_lev { - return false; - } - } else { - return false; - } - } - if let Some((sub, sup)) = this.get_subsup() { - if let Some((other_sub, other_sup)) = other.get_subsup() { - this.dummy_link(); - other.dummy_link(); - let res = sub == other_sub && sup == other_sup; - this.undo(); - other.undo(); - res - } else { - false - } - } else if let Some(self_t) = this.get_type() { - if let Some(other_t) = other.get_type() { - self_t == other_t + if let Some(self_id) = this.unbound_id() { + if let Some(other_id) = other.unbound_id() { + self_id == other_id } else { false } @@ -659,8 +593,7 @@ impl PartialEq for Free { false } } else { - // name, level, constraint are equal - true + false } } } @@ -679,27 +612,9 @@ impl PartialEq for Free { } else { other }; - if let Some(self_name) = this.unbound_name() { - if let Some(other_name) = other.unbound_name() { - if self_name != other_name { - return false; - } - } else { - return false; - } - } - if let Some(self_lev) = this.level() { - if let Some(other_lev) = other.level() { - if self_lev != other_lev { - return false; - } - } else { - return false; - } - } - if let Some(self_t) = this.get_type() { - if let Some(other_t) = other.get_type() { - self_t == other_t + if let Some(self_id) = this.unbound_id() { + if let Some(other_id) = other.unbound_id() { + self_id == other_id } else { false } @@ -710,8 +625,7 @@ impl PartialEq for Free { false } } else { - // name, level, constraint are equal - true + false } } } @@ -827,24 +741,26 @@ impl Free { }) { return; } - match &mut *self.borrow_mut() { - FreeKind::Unbound { - lev, constraint, .. - } - | FreeKind::NamedUnbound { - lev, constraint, .. - } => { - if !in_inst_or_gen && *lev == GENERIC_LEVEL { - log!(err "cannot update the constraint of a generalized type variable"); - return; + if let Some(linked) = self.get_linked() { + linked.destructive_update_constraint(new_constraint, in_inst_or_gen); + } else { + match &mut *self.borrow_mut() { + FreeKind::Unbound { + lev, constraint, .. } - if addr_eq!(*constraint, new_constraint) { - return; + | FreeKind::NamedUnbound { + lev, constraint, .. + } => { + if !in_inst_or_gen && *lev == GENERIC_LEVEL { + log!(err "cannot update the constraint of a generalized type variable"); + return; + } + if addr_eq!(*constraint, new_constraint) { + return; + } + *constraint = new_constraint; } - *constraint = new_constraint; - } - FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => { - t.destructive_update_constraint(new_constraint, in_inst_or_gen); + FreeKind::Linked(_) | FreeKind::UndoableLinked { .. } => unreachable!(), } } } @@ -1018,17 +934,17 @@ impl Free { } pub fn new_unbound(level: Level, constraint: Constraint) -> Self { - UNBOUND_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - Self(Forkable::new(FreeKind::unbound( - UNBOUND_ID.load(std::sync::atomic::Ordering::SeqCst), - level, - constraint, - ))) + let id = UNBOUND_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + Self(Forkable::new(FreeKind::unbound(id + 1, level, constraint))) } pub fn new_named_unbound(name: Str, level: Level, constraint: Constraint) -> Self { + let id = UNBOUND_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst); Self(Forkable::new(FreeKind::named_unbound( - name, level, constraint, + name, + id + 1, + level, + constraint, ))) } @@ -1163,6 +1079,7 @@ impl Free { } => (None, lev, constraint), FreeKind::NamedUnbound { name, + id: _, lev, constraint, } => (Some(name), lev, constraint), @@ -1310,24 +1227,26 @@ impl Free { if new_constraint.get_type() == Some(&Type::Never) { panic!("{new_constraint}"); } - match &mut *self.borrow_mut() { - FreeKind::Unbound { - lev, constraint, .. - } - | FreeKind::NamedUnbound { - lev, constraint, .. - } => { - if !in_inst_or_gen && *lev == GENERIC_LEVEL { - log!(err "cannot update the constraint of a generalized type variable"); - return; + if let Some(linked) = self.get_linked() { + linked.destructive_update_constraint(new_constraint, in_inst_or_gen); + } else { + match &mut *self.borrow_mut() { + FreeKind::Unbound { + lev, constraint, .. } - if addr_eq!(*constraint, new_constraint) { - return; + | FreeKind::NamedUnbound { + lev, constraint, .. + } => { + if !in_inst_or_gen && *lev == GENERIC_LEVEL { + log!(err "cannot update the constraint of a generalized type variable"); + return; + } + if addr_eq!(*constraint, new_constraint) { + return; + } + *constraint = new_constraint; } - *constraint = new_constraint; - } - FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => { - t.destructive_update_constraint(new_constraint, in_inst_or_gen); + FreeKind::Linked(_) | FreeKind::UndoableLinked { .. } => unreachable!(), } } } @@ -1362,6 +1281,7 @@ mod tests { let u = named_free_var("T".into(), 1, constraint); println!("{t} {u}"); assert_eq!(t, t); - assert_eq!(t, u); + assert_ne!(t, u); + assert!(t.structural_eq(&u)); } } diff --git a/crates/erg_compiler/ty/mod.rs b/crates/erg_compiler/ty/mod.rs index bc8afae4..68d70588 100644 --- a/crates/erg_compiler/ty/mod.rs +++ b/crates/erg_compiler/ty/mod.rs @@ -212,8 +212,8 @@ macro_rules! impl_t_for_enum { #[derive(Debug, Default)] pub struct SharedFrees { - tvs: Shared>, - tps: Shared>, + tvs: Shared>, + tps: Shared>, } impl SharedFrees { @@ -224,20 +224,20 @@ impl SharedFrees { } } - pub fn get_tv(&self, name: &str) -> Option { - self.tvs.borrow().get(name).cloned() + pub fn get_tv(&self, id: usize) -> Option { + self.tvs.borrow().get(&id).cloned() } - pub fn get_tp(&self, name: &str) -> Option { - self.tps.borrow().get(name).cloned() + pub fn get_tp(&self, id: usize) -> Option { + self.tps.borrow().get(&id).cloned() } - pub fn insert_tv(&self, name: Str, t: Type) { - self.tvs.borrow_mut().insert(name, t); + pub fn insert_tv(&self, id: usize, t: Type) { + self.tvs.borrow_mut().insert(id, t); } - pub fn insert_tp(&self, name: Str, tp: TyParam) { - self.tps.borrow_mut().insert(name, tp); + pub fn insert_tp(&self, id: usize, tp: TyParam) { + self.tps.borrow_mut().insert(id, tp); } } @@ -1783,6 +1783,14 @@ impl CanbeFree for Type { } } + fn unbound_id(&self) -> Option { + if let Some(fv) = self.as_free() { + fv.unbound_id() + } else { + None + } + } + fn constraint(&self) -> Option { if let Some(fv) = self.as_free() { fv.constraint() @@ -4431,19 +4439,6 @@ impl Type { } } - pub(crate) fn eliminate_and_or_recursion(self, target: &Type) -> Self { - match self { - Self::And(tys, idx) => Self::checked_and( - tys.into_iter().filter(|t| !t.addr_eq(target)).collect(), - idx, - ), - Self::Or(tys) => { - Self::checked_or(tys.into_iter().filter(|t| !t.addr_eq(target)).collect()) - } - _ => self, - } - } - pub fn replace(self, target: &Type, to: &Type) -> Type { let table = ReplaceTable::make(target, to); table.replace(self) @@ -4525,8 +4520,8 @@ impl Type { match self { Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked().map(f, tvs), Self::FreeVar(fv) => { - if let Some(name) = fv.unbound_name() { - if let Some(tv) = tvs.get_tv(&name) { + if let Some(id) = fv.unbound_id() { + if let Some(tv) = tvs.get_tv(id) { return tv; } } @@ -4539,8 +4534,8 @@ impl Type { let fv_clone = fv.deep_clone(); fv_clone .update_constraint(Constraint::new_sandwiched(new_sub, new_sup), true); - if let Some(name) = fv.unbound_name() { - tvs.insert_tv(name, Self::FreeVar(fv_clone.clone())); + if let Some(id) = fv.unbound_id() { + tvs.insert_tv(id, Self::FreeVar(fv_clone.clone())); } Self::FreeVar(fv_clone) } else { @@ -4551,8 +4546,8 @@ impl Type { if new_ty != ty { let fv_clone = fv.deep_clone(); fv_clone.update_constraint(Constraint::new_type_of(new_ty), true); - if let Some(name) = fv.unbound_name() { - tvs.insert_tv(name, Self::FreeVar(fv_clone.clone())); + if let Some(id) = fv.unbound_id() { + tvs.insert_tv(id, Self::FreeVar(fv_clone.clone())); } Self::FreeVar(fv_clone) } else { @@ -4638,8 +4633,8 @@ impl Type { match self { Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked()._replace_tp(target, to, tvs), Self::FreeVar(fv) => { - if let Some(name) = fv.unbound_name() { - if let Some(tv) = tvs.get_tv(&name) { + if let Some(id) = fv.unbound_id() { + if let Some(tv) = tvs.get_tv(id) { return tv; } } @@ -4652,8 +4647,8 @@ impl Type { let fv_clone = fv.deep_clone(); fv_clone .update_constraint(Constraint::new_sandwiched(new_sub, new_sup), true); - if let Some(name) = fv.unbound_name() { - tvs.insert_tv(name, Self::FreeVar(fv_clone.clone())); + if let Some(id) = fv.unbound_id() { + tvs.insert_tv(id, Self::FreeVar(fv_clone.clone())); } Self::FreeVar(fv_clone) } else { @@ -4664,8 +4659,8 @@ impl Type { if new_ty != ty { let fv_clone = fv.deep_clone(); fv_clone.update_constraint(Constraint::new_type_of(new_ty), true); - if let Some(name) = fv.unbound_name() { - tvs.insert_tv(name, Self::FreeVar(fv_clone.clone())); + if let Some(id) = fv.unbound_id() { + tvs.insert_tv(id, Self::FreeVar(fv_clone.clone())); } Self::FreeVar(fv_clone) } else { @@ -4756,8 +4751,8 @@ impl Type { match self { Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked().map_tp(f, tvs), Self::FreeVar(fv) => { - if let Some(name) = fv.unbound_name() { - if let Some(tv) = tvs.get_tv(&name) { + if let Some(id) = fv.unbound_id() { + if let Some(tv) = tvs.get_tv(id) { return tv; } } @@ -4770,8 +4765,8 @@ impl Type { let fv_clone = fv.deep_clone(); fv_clone .update_constraint(Constraint::new_sandwiched(new_sub, new_sup), true); - if let Some(name) = fv.unbound_name() { - tvs.insert_tv(name, Self::FreeVar(fv_clone.clone())); + if let Some(id) = fv.unbound_id() { + tvs.insert_tv(id, Self::FreeVar(fv_clone.clone())); } Self::FreeVar(fv_clone) } else { @@ -4782,8 +4777,8 @@ impl Type { if new_ty != ty { let fv_clone = fv.deep_clone(); fv_clone.update_constraint(Constraint::new_type_of(new_ty), true); - if let Some(name) = fv.unbound_name() { - tvs.insert_tv(name, Self::FreeVar(fv_clone.clone())); + if let Some(id) = fv.unbound_id() { + tvs.insert_tv(id, Self::FreeVar(fv_clone.clone())); } Self::FreeVar(fv_clone) } else { @@ -4862,8 +4857,8 @@ impl Type { match self { Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked().try_map_tp(f, tvs), Self::FreeVar(fv) => { - if let Some(name) = fv.unbound_name() { - if let Some(tv) = tvs.get_tv(&name) { + if let Some(id) = fv.unbound_id() { + if let Some(tv) = tvs.get_tv(id) { return Ok(tv); } } @@ -4876,8 +4871,8 @@ impl Type { let fv_clone = fv.deep_clone(); fv_clone .update_constraint(Constraint::new_sandwiched(new_sub, new_sup), true); - if let Some(name) = fv.unbound_name() { - tvs.insert_tv(name, Self::FreeVar(fv_clone.clone())); + if let Some(id) = fv.unbound_id() { + tvs.insert_tv(id, Self::FreeVar(fv_clone.clone())); } Ok(Self::FreeVar(fv_clone)) } else { @@ -4888,8 +4883,8 @@ impl Type { if new_ty != ty { let fv_clone = fv.deep_clone(); fv_clone.update_constraint(Constraint::new_type_of(new_ty), true); - if let Some(name) = fv.unbound_name() { - tvs.insert_tv(name, Self::FreeVar(fv_clone.clone())); + if let Some(id) = fv.unbound_id() { + tvs.insert_tv(id, Self::FreeVar(fv_clone.clone())); } Ok(Self::FreeVar(fv_clone)) } else { @@ -5215,11 +5210,7 @@ impl Type { } match self { Self::FreeVar(fv) => { - // NOTE: we can't use `eliminate_recursion` - let to_ = to - .clone() - .eliminate_subsup(self) - .eliminate_and_or_recursion(self); + let to_ = to.clone().eliminate_subsup(self).eliminate_recursion(self); if self.addr_eq(&to_) { self.inc_undo_count(); } else { diff --git a/crates/erg_compiler/ty/typaram.rs b/crates/erg_compiler/ty/typaram.rs index 1f94c3ba..ce4e8f54 100644 --- a/crates/erg_compiler/ty/typaram.rs +++ b/crates/erg_compiler/ty/typaram.rs @@ -547,6 +547,15 @@ impl CanbeFree for TyParam { } } + fn unbound_id(&self) -> Option { + match self { + TyParam::FreeVar(fv) => fv.unbound_id(), + TyParam::Type(t) => t.unbound_id(), + TyParam::Value(ValueObj::Type(ty)) => ty.typ().unbound_id(), + _ => None, + } + } + fn constraint(&self) -> Option { match self { TyParam::FreeVar(fv) => fv.constraint(), @@ -1567,8 +1576,8 @@ impl TyParam { match self { Self::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked().replace_t(target, to, tvs), Self::FreeVar(fv) if fv.get_type().is_some() => { - let name = fv.unbound_name().unwrap(); - if let Some(tp) = tvs.get_tp(&name) { + let id = fv.unbound_id().unwrap(); + if let Some(tp) = tvs.get_tp(id) { return tp; } let typ = fv.get_type().unwrap(); @@ -1576,7 +1585,7 @@ impl TyParam { if new_typ != typ { let fv_clone = fv.deep_clone(); fv_clone.update_type(new_typ); - tvs.insert_tp(name, Self::FreeVar(fv_clone.clone())); + tvs.insert_tp(id, Self::FreeVar(fv_clone.clone())); Self::FreeVar(fv_clone) } else { Self::FreeVar(fv) @@ -1926,8 +1935,8 @@ impl TyParam { match self { TyParam::FreeVar(fv) if fv.is_linked() => f(fv.unwrap_linked()), TyParam::FreeVar(fv) if fv.get_type().is_some() => { - if let Some(name) = fv.unbound_name() { - if let Some(tp) = tvs.get_tp(&name) { + if let Some(id) = fv.unbound_id() { + if let Some(tp) = tvs.get_tp(id) { return tp; } } @@ -1936,8 +1945,8 @@ impl TyParam { if typ != new_typ { let fv_clone = fv.deep_clone(); fv_clone.update_type(new_typ); - if let Some(name) = fv_clone.unbound_name() { - tvs.insert_tp(name, TyParam::FreeVar(fv_clone.clone())); + if let Some(id) = fv_clone.unbound_id() { + tvs.insert_tp(id, TyParam::FreeVar(fv_clone.clone())); } TyParam::FreeVar(fv_clone) } else { @@ -2001,8 +2010,8 @@ impl TyParam { match self { TyParam::FreeVar(fv) if fv.is_linked() => fv.unwrap_linked().map_t(f, tvs), TyParam::FreeVar(fv) if fv.get_type().is_some() => { - if let Some(name) = fv.unbound_name() { - if let Some(tp) = tvs.get_tp(&name) { + if let Some(id) = fv.unbound_id() { + if let Some(tp) = tvs.get_tp(id) { return tp; } } @@ -2011,8 +2020,8 @@ impl TyParam { if typ != new_typ { let fv_clone = fv.deep_clone(); fv_clone.update_type(new_typ); - if let Some(name) = fv_clone.unbound_name() { - tvs.insert_tp(name, TyParam::FreeVar(fv_clone.clone())); + if let Some(id) = fv_clone.unbound_id() { + tvs.insert_tp(id, TyParam::FreeVar(fv_clone.clone())); } TyParam::FreeVar(fv_clone) } else {