1const NO_CONTEXT_HASH: u64 = 0;
7
8fn default_context_hash_cache() -> AtomicU64 {
9 AtomicU64::new(NO_CONTEXT_HASH)
10}
11
12use crate::representation::ReprId;
13use crate::representation::Representation;
14use std::any::TypeId;
15
16use std::collections::BTreeMap;
17use std::collections::VecDeque;
18use std::hash::{DefaultHasher, Hash, Hasher};
19use std::sync::Arc;
20use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
21
22use super::comprehension::Comprehension;
23use super::declaration::declaration_content_generation;
24use super::serde::{AsId, DefaultWithId, HasId, IdPtr, ObjId, PtrAsInner};
25use super::{
26 DeclarationPtr, DomainPtr, Expression, GroundDomain, Model, Moo, Name, ReturnType, Typeable,
27};
28use itertools::{Itertools as _, izip};
29use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
30use serde::{Deserialize, Serialize};
31use serde_with::serde_as;
32use tracing::trace;
33use uniplate::{Biplate, Tree, Uniplate};
34
35use indexmap::IndexMap;
36use indexmap::map::Entry;
37
38static SYMBOL_TABLE_ID_COUNTER: AtomicU32 = const { AtomicU32::new(0) };
43
44#[derive(Debug, Clone, PartialEq, Eq, Hash)]
45pub struct SymbolTablePtr
46where
47 Self: Send + Sync,
48{
49 inner: Arc<SymbolTablePtrInner>,
50}
51
52impl SymbolTablePtr {
53 pub fn new() -> Self {
55 Self::new_with_data(SymbolTable::new())
56 }
57
58 pub fn with_parent(symbols: SymbolTablePtr) -> Self {
60 Self::new_with_data(SymbolTable::with_parent(symbols))
61 }
62
63 fn new_with_data(data: SymbolTable) -> Self {
64 let object_id = SYMBOL_TABLE_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
65 let id = ObjId {
66 object_id,
67 type_name: SymbolTablePtr::TYPE_NAME.into(),
68 };
69 Self::new_with_id_and_data(id, data)
70 }
71
72 fn new_with_id_and_data(id: ObjId, data: SymbolTable) -> Self {
73 Self {
74 inner: Arc::new(SymbolTablePtrInner {
75 id,
76 value: RwLock::new(data),
77 }),
78 }
79 }
80
81 pub fn read(&self) -> RwLockReadGuard<'_, SymbolTable> {
88 self.inner.value.read()
89 }
90
91 pub fn write(&self) -> RwLockWriteGuard<'_, SymbolTable> {
104 self.inner.value.write()
105 }
106
107 pub fn detach(&self) -> Self {
110 Self::new_with_data(self.read().clone())
111 }
112}
113
114impl Default for SymbolTablePtr {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120impl HasId for SymbolTablePtr {
121 const TYPE_NAME: &'static str = "SymbolTable";
122
123 fn id(&self) -> ObjId {
124 self.inner.id.clone()
125 }
126}
127
128impl DefaultWithId for SymbolTablePtr {
129 fn default_with_id(id: ObjId) -> Self {
130 Self::new_with_id_and_data(id, SymbolTable::default())
131 }
132}
133
134impl IdPtr for SymbolTablePtr {
135 type Data = SymbolTable;
136
137 fn get_data(&self) -> Self::Data {
138 self.read().clone()
139 }
140
141 fn with_id_and_data(id: ObjId, data: Self::Data) -> Self {
142 Self::new_with_id_and_data(id, data)
143 }
144}
145
146impl Uniplate for SymbolTablePtr {
152 fn uniplate(&self) -> (Tree<Self>, Box<dyn Fn(Tree<Self>) -> Self>) {
153 let symtab = self.read();
154 let (tree, recons) = Biplate::<SymbolTablePtr>::biplate(&symtab as &SymbolTable);
155
156 let self2 = self.clone();
157 (
158 tree,
159 Box::new(move |x| {
160 let self3 = self2.clone();
161 *(self3.write()) = recons(x);
162 self3
163 }),
164 )
165 }
166}
167
168impl<To> Biplate<To> for SymbolTablePtr
169where
170 SymbolTable: Biplate<To>,
171 To: Uniplate,
172{
173 fn biplate(&self) -> (Tree<To>, Box<dyn Fn(Tree<To>) -> Self>) {
174 if TypeId::of::<To>() == TypeId::of::<Self>() {
175 unsafe {
176 let self_as_to = std::mem::transmute::<&Self, &To>(self).clone();
177 (
178 Tree::One(self_as_to),
179 Box::new(move |x| {
180 let Tree::One(x) = x else { panic!() };
181
182 let x_as_self = std::mem::transmute::<&To, &Self>(&x);
183 x_as_self.clone()
184 }),
185 )
186 }
187 } else {
188 let decl = self.read();
190 let (tree, recons) = Biplate::<To>::biplate(&decl as &SymbolTable);
191
192 let self2 = self.clone();
193 (
194 tree,
195 Box::new(move |x| {
196 let self3 = self2.clone();
197 *(self3.write()) = recons(x);
198 self3
199 }),
200 )
201 }
202 }
203}
204
205#[derive(Debug)]
206struct SymbolTablePtrInner {
207 id: ObjId,
208 value: RwLock<SymbolTable>,
209}
210
211impl Hash for SymbolTablePtrInner {
212 fn hash<H: Hasher>(&self, state: &mut H) {
213 self.id.hash(state);
215 }
216}
217
218impl PartialEq for SymbolTablePtrInner {
219 fn eq(&self, other: &Self) -> bool {
220 self.id == other.id
221 }
222}
223
224impl Eq for SymbolTablePtrInner {}
225
226#[serde_as]
261#[derive(Debug, Serialize, Deserialize)]
262pub struct SymbolTable {
263 #[serde_as(as = "Vec<(_,PtrAsInner)>")]
264 table: IndexMap<Name, DeclarationPtr>,
265
266 #[serde_as(as = "Option<AsId>")]
267 parent: Option<SymbolTablePtr>,
268
269 next_machine_name: i32,
270
271 #[serde(default, skip_serializing)]
272 local_bindings_xor: u64,
273
274 #[serde(default, skip_serializing)]
275 local_bindings_generation: u64,
277
278 #[serde(default = "default_context_hash_cache", skip_serializing)]
279 context_hash_cache: AtomicU64,
280
281 #[serde(default = "default_context_hash_cache", skip_serializing)]
282 context_hash_generation: AtomicU64,
284}
285
286impl Clone for SymbolTable {
287 fn clone(&self) -> Self {
288 Self {
289 table: self.table.clone(),
290 parent: self.parent.clone(),
291 next_machine_name: self.next_machine_name,
292 local_bindings_xor: self.local_bindings_xor,
293 local_bindings_generation: self.local_bindings_generation,
294 context_hash_cache: AtomicU64::new(NO_CONTEXT_HASH),
295 context_hash_generation: AtomicU64::new(0),
296 }
297 }
298}
299
300impl PartialEq for SymbolTable {
301 fn eq(&self, other: &Self) -> bool {
302 self.table == other.table
303 && self.parent == other.parent
304 && self.next_machine_name == other.next_machine_name
305 }
306}
307
308impl Eq for SymbolTable {}
309
310impl SymbolTable {
311 pub fn new() -> SymbolTable {
313 SymbolTable::new_inner(None)
314 }
315
316 pub fn with_parent(parent: SymbolTablePtr) -> SymbolTable {
318 SymbolTable::new_inner(Some(parent))
319 }
320
321 fn new_inner(parent: Option<SymbolTablePtr>) -> SymbolTable {
322 let id = SYMBOL_TABLE_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
323 trace!(
324 "new symbol table: id = {id} parent_id = {}",
325 parent
326 .as_ref()
327 .map(|x| x.id().to_string())
328 .unwrap_or(String::from("none"))
329 );
330 SymbolTable {
331 table: IndexMap::new(),
332 next_machine_name: 0,
333 parent,
334 local_bindings_xor: 0,
335 local_bindings_generation: declaration_content_generation(),
336 context_hash_cache: AtomicU64::new(NO_CONTEXT_HASH),
337 context_hash_generation: AtomicU64::new(0),
338 }
339 }
340
341 fn binding_contribution(name: &Name, declaration: &DeclarationPtr) -> u64 {
342 let mut hasher = DefaultHasher::new();
343 (name.clone(), declaration.content_hash()).hash(&mut hasher);
344 hasher.finish()
345 }
346
347 fn finalize_context_hash(bindings_xor: u64, binding_count: usize) -> u64 {
348 let mut hasher = DefaultHasher::new();
349 binding_count.hash(&mut hasher);
350 bindings_xor.hash(&mut hasher);
351 hasher.finish()
352 }
353
354 fn recompute_local_bindings_xor(&mut self) {
355 self.local_bindings_xor = self.table.iter().fold(0u64, |acc, (name, declaration)| {
356 acc ^ Self::binding_contribution(name, declaration)
357 });
358 self.local_bindings_generation = declaration_content_generation();
359 }
360
361 fn ensure_local_binding_hashes_current(&mut self) {
363 if self.local_bindings_generation != declaration_content_generation() {
364 self.recompute_local_bindings_xor();
365 }
366 }
367
368 pub(crate) fn refresh_local_binding_hashes(&mut self) {
369 self.recompute_local_bindings_xor();
370 self.invalidate_context_hash_cache();
371 }
372
373 fn xor_binding_in(&mut self, name: &Name, declaration: &DeclarationPtr) {
374 self.local_bindings_xor ^= Self::binding_contribution(name, declaration);
375 }
376
377 fn xor_binding_out(&mut self, name: &Name, declaration: &DeclarationPtr) {
378 self.local_bindings_xor ^= Self::binding_contribution(name, declaration);
379 }
380
381 pub(crate) fn invalidate_context_hash_cache(&mut self) {
382 self.context_hash_cache
383 .store(NO_CONTEXT_HASH, Ordering::Relaxed);
384 self.context_hash_generation.store(0, Ordering::Relaxed);
385 }
386
387 pub fn context_hash(&self) -> u64 {
392 let declaration_generation = declaration_content_generation();
393 let cached_generation = self.context_hash_generation.load(Ordering::Acquire);
394 let cached = self.context_hash_cache.load(Ordering::Relaxed);
395 if cached != NO_CONTEXT_HASH && cached_generation == declaration_generation {
396 return cached;
397 }
398
399 loop {
400 let generation_before = declaration_content_generation();
401 let hash = if self.parent.is_none() {
402 let bindings_xor = if self.local_bindings_generation == generation_before {
403 self.local_bindings_xor
404 } else {
405 self.table.iter().fold(0u64, |acc, (name, declaration)| {
406 acc ^ Self::binding_contribution(name, declaration)
407 })
408 };
409 Self::finalize_context_hash(bindings_xor, self.table.len())
410 } else {
411 self.compute_scoped_context_hash()
412 };
413 let generation_after = declaration_content_generation();
414 if generation_before == generation_after {
415 self.context_hash_cache.store(hash, Ordering::Relaxed);
416 self.context_hash_generation
417 .store(generation_after, Ordering::Release);
418 break hash;
419 }
420 }
421 }
422
423 fn compute_scoped_context_hash(&self) -> u64 {
424 let mut hasher = DefaultHasher::new();
425 let mut declarations: BTreeMap<Name, u64> = BTreeMap::new();
426
427 for (name, declaration) in self.iter_local() {
428 declarations.insert(name.clone(), declaration.content_hash());
429 }
430
431 let mut parent = self.parent.clone();
432 while let Some(parent_ptr) = parent.take() {
433 let guard = parent_ptr.read();
434 for (name, declaration) in guard.iter_local() {
435 declarations
436 .entry(name.clone())
437 .or_insert_with(|| declaration.content_hash());
438 }
439 parent.clone_from(guard.parent());
440 }
441
442 declarations.len().hash(&mut hasher);
443 for declaration in declarations {
444 declaration.hash(&mut hasher);
445 }
446 hasher.finish()
447 }
448
449 pub fn lookup_local(&self, name: &Name) -> Option<DeclarationPtr> {
453 self.table.get(name).cloned()
454 }
455
456 pub fn lookup(&self, name: &Name) -> Option<DeclarationPtr> {
460 self.lookup_local(name).or_else(|| {
461 self.parent
462 .as_ref()
463 .and_then(|parent| parent.read().lookup(name))
464 })
465 }
466
467 pub fn insert(&mut self, declaration: DeclarationPtr) -> Option<()> {
471 self.ensure_local_binding_hashes_current();
472 let name = declaration.name().clone();
473 let contribution = Self::binding_contribution(&name, &declaration);
474 if let Entry::Vacant(e) = self.table.entry(name) {
475 self.local_bindings_xor ^= contribution;
476 e.insert(declaration);
477 self.invalidate_context_hash_cache();
478 Some(())
479 } else {
480 None
481 }
482 }
483
484 pub fn update_insert(&mut self, declaration: DeclarationPtr) {
486 self.ensure_local_binding_hashes_current();
487 let name = declaration.name().clone();
488 if let Some(existing) = self.table.get(&name).cloned() {
489 self.xor_binding_out(&name, &existing);
490 }
491 self.xor_binding_in(&name, &declaration);
492 self.table.insert(name, declaration);
493 self.invalidate_context_hash_cache();
494 }
495
496 pub fn return_type(&self, name: &Name) -> Option<ReturnType> {
498 self.lookup(name).map(|x| x.return_type())
499 }
500
501 pub fn return_type_local(&self, name: &Name) -> Option<ReturnType> {
503 self.lookup_local(name).map(|x| x.return_type())
504 }
505
506 pub fn domain(&self, name: &Name) -> Option<DomainPtr> {
511 if let Name::WithRepresentation(name, _) = name {
512 self.lookup(name)?.domain()
513 } else {
514 self.lookup(name)?.domain()
515 }
516 }
517
518 pub fn resolve_domain(&self, name: &Name) -> Option<Moo<GroundDomain>> {
522 self.domain(name)?.resolve().ok()
523 }
524
525 pub fn into_iter_local(self) -> impl Iterator<Item = (Name, DeclarationPtr)> {
527 self.table.into_iter()
528 }
529
530 pub fn iter_local(&self) -> impl Iterator<Item = (&Name, &DeclarationPtr)> {
532 self.table.iter()
533 }
534
535 pub fn iter_local_mut(&mut self) -> impl Iterator<Item = (&Name, &mut DeclarationPtr)> {
537 self.table.iter_mut()
538 }
539
540 pub fn extend(&mut self, other: SymbolTable) {
543 self.ensure_local_binding_hashes_current();
544 self.next_machine_name = self.next_machine_name.max(other.next_machine_name);
545
546 for (name, declaration) in &other.table {
547 if let Some(existing) = self.table.get(name).cloned() {
548 self.xor_binding_out(name, &existing);
549 }
550 self.xor_binding_in(name, declaration);
551 }
552 self.table.extend(other.table);
553 self.invalidate_context_hash_cache();
554 }
555
556 pub(crate) fn retain_local_changes_from(&mut self, baseline: &SymbolTable) {
562 self.table.retain(|name, declaration| {
563 baseline
564 .table
565 .get(name)
566 .is_none_or(|old| !declaration.content_eq(old))
567 });
568 self.recompute_local_bindings_xor();
569 self.invalidate_context_hash_cache();
570 }
571
572 pub fn gen_find(&mut self, domain: &DomainPtr) -> DeclarationPtr {
577 let decl = DeclarationPtr::new_find(self.gen_sym(), domain.clone());
578 self.insert(decl.clone());
579 decl
580 }
581
582 pub fn gen_find_auxiliary(&mut self, domain: &DomainPtr) -> DeclarationPtr {
586 let decl = DeclarationPtr::new_find_auxiliary(self.gen_sym(), domain.clone());
587 self.insert(decl.clone());
588 decl
589 }
590
591 pub fn gen_sym(&mut self) -> Name {
593 let num = self.next_machine_name;
594 self.next_machine_name += 1;
595 Name::Machine(num)
596 }
597
598 pub fn parent_mut_unchecked(&mut self) -> &mut Option<SymbolTablePtr> {
602 &mut self.parent
603 }
604
605 pub fn parent(&self) -> &Option<SymbolTablePtr> {
607 &self.parent
608 }
609
610 pub fn get_representation(
616 &self,
617 name: &Name,
618 representation: &[ReprId],
619 ) -> Option<Vec<Box<dyn Representation>>> {
620 let decl = self.lookup(name)?;
632 let var = &decl.as_find()?;
633
634 var.representations
635 .iter()
636 .find(|x| &x.iter().map(|r| r.repr_id()).collect_vec()[..] == representation)
637 .cloned()
638 }
639
640 pub fn representations_for(&self, name: &Name) -> Option<Vec<Vec<Box<dyn Representation>>>> {
646 let decl = self.lookup(name)?;
647 decl.as_find().map(|x| x.representations.clone())
648 }
649}
650
651impl IntoIterator for SymbolTable {
652 type Item = (Name, DeclarationPtr);
653
654 type IntoIter = SymbolTableIter;
655
656 fn into_iter(self) -> Self::IntoIter {
658 SymbolTableIter {
659 inner: self.table.into_iter(),
660 parent: self.parent,
661 }
662 }
663}
664
665pub struct SymbolTableIter {
667 inner: indexmap::map::IntoIter<Name, DeclarationPtr>,
669
670 parent: Option<SymbolTablePtr>,
672}
673
674impl Iterator for SymbolTableIter {
675 type Item = (Name, DeclarationPtr);
676
677 fn next(&mut self) -> Option<Self::Item> {
678 let mut val = self.inner.next();
679
680 while val.is_none() {
684 let parent = self.parent.clone()?;
685
686 let guard = parent.read();
687 self.inner = guard.table.clone().into_iter();
688 self.parent.clone_from(&guard.parent);
689
690 val = self.inner.next();
691 }
692
693 val
694 }
695}
696
697impl Default for SymbolTable {
698 fn default() -> Self {
699 Self::new_inner(None)
700 }
701}
702
703impl Uniplate for SymbolTable {
706 fn uniplate(&self) -> (Tree<Self>, Box<dyn Fn(Tree<Self>) -> Self>) {
707 let self2 = self.clone();
709 (Tree::Zero, Box::new(move |_| self2.clone()))
710 }
711}
712
713impl Biplate<SymbolTablePtr> for SymbolTable {
714 fn biplate(
715 &self,
716 ) -> (
717 Tree<SymbolTablePtr>,
718 Box<dyn Fn(Tree<SymbolTablePtr>) -> Self>,
719 ) {
720 let self2 = self.clone();
721 (Tree::Zero, Box::new(move |_| self2.clone()))
722 }
723}
724
725impl Biplate<Expression> for SymbolTable {
726 fn biplate(&self) -> (Tree<Expression>, Box<dyn Fn(Tree<Expression>) -> Self>) {
727 let (child_trees, ctxs): (VecDeque<_>, Vec<_>) = self
728 .table
729 .values()
730 .map(Biplate::<Expression>::biplate)
731 .unzip();
732
733 let tree = Tree::Many(child_trees);
734
735 let self2 = self.clone();
736 let ctx = Box::new(move |tree| {
737 let Tree::Many(exprs) = tree else {
738 panic!("unexpected children structure");
739 };
740
741 let mut self3 = self2.clone();
742 let self3_iter = self3.table.iter_mut();
743 for (ctx, tree, (_, decl)) in izip!(&ctxs, exprs, self3_iter) {
744 *decl = ctx(tree)
747 }
748
749 self3
750 });
751
752 (tree, ctx)
753 }
754}
755
756impl Biplate<Comprehension> for SymbolTable {
757 fn biplate(
758 &self,
759 ) -> (
760 Tree<Comprehension>,
761 Box<dyn Fn(Tree<Comprehension>) -> Self>,
762 ) {
763 let (expr_tree, expr_ctx) = <SymbolTable as Biplate<Expression>>::biplate(self);
764
765 let (exprs, recons_expr_tree) = expr_tree.list();
766
767 let (comprehension_tree, comprehension_ctx) =
768 <VecDeque<Expression> as Biplate<Comprehension>>::biplate(&exprs);
769
770 let ctx = Box::new(move |x| {
771 let exprs = comprehension_ctx(x);
773
774 let expr_tree = recons_expr_tree(exprs);
776
777 expr_ctx(expr_tree)
779 });
780
781 (comprehension_tree, ctx)
782 }
783}
784
785impl Biplate<Model> for SymbolTable {
786 fn biplate(&self) -> (Tree<Model>, Box<dyn Fn(Tree<Model>) -> Self>) {
788 let (expr_tree, expr_ctx) = <SymbolTable as Biplate<Expression>>::biplate(self);
789
790 let (exprs, recons_expr_tree) = expr_tree.list();
791
792 let (submodel_tree, submodel_ctx) =
793 <VecDeque<Expression> as Biplate<Model>>::biplate(&exprs);
794
795 let ctx = Box::new(move |x| {
796 let exprs = submodel_ctx(x);
798
799 let expr_tree = recons_expr_tree(exprs);
801
802 expr_ctx(expr_tree)
804 });
805 (submodel_tree, ctx)
806 }
807}
808
809#[cfg(test)]
810mod tests {
811 use std::hash::{DefaultHasher, Hash, Hasher};
812
813 use super::*;
814 use crate::ast::{Domain, Range, Reference};
815
816 #[test]
817 fn reference_observes_in_place_domain_update() {
818 let old_domain = Domain::int(vec![Range::Bounded(1, 3)]);
819 let new_domain = Domain::int(vec![Range::Bounded(1, 2)]);
820 let mut declaration = DeclarationPtr::new_find(Name::user("x"), old_domain);
821 let reference = Reference::new(declaration.clone());
822 let original_id = reference.id();
823
824 declaration.as_find_mut().unwrap().domain = new_domain.clone();
825
826 assert_eq!(reference.id(), original_id);
827 assert_eq!(reference.domain(), Some(new_domain));
828 }
829
830 #[test]
831 fn context_hash_observes_shared_declaration_update() {
832 let mut symbols = SymbolTable::new();
833 let mut declaration =
834 DeclarationPtr::new_find(Name::user("x"), Domain::int(vec![Range::Bounded(1, 3)]));
835 symbols.insert(declaration.clone()).unwrap();
836 let before = symbols.context_hash();
837
838 declaration.as_find_mut().unwrap().domain = Domain::int(vec![Range::Bounded(1, 2)]);
839
840 assert_ne!(symbols.context_hash(), before);
841 }
842
843 #[test]
844 fn child_context_hash_observes_parent_declaration_update() {
845 let parent = SymbolTablePtr::new();
846 let mut declaration =
847 DeclarationPtr::new_find(Name::user("x"), Domain::int(vec![Range::Bounded(1, 3)]));
848 parent.write().insert(declaration.clone()).unwrap();
849 let child = SymbolTable::with_parent(parent);
850 let before = child.context_hash();
851
852 declaration.as_find_mut().unwrap().domain = Domain::int(vec![Range::Bounded(1, 2)]);
853
854 assert_ne!(child.context_hash(), before);
855 }
856
857 #[test]
858 fn symbol_table_pointer_equality_and_hash_are_identity_based() {
859 let left = SymbolTablePtr::new();
860 let right = SymbolTablePtr::new();
861 assert_ne!(left, right);
862
863 let mut left_hasher = DefaultHasher::new();
864 left.hash(&mut left_hasher);
865 let mut clone_hasher = DefaultHasher::new();
866 left.clone().hash(&mut clone_hasher);
867 assert_eq!(left_hasher.finish(), clone_hasher.finish());
868 }
869}