1use std::collections::BTreeSet;
23
24use crate::env::Env;
25use crate::heap::{Address, Heap};
26use crate::value::Value;
27
28enum Lookup<'a> {
30 Dangling,
31 Found(&'a Value),
32}
33
34fn lookup(heap: &Heap, address: Address) -> Lookup<'_> {
35 heap.load(address).map_or(Lookup::Dangling, Lookup::Found)
36}
37
38#[must_use]
41pub fn reachable(roots: &[&Value], env: &Env, heap: &Heap) -> BTreeSet<Address> {
42 let from_env = mark_env(env, heap, BTreeSet::new());
43 roots
44 .iter()
45 .fold(from_env, |acc, &value| mark_value(value, heap, acc))
46}
47
48#[must_use]
53pub fn collect(roots: &[&Value], env: &Env, heap: &Heap) -> Heap {
54 heap.retain(&reachable(roots, env, heap))
55}
56
57fn mark_env(env: &Env, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
58 match env {
59 Env::Empty => live,
60 Env::Cons { value, rest, .. } => {
61 let live = mark_value(value, heap, live);
62 mark_env(rest, heap, live)
63 }
64 }
65}
66
67fn mark_value(value: &Value, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
68 match value {
69 Value::Closure { env: capture, .. } => mark_env(capture, heap, live),
70 Value::Ref(address) => mark_reference(*address, heap, live),
71 }
72}
73
74fn mark_reference(address: Address, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
75 if live.contains(&address) {
76 live
77 } else {
78 let extended: BTreeSet<Address> =
79 live.into_iter().chain(std::iter::once(address)).collect();
80 match lookup(heap, address) {
81 Lookup::Dangling => extended,
82 Lookup::Found(value) => mark_value(value, heap, extended),
83 }
84 }
85}