Skip to content

Commit 746e90c

Browse files
committed
Merge name_users and name_dependents into a single map
Consolidate two reverse index maps (name_users and name_dependents) into a single name_dependents map using a unified NameDependent enum with Definition, Reference, and Name variants. This reduces the Graph struct from 3 auxiliary maps to 2 and eliminates a class of consistency bugs where two maps keyed on NameId had to be cleaned up in sync. Addresses PR review feedback about multiple hashmaps requiring consistency maintenance.
1 parent a369b08 commit 746e90c

File tree

1 file changed

+61
-67
lines changed

1 file changed

+61
-67
lines changed

rust/rubydex/src/model/graph.rs

Lines changed: 61 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ use crate::model::references::{ConstantReference, MethodRef};
1414
use crate::model::string_ref::StringRef;
1515
use crate::stats;
1616

17-
/// A definition or constant reference that uses a particular `NameId`.
18-
/// Used as the value type in the `name_users` reverse index.
17+
/// An entity whose validity depends on a particular `NameId`.
18+
/// Used as the value type in the `name_dependents` reverse index.
1919
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20-
pub enum NameUser {
20+
pub enum NameDependent {
2121
Definition(DefinitionId),
2222
Reference(ReferenceId),
23+
Name(NameId),
2324
}
2425

2526
/// Items processed by the unified invalidation worklist.
@@ -72,13 +73,9 @@ pub struct Graph {
7273
/// Used during incremental invalidation to find names that need unresolving when a declaration is removed.
7374
declaration_to_names: IdentityHashMap<DeclarationId, IdentityHashSet<NameId>>,
7475

75-
/// Reverse index: for each `NameId`, which definitions and constant references use it.
76-
/// Eliminates O(D+R) scans during invalidation.
77-
name_users: IdentityHashMap<NameId, Vec<NameUser>>,
78-
79-
/// Reverse index: for each `NameId`, which other names depend on it
80-
/// (via nesting or `parent_scope`). Used for cascade invalidation.
81-
name_dependents: IdentityHashMap<NameId, IdentityHashSet<NameId>>,
76+
/// Reverse index: for each `NameId`, what depends on it (definitions, references, and child names).
77+
/// Used during invalidation to efficiently find affected entities without scanning the full graph.
78+
name_dependents: IdentityHashMap<NameId, Vec<NameDependent>>,
8279

8380
/// Accumulated work items from update/delete operations.
8481
/// Drained by `take_pending_work()` before resolution.
@@ -98,7 +95,6 @@ impl Graph {
9895
method_references: IdentityHashMap::default(),
9996
position_encoding: Encoding::default(),
10097
declaration_to_names: IdentityHashMap::default(),
101-
name_users: IdentityHashMap::default(),
10298
name_dependents: IdentityHashMap::default(),
10399
pending_work: Vec::default(),
104100
}
@@ -578,19 +574,13 @@ impl Graph {
578574
let nesting_id = name_ref.nesting().as_ref().copied();
579575
let parent_scope_id = name_ref.parent_scope().as_ref().copied();
580576

581-
// Remove this name from name_dependents of its nesting and parent_scope names
577+
// Remove this name from its nesting/parent_scope owners' dependent lists
582578
for dep_owner_id in [nesting_id, parent_scope_id].into_iter().flatten() {
583-
if let Some(deps) = self.name_dependents.get_mut(&dep_owner_id) {
584-
deps.remove(&name_id);
585-
if deps.is_empty() {
586-
self.name_dependents.remove(&dep_owner_id);
587-
}
588-
}
579+
self.remove_name_dependent(dep_owner_id, NameDependent::Name(name_id));
589580
}
590581
}
591582

592583
self.name_dependents.remove(&name_id);
593-
self.name_users.remove(&name_id);
594584
self.names.remove(&name_id);
595585
}
596586

@@ -768,21 +758,29 @@ impl Graph {
768758
Entry::Vacant(entry) => {
769759
// Track nesting and parent_scope dependents for new names only
770760
if let Some(nesting_id) = entry.insert(name_ref).nesting() {
771-
self.name_dependents.entry(*nesting_id).or_default().insert(name_id);
761+
let deps = self.name_dependents.entry(*nesting_id).or_default();
762+
let dep = NameDependent::Name(name_id);
763+
if !deps.contains(&dep) {
764+
deps.push(dep);
765+
}
772766
}
773767
if let Some(parent_id) = self.names.get(&name_id).unwrap().parent_scope().as_ref() {
774-
self.name_dependents.entry(*parent_id).or_default().insert(name_id);
768+
let deps = self.name_dependents.entry(*parent_id).or_default();
769+
let dep = NameDependent::Name(name_id);
770+
if !deps.contains(&dep) {
771+
deps.push(dep);
772+
}
775773
}
776774
}
777775
}
778776
}
779777

780778
for (definition_id, definition) in definitions {
781779
if let Some(name_id) = definition.name_id() {
782-
self.name_users
780+
self.name_dependents
783781
.entry(*name_id)
784782
.or_default()
785-
.push(NameUser::Definition(definition_id));
783+
.push(NameDependent::Definition(definition_id));
786784
}
787785

788786
if self.definitions.insert(definition_id, definition).is_some() {
@@ -793,10 +791,10 @@ impl Graph {
793791
}
794792

795793
for (constant_ref_id, constant_ref) in constant_references {
796-
self.name_users
794+
self.name_dependents
797795
.entry(*constant_ref.name_id())
798796
.or_default()
799-
.push(NameUser::Reference(constant_ref_id));
797+
.push(NameDependent::Reference(constant_ref_id));
800798

801799
work.push(Unit::ConstantRef(constant_ref_id));
802800

@@ -907,7 +905,7 @@ impl Graph {
907905
declaration.remove_reference(ref_id);
908906
}
909907

910-
self.remove_name_user(*constant_ref.name_id(), NameUser::Reference(*ref_id));
908+
self.remove_name_dependent(*constant_ref.name_id(), NameDependent::Reference(*ref_id));
911909
self.untrack_name(*constant_ref.name_id());
912910
}
913911
}
@@ -919,19 +917,19 @@ impl Graph {
919917

920918
let definition = self.definitions.remove(def_id).unwrap();
921919
if let Some(name_id) = definition.name_id() {
922-
self.remove_name_user(*name_id, NameUser::Definition(*def_id));
920+
self.remove_name_dependent(*name_id, NameDependent::Definition(*def_id));
923921
}
924922
self.untrack_definition_strings(&definition);
925923
}
926924
}
927925

928-
/// Removes a specific user from the `name_users` entry for `name_id`, cleaning up the entry
929-
/// if no users remain.
930-
fn remove_name_user(&mut self, name_id: NameId, user: NameUser) {
931-
if let Some(users) = self.name_users.get_mut(&name_id) {
932-
users.retain(|u| *u != user);
933-
if users.is_empty() {
934-
self.name_users.remove(&name_id);
926+
/// Removes a specific dependent from the `name_dependents` entry for `name_id`,
927+
/// cleaning up the entry if no dependents remain.
928+
fn remove_name_dependent(&mut self, name_id: NameId, dependent: NameDependent) {
929+
if let Some(deps) = self.name_dependents.get_mut(&name_id) {
930+
deps.retain(|d| *d != dependent);
931+
if deps.is_empty() {
932+
self.name_dependents.remove(&name_id);
935933
}
936934
}
937935
}
@@ -987,13 +985,15 @@ impl Graph {
987985
}
988986
}
989987

990-
// Unresolve names resolved to this declaration, queue their dependents
988+
// Unresolve names resolved to this declaration, queue their Name dependents
991989
if let Some(name_set) = self.declaration_to_names.remove(&decl_id) {
992990
for name_id in name_set {
993991
self.unresolve_name(name_id);
994992
if let Some(deps) = self.name_dependents.get(&name_id) {
995993
for dep in deps {
996-
queue.push(InvalidationItem::Name(*dep));
994+
if let NameDependent::Name(dep_name_id) = dep {
995+
queue.push(InvalidationItem::Name(*dep_name_id));
996+
}
997997
}
998998
}
999999
}
@@ -1047,12 +1047,14 @@ impl Graph {
10471047

10481048
work.push(Unit::Ancestors(decl_id));
10491049

1050-
// Queue dependents of resolved names (skipping seeds themselves)
1050+
// Queue Name dependents of resolved names (skipping seeds themselves)
10511051
if let Some(name_set) = self.declaration_to_names.get(&decl_id) {
10521052
for seed_name_id in name_set {
10531053
if let Some(deps) = self.name_dependents.get(seed_name_id) {
10541054
for dep in deps {
1055-
queue.push(InvalidationItem::Name(*dep));
1055+
if let NameDependent::Name(dep_name_id) = dep {
1056+
queue.push(InvalidationItem::Name(*dep_name_id));
1057+
}
10561058
}
10571059
}
10581060
}
@@ -1076,31 +1078,31 @@ impl Graph {
10761078
return;
10771079
}
10781080

1079-
// Always propagate to dependents
1080-
if let Some(deps) = self.name_dependents.get(&name_id) {
1081-
for dep in deps {
1082-
queue.push(InvalidationItem::Name(*dep));
1081+
let dependents: Vec<NameDependent> = self.name_dependents.get(&name_id).cloned().unwrap_or_default();
1082+
1083+
// Always propagate to Name dependents
1084+
for dep in &dependents {
1085+
if let NameDependent::Name(dep_name_id) = dep {
1086+
queue.push(InvalidationItem::Name(*dep_name_id));
10831087
}
10841088
}
10851089

10861090
if self.has_unresolved_dependency(name_id) {
10871091
// Structural cascade: the name's resolution is invalid
10881092
if let Some(old_decl_id) = self.unresolve_name(name_id) {
1089-
let users: Vec<NameUser> = self.name_users.get(&name_id).cloned().unwrap_or_default();
1090-
1091-
for user in users {
1092-
match user {
1093-
NameUser::Reference(ref_id) => {
1093+
for dep in &dependents {
1094+
match dep {
1095+
NameDependent::Reference(ref_id) => {
10941096
if let Some(decl) = self.declarations.get_mut(&old_decl_id) {
1095-
decl.remove_reference(&ref_id);
1097+
decl.remove_reference(ref_id);
10961098
}
1097-
work.push(Unit::ConstantRef(ref_id));
1099+
work.push(Unit::ConstantRef(*ref_id));
10981100
}
1099-
NameUser::Definition(def_id) => {
1100-
work.push(Unit::Definition(def_id));
1101+
NameDependent::Definition(def_id) => {
1102+
work.push(Unit::Definition(*def_id));
11011103

11021104
if let Some(decl) = self.declarations.get_mut(&old_decl_id) {
1103-
decl.remove_definition(&def_id);
1105+
decl.remove_definition(def_id);
11041106
}
11051107

11061108
if self
@@ -1111,29 +1113,21 @@ impl Graph {
11111113
queue.push(InvalidationItem::Declaration(old_decl_id));
11121114
}
11131115
}
1116+
NameDependent::Name(_) => {} // already propagated above
11141117
}
11151118
}
11161119
}
11171120
} else {
11181121
// Ancestor change only: re-evaluate constant references under this name
1119-
let ref_ids: Vec<ReferenceId> = self
1120-
.name_users
1121-
.get(&name_id)
1122-
.into_iter()
1123-
.flatten()
1124-
.filter_map(|u| match u {
1125-
NameUser::Reference(id) => Some(*id),
1126-
NameUser::Definition(_) => None,
1127-
})
1128-
.collect();
1129-
11301122
let is_resolved = matches!(self.names.get(&name_id), Some(NameRef::Resolved(_)));
11311123

1132-
for ref_id in ref_ids {
1133-
if is_resolved {
1134-
self.unresolve_reference(ref_id);
1124+
for dep in &dependents {
1125+
if let NameDependent::Reference(ref_id) = dep {
1126+
if is_resolved {
1127+
self.unresolve_reference(*ref_id);
1128+
}
1129+
work.push(Unit::ConstantRef(*ref_id));
11351130
}
1136-
work.push(Unit::ConstantRef(ref_id));
11371131
}
11381132
}
11391133
}

0 commit comments

Comments
 (0)