From 41e3504779bca0524c11c9a3cc49f1a080dab771 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 6 Jun 2022 20:37:16 +0200 Subject: [PATCH 1/7] remove unneeded function --- compiler/types/src/subs.rs | 8 ++++---- compiler/types/src/unification_table.rs | 6 ------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index b252a7b6ee..8a6aac861f 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -1601,8 +1601,8 @@ impl Subs { /// Unions two keys without the possibility of failure. pub fn union(&mut self, left: Variable, right: Variable, desc: Descriptor) { - let l_root = self.utable.inlined_get_root_key(left); - let r_root = self.utable.inlined_get_root_key(right); + let l_root = self.utable.root_key(left); + let r_root = self.utable.root_key(right); // NOTE this swapping is intentional! most of our unifying commands are based on the elm // source, but unify_roots is from `ena`, not the elm source. Turns out that they have @@ -1657,7 +1657,7 @@ impl Subs { #[inline(always)] pub fn get_root_key(&mut self, key: Variable) -> Variable { - self.utable.inlined_get_root_key(key) + self.utable.root_key(key) } #[inline(always)] @@ -1667,7 +1667,7 @@ impl Subs { #[inline(always)] pub fn set(&mut self, key: Variable, r_value: Descriptor) { - let l_key = self.utable.inlined_get_root_key(key); + let l_key = self.utable.root_key(key); // self.utable.update_value(l_key, |node| node.value = r_value); self.utable.set_descriptor(l_key, r_value) diff --git a/compiler/types/src/unification_table.rs b/compiler/types/src/unification_table.rs index 9f2f95a223..693a83c53f 100644 --- a/compiler/types/src/unification_table.rs +++ b/compiler/types/src/unification_table.rs @@ -263,12 +263,6 @@ impl UnificationTable { self.ranks[index] } - // TODO remove - #[inline(always)] - pub fn inlined_get_root_key(&mut self, key: Variable) -> Variable { - self.root_key(key) - } - /// NOTE: assumes variables are root pub fn unify_roots(&mut self, to: Variable, from: Variable, desc: Descriptor) { let from_index = from.index() as usize; From 074834d2183810b1b0a1af9f4f9dd3d27c1633a2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 6 Jun 2022 20:57:04 +0200 Subject: [PATCH 2/7] just one metadata allocation --- compiler/types/src/unification_table.rs | 199 +++++++++++++++--------- 1 file changed, 128 insertions(+), 71 deletions(-) diff --git a/compiler/types/src/unification_table.rs b/compiler/types/src/unification_table.rs index 693a83c53f..1b9f05fea6 100644 --- a/compiler/types/src/unification_table.rs +++ b/compiler/types/src/unification_table.rs @@ -3,23 +3,42 @@ use crate::subs::{Content, Descriptor, Mark, OptVariable, Rank, Variable, Variab #[derive(Clone, Default)] pub struct UnificationTable { contents: Vec, - ranks: Vec, - marks: Vec, - copies: Vec, - redirects: Vec, + metadata: Vec, + // ranks: Vec, + // marks: Vec, + // copies: Vec, + // redirects: Vec, } pub struct Snapshot(UnificationTable); +#[derive(Debug, Clone, Copy)] +struct Combine { + redirect: OptVariable, + rank: Rank, + mark: Mark, + copy: OptVariable, +} + +impl Combine { + const NONE: Self = Self { + redirect: OptVariable::NONE, + rank: Rank::NONE, + mark: Mark::NONE, + copy: OptVariable::NONE, + }; +} + impl UnificationTable { #[allow(unused)] pub fn with_capacity(cap: usize) -> Self { Self { contents: Vec::with_capacity(cap), // vec![Content::Error; cap], - ranks: Vec::with_capacity(cap), // vec![Rank::NONE; cap], - marks: Vec::with_capacity(cap), // vec![Mark::NONE; cap], - copies: Vec::with_capacity(cap), // vec![OptVariable::NONE; cap], - redirects: Vec::with_capacity(cap), // vec![OptVariable::NONE; cap], + metadata: Vec::with_capacity(cap) +// ranks: Vec::with_capacity(cap), // vec![Rank::NONE; cap], +// marks: Vec::with_capacity(cap), // vec![Mark::NONE; cap], +// copies: Vec::with_capacity(cap), // vec![OptVariable::NONE; cap], +// redirects: Vec::with_capacity(cap), // vec![OptVariable::NONE; cap], } } @@ -38,12 +57,8 @@ impl UnificationTable { self.contents .extend(repeat(Content::FlexVar(None)).take(extra_length)); - self.ranks.extend(repeat(Rank::NONE).take(extra_length)); - self.marks.extend(repeat(Mark::NONE).take(extra_length)); - self.copies - .extend(repeat(OptVariable::NONE).take(extra_length)); - self.redirects - .extend(repeat(OptVariable::NONE).take(extra_length)); + self.metadata + .extend(repeat(Combine::NONE).take(extra_length)); VariableSubsSlice::new(start as _, extra_length as _) } @@ -58,10 +73,15 @@ impl UnificationTable { let variable = unsafe { Variable::from_index(self.len() as _) }; self.contents.push(content); - self.ranks.push(rank); - self.marks.push(mark); - self.copies.push(copy); - self.redirects.push(OptVariable::NONE); + + let combine = Combine { + redirect: OptVariable::NONE, + rank, + mark, + copy, + }; + + self.metadata.push(combine); variable } @@ -75,33 +95,51 @@ impl UnificationTable { mark: Mark, copy: OptVariable, ) { - let index = self.root_key(key).index() as usize; + let root = self.root_key(key); + self.set_unchecked(root, content, rank, mark, copy) + } + + pub fn set_unchecked( + &mut self, + key: Variable, + content: Content, + rank: Rank, + mark: Mark, + copy: OptVariable, + ) { + let index = key.index() as usize; self.contents[index] = content; - self.ranks[index] = rank; - self.marks[index] = mark; - self.copies[index] = copy; + + let combine = Combine { + redirect: OptVariable::NONE, + rank, + mark, + copy, + }; + + self.metadata[index] = combine; } pub fn modify(&mut self, key: Variable, mapper: F) -> T where F: FnOnce(&mut Descriptor) -> T, { - let index = self.root_key(key).index() as usize; + let root = self.root_key(key); + let index = root.index() as usize; + + let combine = self.metadata[index]; let mut desc = Descriptor { content: self.contents[index], - rank: self.ranks[index], - mark: self.marks[index], - copy: self.copies[index], + rank: combine.rank, + mark: combine.mark, + copy: combine.copy, }; let result = mapper(&mut desc); - self.contents[index] = desc.content; - self.ranks[index] = desc.rank; - self.marks[index] = desc.mark; - self.copies[index] = desc.copy; + self.set_unchecked(key, desc.content, desc.rank, desc.mark, desc.copy); result } @@ -110,18 +148,18 @@ impl UnificationTable { #[inline(always)] pub fn get_rank_unchecked(&self, key: Variable) -> Rank { - self.ranks[key.index() as usize] + self.metadata[key.index() as usize].rank } #[inline(always)] pub fn get_mark_unchecked(&self, key: Variable) -> Mark { - self.marks[key.index() as usize] + self.metadata[key.index() as usize].mark } #[allow(unused)] #[inline(always)] pub fn get_copy_unchecked(&self, key: Variable) -> OptVariable { - self.copies[key.index() as usize] + self.metadata[key.index() as usize].copy } #[inline(always)] @@ -133,18 +171,18 @@ impl UnificationTable { #[inline(always)] pub fn get_rank(&self, key: Variable) -> Rank { - self.ranks[self.root_key_without_compacting(key).index() as usize] + self.metadata[self.root_key_without_compacting(key).index() as usize].rank } #[inline(always)] pub fn get_mark(&self, key: Variable) -> Mark { - self.marks[self.root_key_without_compacting(key).index() as usize] + self.metadata[self.root_key_without_compacting(key).index() as usize].mark } #[inline(always)] pub fn get_copy(&self, key: Variable) -> OptVariable { let index = self.root_key_without_compacting(key).index() as usize; - self.copies[index] + self.metadata[index].copy } #[inline(always)] @@ -156,18 +194,18 @@ impl UnificationTable { #[inline(always)] pub fn set_rank_unchecked(&mut self, key: Variable, value: Rank) { - self.ranks[key.index() as usize] = value; + self.metadata[key.index() as usize].rank = value; } #[inline(always)] pub fn set_mark_unchecked(&mut self, key: Variable, value: Mark) { - self.marks[key.index() as usize] = value; + self.metadata[key.index() as usize].mark = value; } #[allow(unused)] #[inline(always)] pub fn set_copy_unchecked(&mut self, key: Variable, value: OptVariable) { - self.copies[key.index() as usize] = value; + self.metadata[key.index() as usize].copy = value; } #[allow(unused)] @@ -181,19 +219,19 @@ impl UnificationTable { #[inline(always)] pub fn set_rank(&mut self, key: Variable, value: Rank) { let index = self.root_key(key).index() as usize; - self.ranks[index] = value; + self.metadata[index].rank = value; } #[inline(always)] pub fn set_mark(&mut self, key: Variable, value: Mark) { let index = self.root_key(key).index() as usize; - self.marks[index] = value; + self.metadata[index].mark = value; } #[inline(always)] pub fn set_copy(&mut self, key: Variable, value: OptVariable) { let index = self.root_key(key).index() as usize; - self.copies[index] = value; + self.metadata[index].copy = value; } #[inline(always)] @@ -208,12 +246,12 @@ impl UnificationTable { pub fn root_key(&mut self, mut key: Variable) -> Variable { let index = key.index() as usize; - while let Some(redirect) = self.redirects[key.index() as usize].into_variable() { + while let Some(redirect) = self.metadata[key.index() as usize].redirect.into_variable() { key = redirect; } if index != key.index() as usize { - self.redirects[index] = OptVariable::from(key); + self.metadata[index].redirect = OptVariable::from(key); } key @@ -221,7 +259,7 @@ impl UnificationTable { #[inline(always)] pub fn root_key_without_compacting(&self, mut key: Variable) -> Variable { - while let Some(redirect) = self.redirects[key.index() as usize].into_variable() { + while let Some(redirect) = self.metadata[key.index() as usize].redirect.into_variable() { key = redirect; } @@ -246,7 +284,7 @@ impl UnificationTable { } pub fn is_redirect(&self, key: Variable) -> bool { - self.redirects[key.index() as usize].is_some() + self.metadata[key.index() as usize].redirect.is_some() } pub fn unioned(&mut self, a: Variable, b: Variable) -> bool { @@ -257,10 +295,10 @@ impl UnificationTable { #[inline(always)] pub fn get_rank_set_mark(&mut self, key: Variable, mark: Mark) -> Rank { let index = self.root_key(key).index() as usize; + let metadata = &mut self.metadata[index]; - self.marks[index] = mark; - - self.ranks[index] + metadata.mark = mark; + metadata.rank } /// NOTE: assumes variables are root @@ -270,34 +308,27 @@ impl UnificationTable { // redirect from -> to if from_index != to_index { - self.redirects[from_index] = OptVariable::from(to); + self.metadata[from_index].redirect = OptVariable::from(to); } // update to's Descriptor - self.contents[to_index] = desc.content; - self.ranks[to_index] = desc.rank; - self.marks[to_index] = desc.mark; - self.copies[to_index] = desc.copy; + self.set_unchecked(to, desc.content, desc.rank, desc.mark, desc.copy); } pub fn get_descriptor(&self, key: Variable) -> Descriptor { let index = self.root_key_without_compacting(key).index() as usize; + let metadata = self.metadata[index]; Descriptor { content: self.contents[index], - rank: self.ranks[index], - mark: self.marks[index], - copy: self.copies[index], + rank: metadata.rank, + mark: metadata.mark, + copy: metadata.copy, } } pub fn set_descriptor(&mut self, key: Variable, desc: Descriptor) { - let index = self.root_key(key).index() as usize; - - self.contents[index] = desc.content; - self.ranks[index] = desc.rank; - self.marks[index] = desc.mark; - self.copies[index] = desc.copy; + self.set(key, desc.content, desc.rank, desc.mark, desc.copy); } pub(crate) fn serialize( @@ -308,10 +339,23 @@ impl UnificationTable { use crate::subs::Subs; written = Subs::serialize_slice(&self.contents, writer, written)?; - written = Subs::serialize_slice(&self.ranks, writer, written)?; - written = Subs::serialize_slice(&self.marks, writer, written)?; - written = Subs::serialize_slice(&self.copies, writer, written)?; - written = Subs::serialize_slice(&self.redirects, writer, written)?; + + let mut ranks = Vec::new(); + let mut marks = Vec::new(); + let mut copies = Vec::new(); + let mut redirects = Vec::new(); + + for c in self.metadata.iter() { + ranks.push(c.rank); + marks.push(c.mark); + copies.push(c.copy); + redirects.push(c.redirect); + } + + written = Subs::serialize_slice(&ranks, writer, written)?; + written = Subs::serialize_slice(&marks, writer, written)?; + written = Subs::serialize_slice(&copies, writer, written)?; + written = Subs::serialize_slice(&redirects, writer, written)?; Ok(written) } @@ -325,12 +369,25 @@ impl UnificationTable { let (copies, offset) = Subs::deserialize_slice::(bytes, length, offset); let (redirects, offset) = Subs::deserialize_slice::(bytes, length, offset); + let mut metadata = Vec::with_capacity(ranks.len()); + + let it = ranks + .iter() + .zip(marks.iter()) + .zip(copies.iter()) + .zip(redirects.iter()); + for (((rank, mark), copy), redirect) in it { + metadata.push(Combine { + redirect: *redirect, + rank: *rank, + mark: *mark, + copy: *copy, + }); + } + let this = Self { contents: contents.to_vec(), - ranks: ranks.to_vec(), - marks: marks.to_vec(), - copies: copies.to_vec(), - redirects: redirects.to_vec(), + metadata, }; (this, offset) From f0e4b2619627bf03aa2ef143394a7d1a9bb12847 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 7 Jun 2022 19:33:38 +0200 Subject: [PATCH 3/7] cleanup --- compiler/types/src/unification_table.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/compiler/types/src/unification_table.rs b/compiler/types/src/unification_table.rs index 1b9f05fea6..8561387166 100644 --- a/compiler/types/src/unification_table.rs +++ b/compiler/types/src/unification_table.rs @@ -4,10 +4,6 @@ use crate::subs::{Content, Descriptor, Mark, OptVariable, Rank, Variable, Variab pub struct UnificationTable { contents: Vec, metadata: Vec, - // ranks: Vec, - // marks: Vec, - // copies: Vec, - // redirects: Vec, } pub struct Snapshot(UnificationTable); @@ -33,12 +29,8 @@ impl UnificationTable { #[allow(unused)] pub fn with_capacity(cap: usize) -> Self { Self { - contents: Vec::with_capacity(cap), // vec![Content::Error; cap], - metadata: Vec::with_capacity(cap) -// ranks: Vec::with_capacity(cap), // vec![Rank::NONE; cap], -// marks: Vec::with_capacity(cap), // vec![Mark::NONE; cap], -// copies: Vec::with_capacity(cap), // vec![OptVariable::NONE; cap], -// redirects: Vec::with_capacity(cap), // vec![OptVariable::NONE; cap], + contents: Vec::with_capacity(cap), // vec![Content::Error; cap], + metadata: Vec::with_capacity(cap), } } @@ -111,14 +103,12 @@ impl UnificationTable { self.contents[index] = content; - let combine = Combine { + self.metadata[index] = Combine { redirect: OptVariable::NONE, rank, mark, copy, }; - - self.metadata[index] = combine; } pub fn modify(&mut self, key: Variable, mapper: F) -> T @@ -128,7 +118,7 @@ impl UnificationTable { let root = self.root_key(key); let index = root.index() as usize; - let combine = self.metadata[index]; + let combine = &self.metadata[index]; let mut desc = Descriptor { content: self.contents[index], From d48bbd0499665620c8221234d6d049d0dbe25833 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 7 Jun 2022 21:52:43 +0200 Subject: [PATCH 4/7] fix bug where root key was not used --- compiler/types/src/unification_table.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/types/src/unification_table.rs b/compiler/types/src/unification_table.rs index 8561387166..2dc4a8db05 100644 --- a/compiler/types/src/unification_table.rs +++ b/compiler/types/src/unification_table.rs @@ -29,7 +29,7 @@ impl UnificationTable { #[allow(unused)] pub fn with_capacity(cap: usize) -> Self { Self { - contents: Vec::with_capacity(cap), // vec![Content::Error; cap], + contents: Vec::with_capacity(cap), metadata: Vec::with_capacity(cap), } } @@ -129,7 +129,7 @@ impl UnificationTable { let result = mapper(&mut desc); - self.set_unchecked(key, desc.content, desc.rank, desc.mark, desc.copy); + self.set_unchecked(root, desc.content, desc.rank, desc.mark, desc.copy); result } From 7481786b6b35e741eb122cc66ca9990a222bf6d2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 7 Jun 2022 21:54:35 +0200 Subject: [PATCH 5/7] code reuse in root key finding --- compiler/types/src/unification_table.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/compiler/types/src/unification_table.rs b/compiler/types/src/unification_table.rs index 2dc4a8db05..34983d84db 100644 --- a/compiler/types/src/unification_table.rs +++ b/compiler/types/src/unification_table.rs @@ -233,18 +233,14 @@ impl UnificationTable { // ROOT KEY #[inline(always)] - pub fn root_key(&mut self, mut key: Variable) -> Variable { - let index = key.index() as usize; + pub fn root_key(&mut self, key: Variable) -> Variable { + let root = self.root_key_without_compacting(key); - while let Some(redirect) = self.metadata[key.index() as usize].redirect.into_variable() { - key = redirect; + if root != key { + self.metadata[key.index() as usize].redirect = OptVariable::from(root); } - if index != key.index() as usize { - self.metadata[index].redirect = OptVariable::from(key); - } - - key + root } #[inline(always)] From 552c2706f61da4ab0a845a280458b9d4ae0ff7e0 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 7 Jun 2022 22:17:29 +0200 Subject: [PATCH 6/7] compact all nodes on the way to the root key --- compiler/types/src/unification_table.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/types/src/unification_table.rs b/compiler/types/src/unification_table.rs index 34983d84db..0fa33f84d4 100644 --- a/compiler/types/src/unification_table.rs +++ b/compiler/types/src/unification_table.rs @@ -233,11 +233,19 @@ impl UnificationTable { // ROOT KEY #[inline(always)] - pub fn root_key(&mut self, key: Variable) -> Variable { + pub fn root_key(&mut self, mut key: Variable) -> Variable { let root = self.root_key_without_compacting(key); - if root != key { - self.metadata[key.index() as usize].redirect = OptVariable::from(root); + while root != key { + let next_key = std::mem::replace( + &mut self.metadata[key.index() as usize].redirect, + OptVariable::from(root), + ); + + match next_key.into_variable() { + Some(redirect) => key = redirect, + None => break, // no redirect; we've found the root + } } root From 96ee2c52fbc35ae47810a278b262724ec6b4f51f Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 11 Jun 2022 18:06:18 +0200 Subject: [PATCH 7/7] rework Combined --- compiler/types/src/unification_table.rs | 202 +++++++++++++++--------- 1 file changed, 131 insertions(+), 71 deletions(-) diff --git a/compiler/types/src/unification_table.rs b/compiler/types/src/unification_table.rs index 0fa33f84d4..e134247e00 100644 --- a/compiler/types/src/unification_table.rs +++ b/compiler/types/src/unification_table.rs @@ -1,3 +1,5 @@ +use std::hint::unreachable_unchecked; + use crate::subs::{Content, Descriptor, Mark, OptVariable, Rank, Variable, VariableSubsSlice}; #[derive(Clone, Default)] @@ -9,22 +11,30 @@ pub struct UnificationTable { pub struct Snapshot(UnificationTable); #[derive(Debug, Clone, Copy)] -struct Combine { - redirect: OptVariable, +enum Combine { + Redirect(Variable), + Root(Root), +} + +#[derive(Debug, Clone, Copy)] +pub struct Root { rank: Rank, mark: Mark, copy: OptVariable, } -impl Combine { +impl Root { const NONE: Self = Self { - redirect: OptVariable::NONE, rank: Rank::NONE, mark: Mark::NONE, copy: OptVariable::NONE, }; } +impl Combine { + const NONE: Self = Self::Root(Root::NONE); +} + impl UnificationTable { #[allow(unused)] pub fn with_capacity(cap: usize) -> Self { @@ -66,12 +76,7 @@ impl UnificationTable { self.contents.push(content); - let combine = Combine { - redirect: OptVariable::NONE, - rank, - mark, - copy, - }; + let combine = Combine::Root(Root { rank, mark, copy }); self.metadata.push(combine); @@ -103,53 +108,62 @@ impl UnificationTable { self.contents[index] = content; - self.metadata[index] = Combine { - redirect: OptVariable::NONE, - rank, - mark, - copy, - }; + self.metadata[index] = Combine::Root(Root { rank, mark, copy }); } pub fn modify(&mut self, key: Variable, mapper: F) -> T where F: FnOnce(&mut Descriptor) -> T, { - let root = self.root_key(key); - let index = root.index() as usize; + let root_key = self.root_key(key); + let index = root_key.index() as usize; - let combine = &self.metadata[index]; + let root = self.get_root_unchecked(root_key); let mut desc = Descriptor { content: self.contents[index], - rank: combine.rank, - mark: combine.mark, - copy: combine.copy, + rank: root.rank, + mark: root.mark, + copy: root.copy, }; let result = mapper(&mut desc); - self.set_unchecked(root, desc.content, desc.rank, desc.mark, desc.copy); + self.set_unchecked(root_key, desc.content, desc.rank, desc.mark, desc.copy); result } // GET UNCHECKED + #[inline(always)] + pub fn get_root_unchecked(&self, key: Variable) -> Root { + match self.metadata[key.index() as usize] { + Combine::Root(root) => root, + Combine::Redirect(_) => { + if cfg!(debug_assertions) { + unreachable!("unchecked access on non-root variable {:?}", key); + } else { + unsafe { unreachable_unchecked() } + } + } + } + } + #[inline(always)] pub fn get_rank_unchecked(&self, key: Variable) -> Rank { - self.metadata[key.index() as usize].rank + self.get_root_unchecked(key).rank } #[inline(always)] pub fn get_mark_unchecked(&self, key: Variable) -> Mark { - self.metadata[key.index() as usize].mark + self.get_root_unchecked(key).mark } #[allow(unused)] #[inline(always)] pub fn get_copy_unchecked(&self, key: Variable) -> OptVariable { - self.metadata[key.index() as usize].copy + self.get_root_unchecked(key).copy } #[inline(always)] @@ -159,20 +173,34 @@ impl UnificationTable { // GET CHECKED + #[inline(always)] + pub fn get_root(&self, key: Variable) -> Root { + let root_key = self.root_key_without_compacting(key); + match self.metadata[root_key.index() as usize] { + Combine::Root(root) => root, + Combine::Redirect(_) => { + if cfg!(debug_assertions) { + unreachable!("the root key {:?} is not actually a root key", root_key); + } else { + unsafe { unreachable_unchecked() } + } + } + } + } + #[inline(always)] pub fn get_rank(&self, key: Variable) -> Rank { - self.metadata[self.root_key_without_compacting(key).index() as usize].rank + self.get_root(key).rank } #[inline(always)] pub fn get_mark(&self, key: Variable) -> Mark { - self.metadata[self.root_key_without_compacting(key).index() as usize].mark + self.get_root(key).mark } #[inline(always)] pub fn get_copy(&self, key: Variable) -> OptVariable { - let index = self.root_key_without_compacting(key).index() as usize; - self.metadata[index].copy + self.get_root(key).copy } #[inline(always)] @@ -182,20 +210,37 @@ impl UnificationTable { // SET UNCHECKED + #[inline(always)] + pub fn modify_root_unchecked(&mut self, key: Variable, f: F) -> T + where + F: Fn(&mut Root) -> T, + { + match &mut self.metadata[key.index() as usize] { + Combine::Root(root) => f(root), + Combine::Redirect(_) => { + if cfg!(debug_assertions) { + unreachable!("unchecked access on non-root variable {:?}", key); + } else { + unsafe { unreachable_unchecked() } + } + } + } + } + #[inline(always)] pub fn set_rank_unchecked(&mut self, key: Variable, value: Rank) { - self.metadata[key.index() as usize].rank = value; + self.modify_root_unchecked(key, |root| root.rank = value) } #[inline(always)] pub fn set_mark_unchecked(&mut self, key: Variable, value: Mark) { - self.metadata[key.index() as usize].mark = value; + self.modify_root_unchecked(key, |root| root.mark = value) } #[allow(unused)] #[inline(always)] pub fn set_copy_unchecked(&mut self, key: Variable, value: OptVariable) { - self.metadata[key.index() as usize].copy = value; + self.modify_root_unchecked(key, |root| root.copy = value) } #[allow(unused)] @@ -208,20 +253,20 @@ impl UnificationTable { #[inline(always)] pub fn set_rank(&mut self, key: Variable, value: Rank) { - let index = self.root_key(key).index() as usize; - self.metadata[index].rank = value; + let root_key = self.root_key(key); + self.modify_root_unchecked(root_key, |root| root.rank = value) } #[inline(always)] pub fn set_mark(&mut self, key: Variable, value: Mark) { - let index = self.root_key(key).index() as usize; - self.metadata[index].mark = value; + let root_key = self.root_key(key); + self.modify_root_unchecked(root_key, |root| root.mark = value) } #[inline(always)] pub fn set_copy(&mut self, key: Variable, value: OptVariable) { - let index = self.root_key(key).index() as usize; - self.metadata[index].copy = value; + let root_key = self.root_key(key); + self.modify_root_unchecked(root_key, |root| root.copy = value) } #[inline(always)] @@ -236,16 +281,9 @@ impl UnificationTable { pub fn root_key(&mut self, mut key: Variable) -> Variable { let root = self.root_key_without_compacting(key); - while root != key { - let next_key = std::mem::replace( - &mut self.metadata[key.index() as usize].redirect, - OptVariable::from(root), - ); - - match next_key.into_variable() { - Some(redirect) => key = redirect, - None => break, // no redirect; we've found the root - } + while let Combine::Redirect(redirect) = &mut self.metadata[key.index() as usize] { + key = *redirect; + *redirect = root; } root @@ -253,7 +291,7 @@ impl UnificationTable { #[inline(always)] pub fn root_key_without_compacting(&self, mut key: Variable) -> Variable { - while let Some(redirect) = self.metadata[key.index() as usize].redirect.into_variable() { + while let Combine::Redirect(redirect) = self.metadata[key.index() as usize] { key = redirect; } @@ -278,7 +316,7 @@ impl UnificationTable { } pub fn is_redirect(&self, key: Variable) -> bool { - self.metadata[key.index() as usize].redirect.is_some() + matches!(self.metadata[key.index() as usize], Combine::Redirect(_)) } pub fn unioned(&mut self, a: Variable, b: Variable) -> bool { @@ -288,11 +326,13 @@ impl UnificationTable { // custom very specific helpers #[inline(always)] pub fn get_rank_set_mark(&mut self, key: Variable, mark: Mark) -> Rank { - let index = self.root_key(key).index() as usize; - let metadata = &mut self.metadata[index]; + let root_key = self.root_key(key); - metadata.mark = mark; - metadata.rank + self.modify_root_unchecked(root_key, |root| { + root.mark = mark; + + root.rank + }) } /// NOTE: assumes variables are root @@ -302,7 +342,7 @@ impl UnificationTable { // redirect from -> to if from_index != to_index { - self.metadata[from_index].redirect = OptVariable::from(to); + self.metadata[from_index] = Combine::Redirect(to) } // update to's Descriptor @@ -310,14 +350,16 @@ impl UnificationTable { } pub fn get_descriptor(&self, key: Variable) -> Descriptor { - let index = self.root_key_without_compacting(key).index() as usize; - let metadata = self.metadata[index]; + let root_key = self.root_key_without_compacting(key); + let index = root_key.index() as usize; + + let root = self.get_root_unchecked(root_key); Descriptor { content: self.contents[index], - rank: metadata.rank, - mark: metadata.mark, - copy: metadata.copy, + rank: root.rank, + mark: root.mark, + copy: root.copy, } } @@ -340,10 +382,24 @@ impl UnificationTable { let mut redirects = Vec::new(); for c in self.metadata.iter() { - ranks.push(c.rank); - marks.push(c.mark); - copies.push(c.copy); - redirects.push(c.redirect); + match c { + Combine::Redirect(redirect) => { + let root = Root::NONE; + + ranks.push(root.rank); + marks.push(root.mark); + copies.push(root.copy); + + redirects.push(OptVariable::from(*redirect)); + } + Combine::Root(root) => { + ranks.push(root.rank); + marks.push(root.mark); + copies.push(root.copy); + + redirects.push(OptVariable::NONE); + } + } } written = Subs::serialize_slice(&ranks, writer, written)?; @@ -371,12 +427,16 @@ impl UnificationTable { .zip(copies.iter()) .zip(redirects.iter()); for (((rank, mark), copy), redirect) in it { - metadata.push(Combine { - redirect: *redirect, - rank: *rank, - mark: *mark, - copy: *copy, - }); + match redirect.into_variable() { + Some(redirect) => metadata.push(Combine::Redirect(redirect)), + None => { + metadata.push(Combine::Root(Root { + rank: *rank, + mark: *mark, + copy: *copy, + })); + } + } } let this = Self {