Skip to main content

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}