Merge pull request #534 from erg-lang/perf-tyvar

Fix type-variable related bug
This commit is contained in:
Shunsuke Shibayama 2024-11-09 17:37:09 +09:00 committed by GitHub
commit fdbfaec19a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 286 additions and 288 deletions

View file

@ -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();

View file

@ -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<PathBuf>,
},
REPL,
// use Box to reduce the size
DummyREPL(Box<DummyStdin>),
@ -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("<stdin>"),
Self::DummyREPL(_stdin) => Path::new("<stdin>"),
Self::Str(_) => Path::new("<string>"),
@ -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(_) => "<stdin>",
Self::Str(_) => "<string>",
Self::Dummy => "<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<PathBuf> {
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<PathBuf> {
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<PathBuf> {
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<String> {
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<String> {
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<PathBuf, std::io::Error> {
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<PathBuf, std::io::Error> {
@ -466,21 +478,34 @@ impl Input {
}
pub fn resolve_py(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
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<PathBuf> {
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<PathBuf> {
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::<PathBuf>();
if let Ok(path) = self.resolve_local_decl(root, &path) {
return Some(path);
let path_buf = path.iter().skip(1).collect::<PathBuf>();
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
}

View file

@ -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());

View file

@ -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<Dict<NormalizedPathBuf, String>>,
path_cache: Shared<Dict<(Input, PathBuf), Option<PathBuf>>>,
}
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<PathBuf>) {
self.path_cache.borrow_mut().insert((input, path), result);
}
pub fn get_cached_path(&self, input: Input, path: PathBuf) -> Option<Option<PathBuf>> {
self.path_cache.borrow().get(&(input, path)).cloned()
}
pub fn remove(&self, path: impl AsRef<Path>) {
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<Path>) {
self.0.get_or_init(VirtualFileSystem::new).remove(path)
}
pub fn cache_path(&self, input: Input, path: PathBuf, result: Option<PathBuf>) {
self.0
.get_or_init(VirtualFileSystem::new)
.cache_path(input, path, result)
}
pub fn get_cached_path(&self, input: Input, path: PathBuf) -> Option<Option<PathBuf>> {
self.0
.get_or_init(VirtualFileSystem::new)
.get_cached_path(input, path)
}
}
pub static VFS: SharedVFS = SharedVFS(OnceLock::new());

View file

@ -177,6 +177,9 @@ impl<'c> Substituter<'c> {
qt: &Type,
st: &Type,
) -> EvalResult<Option<Self>> {
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<Option<Self>> {
if qt == st {
return Ok(None);
}
let mut qtps = qt.typarams();
let mut stps = st.typarams();
if qt.qual_name() == st.qual_name() {

View file

@ -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(())
}

View file

@ -269,6 +269,7 @@ impl Constraint {
pub trait CanbeFree {
fn unbound_name(&self) -> Option<Str>;
fn unbound_id(&self) -> Option<usize>;
fn constraint(&self) -> Option<Constraint>;
fn destructive_update_constraint(&self, constraint: Constraint, in_instantiation: bool);
}
@ -278,6 +279,10 @@ impl<T: CanbeFree + Send + Clone> Free<T> {
self.borrow().unbound_name()
}
pub fn unbound_id(&self) -> Option<usize> {
self.borrow().unbound_id()
}
pub fn constraint(&self) -> Option<Constraint> {
self.borrow().constraint()
}
@ -298,6 +303,7 @@ pub enum FreeKind<T> {
},
NamedUnbound {
name: Str,
id: Id,
lev: Level,
constraint: Constraint,
},
@ -307,23 +313,8 @@ impl<T: Hash> Hash for FreeKind<T> {
fn hash<H: Hasher>(&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<T: PartialEq> PartialEq for FreeKind<T> {
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<T: CanbeFree> FreeKind<T> {
}
}
pub fn unbound_id(&self) -> Option<usize> {
match self {
FreeKind::NamedUnbound { id, .. } | FreeKind::Unbound { id, .. } => Some(*id),
FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } => t.unbound_id(),
}
}
pub fn constraint(&self) -> Option<Constraint> {
match self {
FreeKind::Unbound { constraint, .. } | FreeKind::NamedUnbound { constraint, .. } => {
@ -407,6 +385,7 @@ impl<T: LimitedDisplay> LimitedDisplay for FreeKind<T> {
}
Self::NamedUnbound {
name,
id: _,
lev,
constraint,
} => {
@ -473,9 +452,10 @@ impl<T> FreeKind<T> {
}
}
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<T: Send + Clone>(Forkable<FreeKind<T>>);
impl Hash for Free<Type> {
fn hash<H: Hasher>(&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<Type> {
impl Hash for Free<TyParam> {
fn hash<H: Hasher>(&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<Type> {
} 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<Type> {
false
}
} else {
// name, level, constraint are equal
true
false
}
}
}
@ -679,27 +612,9 @@ impl PartialEq for Free<TyParam> {
} 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<TyParam> {
false
}
} else {
// name, level, constraint are equal
true
false
}
}
}
@ -827,24 +741,26 @@ impl Free<Type> {
}) {
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<T: Send + Clone> Free<T> {
}
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<T: Clone + Send + Sync + 'static> Free<T> {
} => (None, lev, constraint),
FreeKind::NamedUnbound {
name,
id: _,
lev,
constraint,
} => (Some(name), lev, constraint),
@ -1310,24 +1227,26 @@ impl Free<TyParam> {
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));
}
}

View file

@ -212,8 +212,8 @@ macro_rules! impl_t_for_enum {
#[derive(Debug, Default)]
pub struct SharedFrees {
tvs: Shared<Dict<Str, Type>>,
tps: Shared<Dict<Str, TyParam>>,
tvs: Shared<Dict<usize, Type>>,
tps: Shared<Dict<usize, TyParam>>,
}
impl SharedFrees {
@ -224,20 +224,20 @@ impl SharedFrees {
}
}
pub fn get_tv(&self, name: &str) -> Option<Type> {
self.tvs.borrow().get(name).cloned()
pub fn get_tv(&self, id: usize) -> Option<Type> {
self.tvs.borrow().get(&id).cloned()
}
pub fn get_tp(&self, name: &str) -> Option<TyParam> {
self.tps.borrow().get(name).cloned()
pub fn get_tp(&self, id: usize) -> Option<TyParam> {
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<usize> {
if let Some(fv) = self.as_free() {
fv.unbound_id()
} else {
None
}
}
fn constraint(&self) -> Option<Constraint> {
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 {

View file

@ -547,6 +547,15 @@ impl CanbeFree for TyParam {
}
}
fn unbound_id(&self) -> Option<usize> {
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<Constraint> {
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 {