lua_types/trace_impls.rs
1//! Phase-D `Trace` implementations for types defined in this crate.
2//!
3//! Each impl enumerates the type's GC-bearing fields and either calls
4//! `field.trace(m)` (delegating to the field's own `Trace` impl) or
5//! `m.mark(field)` (when the field is a `Gc<T>` from `lua-gc`). During the
6//! Phase A/B/C/D-0 window `GcRef<T>` is still an `Rc<T>` newtype rather
7//! than the real `Gc<T>`, so the mark-queue path is not yet reachable —
8//! method resolution dispatches through `Deref` to each underlying type's
9//! own `trace` method.
10
11use crate::closure::{LuaCClosure, LuaClosure, LuaLClosure};
12use crate::gc::GcRef;
13use crate::proto::LuaProto;
14use crate::string::LuaString;
15use crate::table::LuaTable;
16use crate::upval::UpVal;
17use crate::userdata::LuaUserData;
18use crate::value::LuaThread;
19use crate::value::LuaValue;
20use lua_gc::{Marker, Trace};
21
22/// Forwarder for `GcRef<T>`. Now that `GcRef` wraps a real `lua_gc::Gc<T>`
23/// (D-1e), tracing must enqueue the box onto the gray queue via
24/// `Marker::mark` — that is what flips its header color from White to Gray
25/// and ultimately to Black during gray-queue drainage. The previous
26/// `try_visit` short-circuit was a Phase A-D-0 workaround for the
27/// `Rc`-backed handle (no header, no color), and produced a silent bug
28/// post-D-1e: every GC-tracked allocation stayed White and was freed in
29/// the sweep on the first `collectgarbage()`. Cycles are now handled
30/// natively by the heap's gray-queue (Color::Gray check in `mark` makes
31/// re-visits idempotent).
32impl<T: Trace + 'static> Trace for GcRef<T> {
33 fn trace(&self, m: &mut Marker) {
34 m.mark(self.0);
35 }
36}
37
38/// LuaValue — central enum. Variants Nil/Bool/Int/Float/LightUserData carry
39/// no GC; Str/Table/Function/UserData/Thread carry collectable payloads.
40impl Trace for LuaValue {
41
42 fn type_name(&self) -> &'static str {
43 std::any::type_name::<Self>()
44 }
45
46 fn trace(&self, m: &mut Marker) {
47 match self {
48 LuaValue::Nil
49 | LuaValue::Bool(_)
50 | LuaValue::Int(_)
51 | LuaValue::Float(_)
52 | LuaValue::LightUserData(_) => {}
53 LuaValue::Str(s) => s.trace(m),
54 LuaValue::Table(t) => t.trace(m),
55 LuaValue::Function(c) => c.trace(m),
56 LuaValue::UserData(u) => {
57 u.trace(m);
58 }
59 LuaValue::Thread(t) => {
60 // Mark the thread identity itself. lua-vm's GC post-mark hook
61 // uses the visited identities to trace only reachable
62 // suspended LuaState stacks.
63 t.trace(m);
64 }
65 }
66 }
67}
68
69/// LuaString — interned byte string. The `Rc<[u8]>` backing buffer is
70/// owned, not GC-managed, so this impl is intentionally empty.
71impl Trace for LuaString {
72
73 fn type_name(&self) -> &'static str {
74 std::any::type_name::<Self>()
75 }
76
77 fn trace(&self, _m: &mut Marker) {}
78}
79
80/// UpVal — Open (refers to a thread stack slot by index) or Closed (owns a
81/// LuaValue). The Open variant carries no direct GC reference; the slot it
82/// points at is traced through the owning thread's stack walk.
83impl Trace for UpVal {
84
85 fn type_name(&self) -> &'static str {
86 std::any::type_name::<Self>()
87 }
88
89 fn trace(&self, m: &mut Marker) {
90 if self.try_open_payload().is_some() {
91 return;
92 }
93 if let Some(v) = self.try_closed_value() {
94 v.trace(m);
95 }
96 }
97}
98
99/// LuaTable — array+hash entries plus optional metatable.
100///
101/// Weak-table semantics (matches `lgc.c::traversetable`):
102/// * `__mode = "v"` — strong keys, weak values. Trace keys here; value
103/// side is deferred — string values get marked in `prune_weak_dead`'s
104/// surviving-entry pass (Lua's `iscleared`), non-string dead values
105/// trigger entry removal.
106/// * `__mode = "kv"` — both sides weak. Trace NEITHER here; everything
107/// is handled by `prune_weak_dead` (matches Lua's "just add to allweak,
108/// traverse nothing" path).
109/// * `__mode = "k"` — weak keys, strong values. Trace NEITHER here. The
110/// post-mark ephemeron convergence pass walks each weak-key table's
111/// entries and marks values only for entries whose keys are
112/// independently reachable. String keys get marked in `prune_weak_dead`.
113/// * No `__mode` — trace both unconditionally.
114///
115/// Marking strings inline for weak slots (the previous behavior) would
116/// pin them alive even when their containing entry is about to be cleared
117/// because the other side died — breaking the `gc.lua` weak-string-key
118/// block, which expects unreferenced long strings to free their bytes
119/// after a single `collectgarbage()` cycle.
120impl Trace for LuaTable {
121
122 fn type_name(&self) -> &'static str {
123 std::any::type_name::<Self>()
124 }
125
126 fn trace(&self, m: &mut Marker) {
127 const WEAK_KEYS: u8 = 1;
128 const WEAK_VALUES: u8 = 1 << 1;
129 let mode = self.weak_mode();
130 let trace_keys = (mode & WEAK_KEYS) == 0;
131 let trace_values = (mode & WEAK_VALUES) == 0 && trace_keys;
132 if trace_keys && trace_values {
133 self.trace_entries_with_clearkey(|v| v.trace(m));
134 } else {
135 self.for_each_entry(|k, v| {
136 if trace_keys {
137 k.trace(m);
138 }
139 if trace_values {
140 v.trace(m);
141 }
142 });
143 }
144 if let Some(mt) = self.metatable() {
145 mt.trace(m);
146 }
147 }
148}
149
150/// LuaProto — bytecode prototype. k (constants), p (child protos),
151/// source, upvalue names, locvar names.
152impl Trace for LuaProto {
153
154 fn type_name(&self) -> &'static str {
155 std::any::type_name::<Self>()
156 }
157
158 fn trace(&self, m: &mut Marker) {
159 for v in self.k.iter() {
160 v.trace(m);
161 }
162 for p in self.p.iter() {
163 p.trace(m);
164 }
165 if let Some(src) = &self.source {
166 src.trace(m);
167 }
168 for uv in self.upvalues.iter() {
169 if let Some(name) = &uv.name {
170 name.trace(m);
171 }
172 }
173 for lv in self.locvars.iter() {
174 lv.varname.trace(m);
175 }
176 if let Some(c) = self.cache.borrow().as_ref() {
177 c.trace(m);
178 }
179 }
180}
181
182/// LuaLClosure — Lua closure carrying a Proto and its captured upvalues.
183impl Trace for LuaLClosure {
184
185 fn type_name(&self) -> &'static str {
186 std::any::type_name::<Self>()
187 }
188
189 fn trace(&self, m: &mut Marker) {
190 self.proto.trace(m);
191 for uv in self.upvals.iter() {
192 uv.get().trace(m);
193 }
194 }
195}
196
197/// LuaClosure — dispatch to Lua/C variants; LightC is a bare function-ptr
198/// index with no payload.
199impl Trace for LuaClosure {
200
201 fn type_name(&self) -> &'static str {
202 std::any::type_name::<Self>()
203 }
204
205 fn trace(&self, m: &mut Marker) {
206 match self {
207 LuaClosure::Lua(l) => l.trace(m),
208 LuaClosure::C(c) => c.trace(m),
209 LuaClosure::LightC(_) => {}
210 }
211 }
212}
213
214/// LuaCClosure — Rust-side C closure carrying captured upvalues.
215impl Trace for LuaCClosure {
216
217 fn type_name(&self) -> &'static str {
218 std::any::type_name::<Self>()
219 }
220
221 fn trace(&self, m: &mut Marker) {
222 for v in self.upvalues.borrow().iter() {
223 v.trace(m);
224 }
225 }
226}
227
228/// LuaUserData — boxed payload + optional metatable + user values.
229impl Trace for LuaUserData {
230
231 fn type_name(&self) -> &'static str {
232 std::any::type_name::<Self>()
233 }
234
235 fn trace(&self, m: &mut Marker) {
236 if let Some(mt) = self.metatable() {
237 mt.trace(m);
238 }
239 for v in self.uv.borrow().iter() {
240 v.trace(m);
241 }
242 }
243}
244
245/// LuaThread — value-side thread identity. Carries only a `ThreadId`
246/// (the registry key); the real per-thread `LuaState` lives in
247/// `lua-vm`'s `GlobalState::threads` map and is traced from
248/// `GlobalState::trace` as a root.
249impl Trace for LuaThread {
250
251 fn type_name(&self) -> &'static str {
252 std::any::type_name::<Self>()
253 }
254
255 fn trace(&self, _m: &mut Marker) {}
256}
257
258// ──────────────────────────────────────────────────────────────────────────────
259// PORT STATUS
260// source: n/a (GC Trace impls scoped to lua-types public surface)
261// target_crate: lua-types
262// confidence: high
263// todos: 0
264// port_notes: 0
265// unsafe_blocks: 0
266// notes: Trace impls for GC visitor over the canonical type set. No C analogue;
267// the C GC walks struct fields directly via macros.
268// ──────────────────────────────────────────────────────────────────────────────