mirror of
https://github.com/erg-lang/erg.git
synced 2025-10-03 05:54:33 +00:00
Merge pull request #534 from erg-lang/perf-tyvar
Fix type-variable related bug
This commit is contained in:
commit
fdbfaec19a
9 changed files with 286 additions and 288 deletions
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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,6 +741,9 @@ impl Free<Type> {
|
|||
}) {
|
||||
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, ..
|
||||
|
@ -843,8 +760,7 @@ impl Free<Type> {
|
|||
}
|
||||
*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,6 +1227,9 @@ impl Free<TyParam> {
|
|||
if new_constraint.get_type() == Some(&Type::Never) {
|
||||
panic!("{new_constraint}");
|
||||
}
|
||||
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, ..
|
||||
|
@ -1326,8 +1246,7 @@ impl Free<TyParam> {
|
|||
}
|
||||
*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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue