luaur_analysis/functions/prune.rs
1//! Faithful port of `Luau::detail::prune`
2//! (`Analysis/src/TopoSortStatements.cpp:386-401`).
3//!
4//! ```cpp
5//! // Clip arcs to and from the node
6//! void prune(Node* next)
7//! {
8//! for (const auto& node : next->provides)
9//! {
10//! auto it = node->depends.find(next);
11//! LUAU_ASSERT(it != node->depends.end());
12//! node->depends.erase(it);
13//! }
14//!
15//! for (const auto& node : next->depends)
16//! {
17//! auto it = node->provides.find(next);
18//! LUAU_ASSERT(it != node->provides.end());
19//! node->provides.erase(it);
20//! }
21//! }
22//! ```
23use crate::records::node::Node;
24use luaur_common::macros::luau_assert::LUAU_ASSERT;
25
26/// Clip arcs to and from the node.
27///
28/// # Safety
29/// `next` and every pointer reachable through its `provides` / `depends` sets
30/// must be live `Node`s. None of the visited nodes alias `next` (the graph has
31/// no self-edges), so erasing `next` from their sets is sound.
32pub fn prune(next: *mut Node) {
33 // The C++ loops iterate `next->provides` / `next->depends` (which are never
34 // mutated here) while erasing `next` from *other* nodes' sets. Snapshot the
35 // pointer sets first so the mutation of the neighbours cannot alias the
36 // borrow of `next`.
37 let provides: alloc::vec::Vec<*mut Node> =
38 unsafe { (*next).provides.iter().copied().collect() };
39 let depends: alloc::vec::Vec<*mut Node> = unsafe { (*next).depends.iter().copied().collect() };
40
41 for node in provides {
42 let removed = unsafe { (*node).depends.remove(&next) };
43 LUAU_ASSERT!(removed);
44 }
45
46 for node in depends {
47 let removed = unsafe { (*node).provides.remove(&next) };
48 LUAU_ASSERT!(removed);
49 }
50}