Preserve exchange allocations during atomic replacement and reserve generated aliases

This commit is contained in:
2026-09-05 18:51:11 +03:00
parent 757ab88f53
commit e41b8de992
4 changed files with 404 additions and 37 deletions
+46 -6
View File
@@ -203,6 +203,9 @@ pub fn remap_references(p: &mut Project, old: Id, new: Id) {
}
/// IDs removed by a cascade. Cross-layer traced counterparts are never included.
pub fn deletion_impact(p: &Project, id: Id) -> BTreeSet<Id> {
deletion_impact_inner(p, id, false)
}
fn deletion_impact_inner(p: &Project, id: Id, defer_exchange_allocations: bool) -> BTreeSet<Id> {
let mut removed = BTreeSet::from([id]);
loop {
let before = removed.len();
@@ -254,12 +257,15 @@ pub fn deletion_impact(p: &Project, id: Id) -> BTreeSet<Id> {
else {
continue;
};
if p.relations.values().any(|allocation| {
allocation.kind() == RelationKind::PortAllocation
&& removed.contains(&allocation.id)
&& [boundary.source(), boundary.target()].contains(&allocation.source())
&& [*source_port, *target_port].contains(&allocation.target())
}) {
if !defer_exchange_allocations
&& p.relations.values().any(|allocation| {
allocation.kind() == RelationKind::PortAllocation
&& removed.contains(&allocation.id)
&& [boundary.source(), boundary.target()].contains(&allocation.source())
&& [*source_port, *target_port].contains(&allocation.target())
})
&& crate::ports::missing_exchange_port_allocations(p, r, &removed)
{
removed.insert(r.id);
}
}
@@ -288,6 +294,40 @@ pub fn deletion_impact(p: &Project, id: Id) -> BTreeSet<Id> {
}
pub fn delete_cascade(p: &mut Project, id: Id) {
let removed = deletion_impact(p, id);
remove_objects(p, removed);
}
/// Preserve conditional exchange-allocation dependents until the transaction
/// has had the opportunity to supply explicit replacement allocation facts.
pub(crate) fn delete_cascade_deferred(p: &mut Project, id: Id, pending: &mut BTreeSet<Id>) {
let complete = deletion_impact(p, id);
let removed = deletion_impact_inner(p, id, true);
// An explicitly removed relation cannot pass its conditional cascade on
// to a new relation that later reuses the same identity in this batch.
pending.retain(|id| !removed.contains(id));
pending.extend(
complete
.difference(&removed)
.filter(|id| {
p.relations
.get(id)
.is_some_and(|r| r.kind() == RelationKind::ExchangeAllocation)
})
.copied(),
);
remove_objects(p, removed);
}
pub(crate) fn finish_deletions(p: &mut Project, pending: &BTreeSet<Id>) {
let none = BTreeSet::new();
for id in pending {
if p.relations.get(id).is_some_and(|r| {
r.kind() == RelationKind::ExchangeAllocation
&& crate::ports::missing_exchange_port_allocations(p, r, &none)
}) {
delete_cascade(p, *id);
}
}
}
fn remove_objects(p: &mut Project, removed: BTreeSet<Id>) {
let removed_names: std::collections::BTreeMap<_, _> = removed
.iter()
.map(|id| (*id, p.named(*id).to_string()))