• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 13615020054

02 Mar 2025 11:34AM UTC coverage: 47.089% (+0.03%) from 47.062%
13615020054

Pull #310

github

web-flow
Merge f80ed61d2 into f1a605a0f
Pull Request #310: support escaped identifiers

93 of 138 new or added lines in 4 files covered. (67.39%)

4 existing lines in 2 files now uncovered.

12737 of 27049 relevant lines covered (47.09%)

422185.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

46.92
/crates/steel-core/src/rvals/cycles.rs
1
use crate::gc::shared::{MutableContainer, ShareableMut};
2
use crate::steel_vm::{builtin::get_function_name, vm::Continuation, vm::ContinuationMark};
3
use crate::values::lists::Pair;
4
use num::BigInt;
5
use std::{cell::Cell, collections::VecDeque};
6

7
use super::*;
8

9
#[derive(Default)]
10
// Keep track of any reference counted values that are visited, in a pointer
11
pub(super) struct CycleDetector {
12
    // Recording things that have already been seen
13
    cycles: fxhash::FxHashMap<usize, usize>,
14

15
    // Values captured in cycles
16
    values: Vec<SteelVal>,
17

18
    depth: usize,
19
}
20

21
/// Specifies how to format for the `format_with_cycles` function.
22
#[derive(PartialEq)]
23
enum FormatType {
24
    Normal,
25
    TopLevel,
26
}
27

28
impl CycleDetector {
29
    pub(super) fn detect_and_display_cycles(val: &SteelVal, f: &mut fmt::Formatter) -> fmt::Result {
2,301✔
30
        // Consider using one shared queue here
31
        let mut queue = Vec::new();
2,301✔
32

33
        let mut bfs_detector = CycleCollector {
34
            visited: fxhash::FxHashSet::default(),
2,301✔
35
            cycles: fxhash::FxHashMap::default(),
2,301✔
36
            values: Vec::new(),
2,301✔
37
            queue: &mut queue,
2,301✔
38
            found_mutable: false,
39
        };
40

41
        bfs_detector.push_back(val.clone());
2,301✔
42

43
        bfs_detector.visit();
2,301✔
44

45
        CycleDetector {
46
            cycles: bfs_detector.cycles,
2,301✔
47
            values: bfs_detector.values,
2,301✔
48
            depth: 0,
49
        }
50
        .start_format(val, f)
2,301✔
51
    }
52

53
    fn start_format(mut self, val: &SteelVal, f: &mut fmt::Formatter) -> fmt::Result {
2,301✔
54
        for node in std::mem::take(&mut self.values) {
2,301✔
55
            let id = match &node {
×
56
                SteelVal::CustomStruct(c) => {
×
57
                    let ptr_addr = c.as_ptr() as usize;
×
58
                    self.cycles.get(&ptr_addr).unwrap()
×
59
                }
60
                SteelVal::HeapAllocated(b) => {
×
61
                    // Get the object that THIS points to
62
                    let ptr_addr = b.get().as_ptr_usize().unwrap();
×
63
                    self.cycles.get(&ptr_addr).unwrap()
×
64
                }
65
                SteelVal::ListV(l) => {
×
66
                    let ptr_addr = l.as_ptr_usize();
×
67

68
                    self.cycles.get(&ptr_addr).unwrap()
×
69
                }
70
                SteelVal::VectorV(l) => {
×
71
                    let ptr_addr = l.0.as_ptr() as usize;
×
72

73
                    self.cycles.get(&ptr_addr).unwrap()
×
74
                }
75
                SteelVal::HashMapV(l) => {
×
76
                    let ptr_addr = l.0.as_ptr() as usize;
×
77

78
                    self.cycles.get(&ptr_addr).unwrap()
×
79
                }
80
                SteelVal::HashSetV(l) => {
×
81
                    let ptr_addr = l.0.as_ptr() as usize;
×
82

83
                    self.cycles.get(&ptr_addr).unwrap()
×
84
                }
85
                SteelVal::Custom(l) => {
×
86
                    let ptr_addr = l.as_ptr() as usize;
×
87

88
                    self.cycles.get(&ptr_addr).unwrap()
×
89
                }
90
                SteelVal::Boxed(b) => {
×
91
                    let ptr_addr = b.as_ptr() as usize;
×
92

93
                    self.cycles.get(&ptr_addr).unwrap()
×
94
                }
95
                SteelVal::SyntaxObject(s) => {
×
96
                    let ptr_addr = s.as_ptr() as usize;
×
97

98
                    self.cycles.get(&ptr_addr).unwrap()
×
99
                }
100
                SteelVal::MutableVector(v) => {
×
101
                    let ptr_addr = v.as_ptr_usize();
×
102

103
                    self.cycles.get(&ptr_addr).unwrap()
×
104
                }
105
                _ => {
106
                    unreachable!()
107
                }
108
            };
109

110
            write!(f, "#{id}=")?;
×
111
            self.format_with_cycles(&node, f, FormatType::TopLevel)?;
×
112
            writeln!(f)?;
×
113
        }
114

115
        if !self.values.contains(val) {
2,301✔
116
            self.format_with_cycles(val, f, FormatType::Normal)?;
2,301✔
117
        }
118

119
        Ok(())
2,301✔
120
    }
121

122
    fn format_with_cycles(
2,803✔
123
        &mut self,
124
        val: &SteelVal,
125
        f: &mut fmt::Formatter,
126
        format_type: FormatType,
127
    ) -> fmt::Result {
128
        self.depth += 1;
2,803✔
129

130
        if self.depth > 128 {
2,803✔
131
            return write!(f, "...");
×
132
        }
133

134
        let res = match val {
2,803✔
135
            BoolV(b) => write!(f, "#{b}"),
90✔
136
            NumV(x) => write!(f, "{}", RealLiteral::Float(*x)),
26✔
137
            IntV(x) => write!(f, "{x}"),
684✔
138
            BigNum(b) => write!(f, "{}", b.as_ref()),
×
139
            Rational(x) => write!(f, "{n}/{d}", n = x.numer(), d = x.denom()),
4✔
140
            BigRational(x) => write!(f, "{n}/{d}", n = x.numer(), d = x.denom()),
2✔
141
            Complex(x) => write!(f, "{}", x.as_ref()),
×
142
            StringV(s) => write!(f, "{s:?}"),
91✔
143
            ByteVector(b) => {
2✔
144
                write!(f, "#u8(")?;
2✔
145

146
                let bytes = b.vec.read();
2✔
147
                let mut iter = bytes.iter();
2✔
148

149
                if let Some(last) = iter.next_back() {
2✔
150
                    for byte in iter {
2✔
151
                        write!(f, "#x{byte:02X} ")?;
×
152
                    }
153

154
                    write!(f, "#x{last:02X}")?;
2✔
155
                }
156

157
                write!(f, ")")
2✔
158
            }
159
            CharV(c) => {
1,401✔
160
                if c.is_ascii_control() {
1,401✔
161
                    write!(f, "{}", c)
1,400✔
162
                } else {
163
                    write!(f, "#\\{c}")
1✔
164
                }
165
            }
166
            Pair(p) => {
9✔
167
                write!(f, "(")?;
9✔
168
                self.format_with_cycles(&p.car, f, FormatType::Normal)?;
9✔
169
                write!(f, " . ")?;
9✔
170
                self.format_with_cycles(&p.cdr, f, FormatType::Normal)?;
9✔
171
                write!(f, ")")
9✔
172
            }
173
            FuncV(func) => {
4✔
174
                if let Some(name) = get_function_name(*func) {
8✔
175
                    write!(f, "#<function:{}>", name.name)
176
                } else {
177
                    write!(f, "#<function>")
×
178
                }
179
            }
180
            Void => write!(f, "#<void>"),
25✔
181
            SymbolV(s) => write!(f, "{s}"),
232✔
182
            VectorV(lst) => {
×
183
                let mut iter = lst.iter();
×
184
                write!(f, "#(")?;
×
185

186
                if let Some(last) = iter.next_back() {
×
187
                    for item in iter {
×
188
                        self.format_with_cycles(item, f, FormatType::Normal)?;
×
189
                        write!(f, " ")?;
×
190
                    }
191
                    self.format_with_cycles(last, f, FormatType::Normal)?;
×
192
                }
193
                write!(f, ")")
×
194
            }
195
            // TODO: Somehow getting an already borrowed error here on 208
196
            Custom(x) => match format_type {
×
197
                FormatType::Normal => write!(
×
198
                    f,
×
199
                    "{}",
200
                    x.try_read()
×
201
                        .map(|x| x.display())
×
202
                        .unwrap_or(Ok(format!("#<{:p}>", x)))?
×
203
                ),
204
                FormatType::TopLevel => write!(f, "#<{}>", x.read().display()?),
×
205
            },
206
            CustomStruct(s) => match format_type {
4✔
207
                FormatType::Normal => {
208
                    if let Some(id) = self.cycles.get(&(s.as_ptr() as usize)) {
4✔
209
                        write!(f, "#{id}#")
210
                    } else {
211
                        let guard = s;
4✔
212

213
                        {
214
                            if s.get(&SteelVal::SymbolV(SteelString::from("#:transparent")))
4✔
215
                                .and_then(|x| x.as_bool())
12✔
216
                                .unwrap_or_default()
217
                            {
218
                                write!(f, "({}", guard.name())?;
1✔
219

220
                                for i in guard.fields.iter() {
4✔
221
                                    write!(f, " ")?;
3✔
222
                                    self.format_with_cycles(i, f, FormatType::Normal)?;
3✔
223
                                }
224

225
                                write!(f, ")")
1✔
226
                            } else {
227
                                write!(f, "({})", guard.name())
3✔
228
                            }
229
                        }
230
                    }
231
                }
232
                FormatType::TopLevel => {
233
                    let guard = s;
×
234
                    {
235
                        if guard
×
236
                            .get(&SteelVal::SymbolV(SteelString::from("#:transparent")))
×
237
                            .and_then(|x| x.as_bool())
×
238
                            .unwrap_or_default()
239
                        {
240
                            write!(f, "({}", guard.name())?;
×
241

242
                            for i in guard.fields.iter() {
×
243
                                write!(f, " ")?;
×
244
                                self.format_with_cycles(i, f, FormatType::Normal)?;
×
245
                            }
246

247
                            write!(f, ")")
×
248
                        } else {
249
                            write!(f, "({})", guard.name())
×
250
                        }
251
                    }
252
                }
253
            },
254
            PortV(_) => write!(f, "#<port>"),
×
255
            Closure(_) => write!(f, "#<bytecode-closure>"),
17✔
256
            HashMapV(hm) => write!(f, "#<hashmap {:#?}>", hm.as_ref()),
1✔
257
            IterV(_) => write!(f, "#<iterator>"),
×
258
            HashSetV(hs) => write!(f, "#<hashset {:?}>", hs.0),
×
259
            FutureFunc(_) => write!(f, "#<future-func>"),
×
260
            FutureV(_) => write!(f, "#<future>"),
×
261
            StreamV(_) => write!(f, "#<stream>"),
6✔
262
            BoxedFunction(b) => {
×
263
                if let Some(name) = b.name() {
×
264
                    write!(f, "#<function:{}>", name)
265
                } else {
266
                    write!(f, "#<function>")
×
267
                }
268
            }
269
            ContinuationFunction(_) => write!(f, "#<continuation>"),
×
270
            // #[cfg(feature = "jit")]
271
            // CompiledFunction(_) => write!(f, "#<compiled-function>"),
272
            ListV(l) => {
202✔
273
                write!(f, "(")?;
202✔
274

275
                let mut iter = l.iter().peekable();
202✔
276

277
                while let Some(item) = iter.next() {
1,164✔
278
                    self.format_with_cycles(item, f, FormatType::Normal)?;
×
279
                    if iter.peek().is_some() {
481✔
280
                        write!(f, " ")?
300✔
281
                    }
282
                }
283
                write!(f, ")")
202✔
284
            }
285
            // write!(f, "#<list {:?}>", l),
286
            MutFunc(_) => write!(f, "#<function>"),
×
287
            BuiltIn(_) => write!(f, "#<function>"),
×
288
            ReducerV(_) => write!(f, "#<reducer>"),
×
289
            MutableVector(v) => write!(f, "{:?}", v.get()),
2✔
290
            SyntaxObject(s) => {
1✔
291
                if let Some(raw) = &s.raw {
2✔
292
                    write!(f, "#<syntax:{:?} {:?}>", s.span, raw)
293
                } else {
294
                    write!(f, "#<syntax:{:?} {:?}>", s.span, s.syntax)
×
295
                }
296
            }
297
            BoxedIterator(_) => write!(f, "#<iterator>"),
×
298
            Boxed(b) => write!(f, "'#&{}", b.read()),
×
299
            Reference(x) => write!(f, "{}", x.format()?),
×
300
            HeapAllocated(b) => {
×
301
                let maybe_id = b.get().as_ptr_usize().and_then(|x| self.cycles.get(&x));
×
302
                match (maybe_id, format_type) {
×
303
                    (Some(id), FormatType::Normal) => {
×
304
                        write!(f, "#{id}#")
×
305
                    }
306
                    _ => {
307
                        write!(f, "'#&{}", b.get())
×
308
                    }
309
                }
310
            }
311
        };
312

313
        self.depth -= 1;
314

315
        res
316
    }
317
}
318

319
fn replace_with_void(value: &mut SteelVal) -> SteelVal {
4✔
320
    std::mem::replace(value, SteelVal::Void)
4✔
321
}
322

323
impl SteelVal {
324
    fn make_void(&mut self) -> SteelVal {
228✔
325
        std::mem::replace(self, SteelVal::Void)
228✔
326
    }
327
}
328

329
pub(crate) struct SteelCycleCollector {
330
    cycles: fxhash::FxHashMap<usize, usize>,
331
    values: List<SteelVal>,
332
}
333

334
impl Custom for SteelCycleCollector {}
335

336
impl SteelCycleCollector {
337
    pub fn from_root(value: SteelVal) -> Self {
10,704✔
338
        let mut queue = Vec::new();
10,704✔
339

340
        let mut collector = CycleCollector {
341
            visited: fxhash::FxHashSet::default(),
10,704✔
342
            cycles: fxhash::FxHashMap::default(),
10,704✔
343
            values: Vec::new(),
10,704✔
344
            queue: &mut queue,
10,704✔
345
            found_mutable: false,
346
        };
347

348
        collector.push_back(value);
10,704✔
349

350
        collector.visit();
10,704✔
351

352
        if collector.found_mutable {
10,704✔
353
            SteelCycleCollector {
354
                cycles: collector.cycles,
10✔
355
                values: collector.values.into(),
10✔
356
            }
357
        } else {
358
            SteelCycleCollector {
359
                cycles: fxhash::FxHashMap::default(),
10,694✔
360
                values: List::new(),
10,694✔
361
            }
362
        }
363
    }
364

365
    // Get the value
366
    pub fn get(&self, node: SteelVal) -> Option<usize> {
168✔
367
        match node {
168✔
368
            SteelVal::CustomStruct(c) => {
168✔
369
                let ptr_addr = c.as_ptr() as usize;
168✔
370
                self.cycles.get(&ptr_addr)
168✔
371
            }
372
            SteelVal::HeapAllocated(b) => {
×
373
                // Get the object that THIS points to
374
                let ptr_addr = b.get().as_ptr_usize().unwrap();
×
375
                self.cycles.get(&ptr_addr)
×
376
            }
377
            SteelVal::ListV(l) => {
×
378
                let ptr_addr = l.as_ptr_usize();
×
379

380
                self.cycles.get(&ptr_addr)
×
381
            }
382
            SteelVal::VectorV(l) => {
×
383
                let ptr_addr = l.0.as_ptr() as usize;
×
384

385
                self.cycles.get(&ptr_addr)
×
386
            }
387
            SteelVal::HashMapV(l) => {
×
388
                let ptr_addr = l.0.as_ptr() as usize;
×
389

390
                self.cycles.get(&ptr_addr)
×
391
            }
392
            SteelVal::HashSetV(l) => {
×
393
                let ptr_addr = l.0.as_ptr() as usize;
×
394

395
                self.cycles.get(&ptr_addr)
×
396
            }
397
            SteelVal::Custom(l) => {
×
398
                let ptr_addr = l.as_ptr() as usize;
×
399

400
                self.cycles.get(&ptr_addr)
×
401
            }
402
            SteelVal::Boxed(b) => {
×
403
                let ptr_addr = b.as_ptr() as usize;
×
404

405
                self.cycles.get(&ptr_addr)
×
406
            }
407
            SteelVal::SyntaxObject(s) => {
×
408
                let ptr_addr = s.as_ptr() as usize;
×
409

410
                self.cycles.get(&ptr_addr)
×
411
            }
412
            _ => None,
×
413
        }
414
        .copied()
415
    }
416

417
    pub fn values(&self) -> List<SteelVal> {
10,704✔
418
        self.values.clone()
10,704✔
419
    }
420
}
421

422
struct CycleCollector<'a> {
423
    // Keep a mapping of the pointer -> gensym
424
    visited: fxhash::FxHashSet<usize>,
425

426
    // Recording things that have already been seen
427
    cycles: fxhash::FxHashMap<usize, usize>,
428

429
    // Values captured in cycles
430
    values: Vec<SteelVal>,
431

432
    // Queue of items to check
433
    queue: &'a mut Vec<SteelVal>,
434

435
    // Whether we found something mutable - if we haven't, then a cycle
436
    // isn't even possible
437
    found_mutable: bool,
438
}
439

440
impl<'a> CycleCollector<'a> {
441
    fn add(&mut self, val: usize, steelval: &SteelVal) -> bool {
407✔
442
        if !self.found_mutable {
652✔
443
            false;
245✔
444
        }
445

446
        if self.visited.contains(&val) {
407✔
447
            let id = self.cycles.len();
28✔
448

449
            // If we've already seen this, its fine, we can just move on
450
            if let std::collections::hash_map::Entry::Vacant(e) = self.cycles.entry(val) {
56✔
451
                e.insert(id);
×
452
                // Keep track of the actual values that are being captured
453
                self.values.push(steelval.clone());
×
454
            } else {
455
                return true;
×
456
            }
457

458
            return true;
×
459
        }
460

461
        self.visited.insert(val);
379✔
462
        false
379✔
463
    }
464
}
465

466
impl<'a> BreadthFirstSearchSteelValVisitor for CycleCollector<'a> {
467
    type Output = ();
468

469
    fn default_output(&mut self) -> Self::Output {}
13,005✔
470

471
    fn pop_front(&mut self) -> Option<SteelVal> {
26,793✔
472
        self.queue.pop()
26,793✔
473
    }
474

475
    fn push_back(&mut self, value: SteelVal) {
13,788✔
476
        self.queue.push(value)
13,788✔
477
    }
478

479
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
2✔
480
    fn visit_closure(&mut self, _closure: Gc<ByteCodeLambda>) -> Self::Output {}
17✔
481
    fn visit_bool(&mut self, _boolean: bool) -> Self::Output {}
113✔
482
    fn visit_float(&mut self, _float: f64) -> Self::Output {}
26✔
483
    fn visit_int(&mut self, _int: isize) -> Self::Output {}
863✔
484
    fn visit_rational(&mut self, _: Rational32) -> Self::Output {}
4✔
485
    fn visit_bigrational(&mut self, _: Gc<BigRational>) -> Self::Output {}
2✔
486
    fn visit_bignum(&mut self, _: Gc<BigInt>) -> Self::Output {}
×
487
    fn visit_complex(&mut self, _: Gc<SteelComplex>) -> Self::Output {}
×
488
    fn visit_char(&mut self, _c: char) -> Self::Output {}
2,801✔
489

490
    fn visit_immutable_vector(&mut self, vector: SteelVector) -> Self::Output {
×
491
        if !self.add(
×
492
            vector.0.as_ptr() as usize,
×
493
            &SteelVal::VectorV(vector.clone()),
×
494
        ) {
495
            for value in vector.0.iter() {
×
496
                self.push_back(value.clone());
×
497
            }
498
        }
499
    }
500

501
    fn visit_void(&mut self) -> Self::Output {}
25✔
502
    fn visit_string(&mut self, _string: SteelString) -> Self::Output {}
9,238✔
503
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) -> Self::Output {}
4✔
504
    fn visit_symbol(&mut self, _symbol: SteelString) -> Self::Output {}
265✔
505

506
    // If we have cycles here, it is game over - we probably don't want to be
507
    // able to render to these easily?
508
    fn visit_custom_type(&mut self, _custom_type: GcMut<Box<dyn CustomType>>) -> Self::Output {}
×
509

510
    fn visit_hash_map(&mut self, hashmap: SteelHashMap) -> Self::Output {
1✔
511
        if !self.add(
1✔
512
            hashmap.0.as_ptr() as usize,
1✔
513
            &SteelVal::HashMapV(hashmap.clone()),
1✔
514
        ) {
515
            for (key, value) in hashmap.0.iter() {
1✔
516
                self.push_back(key.clone());
×
517
                self.push_back(value.clone());
×
518
            }
519
        }
520
    }
521

522
    fn visit_hash_set(&mut self, hashset: SteelHashSet) -> Self::Output {
×
523
        if !self.add(
×
524
            hashset.0.as_ptr() as usize,
×
525
            &SteelVal::HashSetV(hashset.clone()),
×
526
        ) {
527
            for key in hashset.0.iter() {
×
528
                self.push_back(key.clone())
×
529
            }
530
        }
531
    }
532

533
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) -> Self::Output {
70✔
534
        if !self.add(
70✔
535
            steel_struct.as_ptr() as usize,
70✔
536
            &SteelVal::CustomStruct(steel_struct.clone()),
70✔
537
        ) {
538
            for value in steel_struct.fields.iter() {
155✔
539
                self.push_back(value.clone())
113✔
540
            }
541
        }
542
    }
543

544
    fn visit_port(&mut self, _port: SteelPort) -> Self::Output {}
×
545
    fn visit_transducer(&mut self, _transducer: Gc<Transducer>) -> Self::Output {}
×
546
    fn visit_reducer(&mut self, _reducer: Gc<Reducer>) -> Self::Output {}
×
547
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) -> Self::Output {}
×
548
    fn visit_future(&mut self, _future: Gc<FutureResult>) -> Self::Output {}
×
549
    fn visit_stream(&mut self, _stream: Gc<LazyStream>) -> Self::Output {}
12✔
550
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) -> Self::Output {}
×
551
    fn visit_continuation(&mut self, _continuation: Continuation) -> Self::Output {}
×
552

553
    fn visit_list(&mut self, list: List<SteelVal>) -> Self::Output {
224✔
554
        if !self.add(list.as_ptr_usize(), &SteelVal::ListV(list.clone())) {
224✔
555
            for value in list {
1,829✔
556
                self.push_back(value);
535✔
557
            }
558
        }
559
    }
560

561
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) -> Self::Output {}
×
562

563
    // TODO: Figure out the mutable vector first
564
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output {
2✔
565
        self.found_mutable = true;
2✔
566

567
        if !self.add(
2✔
568
            vector.as_ptr_usize(),
2✔
569
            &SteelVal::MutableVector(vector.clone()),
2✔
570
        ) {
571
            for value in vector.get().iter() {
4✔
572
                self.push_back(value.clone());
1✔
573
            }
574
        }
575
    }
576

577
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) -> Self::Output {}
×
578

579
    fn visit_boxed_iterator(&mut self, _iterator: GcMut<OpaqueIterator>) -> Self::Output {}
×
580

581
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) -> Self::Output {
6✔
582
        if !self.add(
6✔
583
            syntax_object.as_ptr() as usize,
6✔
584
            &SteelVal::SyntaxObject(syntax_object.clone()),
6✔
585
        ) {
586
            if let Some(raw) = syntax_object.raw.clone() {
12✔
587
                self.push_back(raw);
×
588
            }
589

590
            self.push_back(syntax_object.syntax.clone());
×
591
        }
592
    }
593

594
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) -> Self::Output {
×
595
        if !self.add(
×
596
            boxed_value.as_ptr() as usize,
×
597
            &SteelVal::Boxed(boxed_value.clone()),
×
598
        ) {
599
            self.push_back(boxed_value.read().clone());
×
600
        }
601
    }
602

603
    fn visit_reference_value(&mut self, _reference: Gc<OpaqueReference<'static>>) -> Self::Output {}
×
604

605
    fn visit_heap_allocated(&mut self, heap_ref: HeapRef<SteelVal>) -> Self::Output {
104✔
606
        self.found_mutable = true;
104✔
607

608
        if !self.add(
104✔
609
            heap_ref.as_ptr_usize(),
104✔
610
            &SteelVal::HeapAllocated(heap_ref.clone()),
104✔
611
        ) {
612
            self.push_back(heap_ref.get());
104✔
613
        }
614
    }
615

616
    // TODO: Revisit this!
617
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
9✔
618
        self.push_back(pair.car());
9✔
619
        self.push_back(pair.cdr());
9✔
620
    }
621
}
622

623
#[cfg(not(feature = "without-drop-protection"))]
624
pub(crate) mod drop_impls {
625
    // use crate::values::recycler::{Recyclable, Recycle};
626

627
    use super::*;
628

629
    thread_local! {
630
        pub static DROP_BUFFER: RefCell<VecDeque<SteelVal>> = RefCell::new(VecDeque::with_capacity(128));
631
        pub static FORMAT_BUFFER: RefCell<VecDeque<SteelVal>> = RefCell::new(VecDeque::with_capacity(128));
632
    }
633

634
    impl Drop for SteelVector {
635
        fn drop(&mut self) {
769✔
636
            if self.0.is_empty() {
769✔
637
                return;
142✔
638
            }
639

640
            if let Some(inner) = self.0.get_mut() {
228✔
641
                DROP_BUFFER
642
                    .try_with(|drop_buffer| {
228✔
643
                        if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
456✔
644
                            for value in std::mem::take(inner) {
5,624✔
645
                                drop_buffer.push_back(value);
2,812✔
646
                            }
647

648
                            IterativeDropHandler::bfs(&mut drop_buffer);
228✔
649
                        }
650
                    })
651
                    .ok();
652
            }
653
        }
654
    }
655

656
    impl Drop for SteelHashMap {
657
        fn drop(&mut self) {
234,081✔
658
            if self.0.is_empty() {
234,081✔
659
                return;
102,578✔
660
            }
661

662
            if let Some(inner) = self.0.get_mut() {
2,279✔
663
                DROP_BUFFER
664
                    .try_with(|drop_buffer| {
2,279✔
665
                        if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
4,558✔
666
                            for (key, value) in std::mem::take(inner) {
32,364✔
667
                                drop_buffer.push_back(key);
16,182✔
668
                                drop_buffer.push_back(value);
16,182✔
669
                            }
670

671
                            IterativeDropHandler::bfs(&mut drop_buffer);
2,279✔
672
                        }
673
                    })
674
                    .ok();
675
            }
676
        }
677
    }
678

679
    impl Drop for UserDefinedStruct {
680
        fn drop(&mut self) {
107,420✔
681
            if self.fields.is_empty() {
107,420✔
682
                return;
4,506✔
683
            }
684

685
            if DROP_BUFFER
102,914✔
686
                .try_with(|drop_buffer| {
205,828✔
687
                    if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
205,767✔
688
                        // for value in std::mem::take(&mut self.fields) {
689
                        //     drop_buffer.push_back(value);
690
                        // }
691

692
                        drop_buffer.extend(
693
                            self.fields.drain(..),
694
                            // std::mem::replace(&mut self.fields, Recycle::noop()).into_iter(),
695
                        );
696

697
                        // std::mem::replace(&mut self, self.fields.put();
698

699
                        IterativeDropHandler::bfs(&mut drop_buffer);
700
                    }
701
                })
702
                .is_err()
703
            {
704
                let mut buffer = self.fields.drain(..).collect();
×
705

706
                IterativeDropHandler::bfs(&mut buffer);
×
707
            }
708
        }
709
    }
710

711
    impl Drop for LazyStream {
712
        fn drop(&mut self) {
116✔
713
            if self.initial_value == SteelVal::Void && self.stream_thunk == SteelVal::Void {
118✔
714
                return;
2✔
715
            }
716

717
            DROP_BUFFER
114✔
718
                .try_with(|drop_buffer| {
228✔
719
                    if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
228✔
720
                        drop_buffer.push_back(self.initial_value.make_void());
721
                        drop_buffer.push_back(self.stream_thunk.make_void());
722

723
                        IterativeDropHandler::bfs(&mut drop_buffer);
724
                    }
725
                })
726
                .ok();
727
        }
728
    }
729

730
    // impl Drop for ByteCodeLambda {
731
    //     fn drop(&mut self) {
732
    //         if self.captures.is_empty() {
733
    //             return;
734
    //         }
735

736
    //         DROP_BUFFER
737
    //             .try_with(|drop_buffer| {
738
    //                 if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
739
    //                     for value in std::mem::take(&mut self.captures) {
740
    //                         drop_buffer.push_back(value);
741
    //                     }
742

743
    //                     IterativeDropHandler::bfs(&mut drop_buffer);
744
    //                 }
745
    //             })
746
    //             .ok();
747
    //     }
748
    // }
749
}
750

751
pub struct IterativeDropHandler<'a> {
752
    drop_buffer: &'a mut VecDeque<SteelVal>,
753
    #[cfg(feature = "experimental-drop-handler")]
754
    moved_threads: bool,
755
}
756

757
impl<'a> IterativeDropHandler<'a> {
758
    pub fn bfs(drop_buffer: &'a mut VecDeque<SteelVal>) {
261,392✔
759
        IterativeDropHandler {
760
            drop_buffer,
761
            #[cfg(feature = "experimental-drop-handler")]
762
            moved_threads: false,
763
        }
764
        .visit();
765
    }
766
}
767

768
impl<'a> BreadthFirstSearchSteelValVisitor for IterativeDropHandler<'a> {
769
    type Output = ();
770

771
    fn default_output(&mut self) -> Self::Output {
261,392✔
772
        ()
773
    }
774

775
    fn pop_front(&mut self) -> Option<SteelVal> {
861,296✔
776
        self.drop_buffer.pop_front()
861,296✔
777
    }
778

779
    fn push_back(&mut self, value: SteelVal) {
534,367✔
780
        match &value {
534,367✔
781
            SteelVal::BoolV(_)
×
782
            | SteelVal::NumV(_)
×
783
            | SteelVal::IntV(_)
×
784
            | SteelVal::CharV(_)
×
785
            | SteelVal::Void
×
786
            | SteelVal::StringV(_)
×
787
            | SteelVal::FuncV(_)
×
788
            | SteelVal::SymbolV(_)
×
789
            | SteelVal::FutureFunc(_)
×
790
            | SteelVal::FutureV(_)
×
791
            | SteelVal::BoxedFunction(_)
×
792
            | SteelVal::MutFunc(_)
×
793
            | SteelVal::BuiltIn(_)
×
794
            | SteelVal::ByteVector(_)
×
795
            | SteelVal::BigNum(_) => return,
317,391✔
796
            _ => {
216,976✔
797
                self.drop_buffer.push_back(value);
216,976✔
798
            }
799
        }
800
    }
801

802
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
15✔
803
    fn visit_bool(&mut self, _boolean: bool) {}
3,243✔
804
    fn visit_float(&mut self, _float: f64) {}
×
805
    fn visit_int(&mut self, _int: isize) {}
764✔
806
    fn visit_rational(&mut self, _: Rational32) {}
×
807
    fn visit_bigrational(&mut self, _: Gc<BigRational>) {}
×
808
    fn visit_char(&mut self, _c: char) {}
42✔
809
    fn visit_void(&mut self) {}
2,592✔
810
    fn visit_string(&mut self, _string: SteelString) {}
132✔
811
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) {}
326✔
812
    fn visit_symbol(&mut self, _symbol: SteelString) {}
16,918✔
813
    fn visit_port(&mut self, _port: SteelPort) {}
11,209✔
814
    fn visit_future(&mut self, _future: Gc<FutureResult>) {}
×
815
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) {}
×
816
    fn visit_complex(&mut self, _: Gc<SteelComplex>) {}
×
817
    fn visit_bignum(&mut self, _bignum: Gc<BigInt>) {}
×
818
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) {}
×
819
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) {}
×
820
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) {}
2,129✔
821

822
    fn visit_closure(&mut self, closure: Gc<ByteCodeLambda>) {
20,925✔
823
        if let Ok(mut inner) = closure.try_unwrap() {
27,160✔
824
            for value in std::mem::take(&mut inner.captures) {
13,622✔
825
                self.push_back(value);
6,811✔
826
            }
827
        }
828
    }
829

830
    fn visit_immutable_vector(&mut self, mut vector: SteelVector) {
87✔
831
        if let Some(inner) = vector.0.get_mut() {
166✔
832
            for value in std::mem::take(inner) {
440✔
833
                self.push_back(value);
220✔
834
            }
835
        }
836
    }
837

838
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) {
1,864✔
839
        if let Ok(inner) = custom_type.try_unwrap() {
1,864✔
840
            let mut inner = inner.consume();
×
841

842
            // let this decide if we're doing anything with this custom type
843
            inner.drop_mut(self);
×
844
        }
845
    }
846

847
    fn visit_hash_map(&mut self, mut hashmap: SteelHashMap) {
100,010✔
848
        if let Some(inner) = hashmap.0.get_mut() {
200,010✔
849
            for (key, value) in std::mem::take(inner) {
200,000✔
850
                self.push_back(key);
100,000✔
851
                self.push_back(value);
100,000✔
852
            }
853
        }
854
    }
855

856
    fn visit_hash_set(&mut self, mut hashset: SteelHashSet) {
×
857
        if let Some(inner) = hashset.0.get_mut() {
×
858
            for key in std::mem::take(inner) {
×
859
                self.push_back(key);
×
860
            }
861
        }
862
    }
863

864
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) {
10,333✔
865
        if let Ok(mut inner) = steel_struct.try_unwrap() {
14,838✔
866
            for value in inner.fields.drain(..) {
23,690✔
867
                self.push_back(value);
11,845✔
868
            }
869
        }
870
    }
871

872
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) {
×
873
        if let Ok(inner) = transducer.try_unwrap() {
×
874
            for transducer in inner.ops {
×
875
                match transducer {
×
876
                    crate::values::transducers::Transducers::Map(m) => self.push_back(m),
×
877
                    crate::values::transducers::Transducers::Filter(v) => self.push_back(v),
×
878
                    crate::values::transducers::Transducers::Take(t) => self.push_back(t),
×
879
                    crate::values::transducers::Transducers::Drop(d) => self.push_back(d),
×
880
                    crate::values::transducers::Transducers::FlatMap(fm) => self.push_back(fm),
×
881
                    crate::values::transducers::Transducers::Flatten => {}
×
882
                    crate::values::transducers::Transducers::Window(w) => self.push_back(w),
×
883
                    crate::values::transducers::Transducers::TakeWhile(tw) => self.push_back(tw),
×
884
                    crate::values::transducers::Transducers::DropWhile(dw) => self.push_back(dw),
×
885
                    crate::values::transducers::Transducers::Extend(e) => self.push_back(e),
×
886
                    crate::values::transducers::Transducers::Cycle => {}
×
887
                    crate::values::transducers::Transducers::Enumerating => {}
×
888
                    crate::values::transducers::Transducers::Zipping(z) => self.push_back(z),
×
889
                    crate::values::transducers::Transducers::Interleaving(i) => self.push_back(i),
×
890
                }
891
            }
892
        }
893
    }
894

895
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) {
×
896
        if let Ok(inner) = reducer.try_unwrap() {
×
897
            match inner {
×
898
                Reducer::ForEach(f) => self.push_back(f),
×
899
                Reducer::Generic(rf) => {
×
900
                    self.push_back(rf.initial_value);
×
901
                    self.push_back(rf.function);
×
902
                }
903
                _ => {}
×
904
            }
905
        }
906
    }
907

908
    fn visit_stream(&mut self, stream: Gc<LazyStream>) {
14✔
909
        if let Ok(mut inner) = stream.try_unwrap() {
16✔
910
            self.push_back(replace_with_void(&mut inner.initial_value));
×
911
            self.push_back(replace_with_void(&mut inner.stream_thunk));
×
912
        }
913
    }
914

915
    // Walk the whole thing! This includes the stack and all the stack frames
916
    fn visit_continuation(&mut self, continuation: Continuation) {
65✔
917
        if let Ok(inner) = crate::gc::Shared::try_unwrap(continuation.inner).map(|x| x.consume()) {
222✔
918
            match inner {
×
919
                ContinuationMark::Closed(mut inner) => {
21✔
920
                    for value in std::mem::take(&mut inner.stack) {
272✔
921
                        self.push_back(value);
136✔
922
                    }
923

924
                    if let Some(inner) = inner.current_frame.function.get_mut() {
21✔
925
                        for value in std::mem::take(&mut inner.captures) {
×
926
                            self.push_back(value);
×
927
                        }
928
                    }
929

930
                    for mut frame in std::mem::take(&mut inner.stack_frames) {
112✔
931
                        if let Some(inner) = frame.function.get_mut() {
96✔
932
                            for value in std::mem::take(&mut inner.captures) {
×
933
                                self.push_back(value);
×
934
                            }
935
                        }
936
                    }
937
                }
938

939
                ContinuationMark::Open(mut inner) => {
25✔
940
                    for value in inner.current_stack_values {
100✔
941
                        self.push_back(value);
25✔
942
                    }
943

944
                    if let Some(inner) = inner.current_frame.function.get_mut() {
25✔
945
                        for value in std::mem::take(&mut inner.captures) {
×
946
                            self.push_back(value);
×
947
                        }
948
                    }
949
                }
950
            }
951
        }
952
    }
953

954
    fn visit_list(&mut self, list: List<SteelVal>) {
202,197✔
955
        // println!("VISITING LIST: {}", list.strong_count());
956
        // println!("list: {:?}", list);
957

958
        if list.strong_count() == 1 {
202,197✔
959
            for value in list.draining_iterator() {
658,586✔
960
                // println!(
961
                // "PUSHING BACK VALUE - queue size: {}",
962
                // self.drop_buffer.len()
963
                // );
964

965
                // println!("enqueueing: {}", value);
966

967
                self.push_back(value);
277,533✔
968
            }
969
        }
970
    }
971

972
    // TODO: When this gets replaced with heap storage, then we can do this more
973
    // effectively!
974
    fn visit_mutable_vector(&mut self, _vector: HeapRef<Vec<SteelVal>>) {}
4,004✔
975

976
    // TODO: Once the root is added back to this, bring it back
977
    fn visit_boxed_iterator(&mut self, iterator: GcMut<OpaqueIterator>) {
×
978
        if let Ok(inner) = iterator.try_unwrap() {
×
979
            self.push_back(inner.consume().root)
×
980
        }
981
    }
982

983
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) {
13,180✔
984
        if let Ok(inner) = syntax_object.try_unwrap() {
26,277✔
985
            if let Some(raw) = inner.raw {
13,076✔
986
                self.push_back(raw);
×
987
            }
988

989
            self.push_back(inner.syntax);
×
990
        }
991
    }
992

993
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) {
×
994
        if let Ok(inner) = boxed_value.try_unwrap() {
×
995
            self.push_back(inner.consume());
×
996
        }
997
    }
998

999
    fn visit_reference_value(&mut self, reference: Gc<OpaqueReference<'static>>) {
×
1000
        if let Ok(mut inner) = Gc::try_unwrap(reference) {
×
1001
            inner.drop_mut(self);
×
1002
        }
1003
    }
1004

1005
    fn visit_heap_allocated(&mut self, _heap_ref: HeapRef<SteelVal>) -> Self::Output {}
202,850✔
1006

1007
    // TODO: After a certain point, we should just pause
1008
    // and continue the iteration on another thread. That will
1009
    // help with long drops for recursive data structures.
1010
    fn visit(&mut self) -> Self::Output {
261,392✔
1011
        let mut ret = self.default_output();
261,392✔
1012

1013
        while let Some(value) = self.pop_front() {
1,461,200✔
1014
            ret = match value {
×
1015
                Closure(c) => self.visit_closure(c),
20,925✔
1016
                BoolV(b) => self.visit_bool(b),
3,243✔
1017
                NumV(n) => self.visit_float(n),
×
1018
                IntV(i) => self.visit_int(i),
764✔
1019
                Rational(x) => self.visit_rational(x),
×
1020
                BigRational(x) => self.visit_bigrational(x),
×
1021
                BigNum(b) => self.visit_bignum(b),
×
1022
                Complex(x) => self.visit_complex(x),
×
1023
                CharV(c) => self.visit_char(c),
42✔
1024
                VectorV(v) => self.visit_immutable_vector(v),
87✔
1025
                Void => self.visit_void(),
2,592✔
1026
                StringV(s) => self.visit_string(s),
132✔
1027
                FuncV(f) => self.visit_function_pointer(f),
326✔
1028
                SymbolV(s) => self.visit_symbol(s),
16,918✔
1029
                SteelVal::Custom(c) => self.visit_custom_type(c),
1,864✔
1030
                HashMapV(h) => self.visit_hash_map(h),
100,010✔
1031
                HashSetV(s) => self.visit_hash_set(s),
×
1032
                CustomStruct(c) => self.visit_steel_struct(c),
10,333✔
1033
                PortV(p) => self.visit_port(p),
11,209✔
1034
                IterV(t) => self.visit_transducer(t),
×
1035
                ReducerV(r) => self.visit_reducer(r),
×
1036
                FutureFunc(f) => self.visit_future_function(f),
×
1037
                FutureV(f) => self.visit_future(f),
×
1038
                StreamV(s) => self.visit_stream(s),
14✔
1039
                BoxedFunction(b) => self.visit_boxed_function(b),
2,129✔
1040
                ContinuationFunction(c) => self.visit_continuation(c),
65✔
1041
                ListV(l) => self.visit_list(l),
202,197✔
1042
                MutFunc(m) => self.visit_mutable_function(m),
×
1043
                BuiltIn(b) => self.visit_builtin_function(b),
×
1044
                MutableVector(b) => self.visit_mutable_vector(b),
4,004✔
1045
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1046
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
13,180✔
1047
                Boxed(b) => self.visit_boxed_value(b),
×
1048
                Reference(r) => self.visit_reference_value(r),
×
1049
                HeapAllocated(b) => self.visit_heap_allocated(b),
202,850✔
1050
                Pair(p) => self.visit_pair(p),
7,005✔
1051
                ByteVector(b) => self.visit_bytevector(b),
15✔
1052
            };
1053

1054
            // Long recursive drops will block the main thread from continuing - we should
1055
            // have another thread pick up the work?
1056
            #[cfg(feature = "experimental-drop-handler")]
×
1057
            if !self.moved_threads && self.drop_buffer.len() > 20 {
×
1058
                self.moved_threads = true;
×
1059

1060
                static DROP_THREAD: once_cell::sync::Lazy<
×
1061
                    crossbeam::channel::Sender<OwnedIterativeDropHandler>,
×
1062
                > = once_cell::sync::Lazy::new(start_background_drop_thread);
×
1063

1064
                fn start_background_drop_thread(
×
1065
                ) -> crossbeam::channel::Sender<OwnedIterativeDropHandler> {
1066
                    let (sender, receiver) =
×
1067
                        crossbeam::channel::unbounded::<OwnedIterativeDropHandler>();
×
1068

1069
                    std::thread::spawn(move || {
×
1070
                        while let Ok(mut value) = receiver.recv() {
×
1071
                            // let now = std::time::Instant::now();
1072
                            value.visit();
×
1073
                            // println!("Dropping: {:?}", now.elapsed());
1074
                        }
1075
                    });
1076

1077
                    sender
×
1078
                }
1079

1080
                // let buffer = VecDeque::new();
1081
                let original_buffer = std::mem::replace(self.drop_buffer, VecDeque::new());
×
1082

1083
                // println!("Moving to another thread");
1084

1085
                DROP_THREAD
×
1086
                    .send(OwnedIterativeDropHandler {
×
1087
                        drop_buffer: original_buffer,
×
1088
                    })
1089
                    .ok();
1090

1091
                // std::thread::spawn(move || {
1092
                //     let mut handler = OwnedIterativeDropHandler {
1093
                //         drop_buffer: original_buffer,
1094
                //     };
1095

1096
                //     handler.visit();
1097

1098
                //     drop(handler)
1099
                // });
1100
            }
1101
        }
1102

1103
        ret
261,392✔
1104
    }
1105

1106
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
7,005✔
1107
        if let Ok(inner) = Gc::try_unwrap(pair) {
12,815✔
1108
            self.push_back(inner.car);
×
1109
            self.push_back(inner.cdr);
×
1110
        }
1111
    }
1112
}
1113

1114
// TODO: Figure out a more elegant way to do this!
1115

1116
#[cfg(feature = "experimental-drop-handler")]
1117
pub struct OwnedIterativeDropHandler {
1118
    drop_buffer: VecDeque<SteelVal>,
1119
}
1120

1121
#[cfg(feature = "experimental-drop-handler")]
1122
impl BreadthFirstSearchSteelValVisitor for OwnedIterativeDropHandler {
1123
    type Output = ();
1124

1125
    fn default_output(&mut self) -> Self::Output {
1126
        ()
1127
    }
1128

1129
    fn pop_front(&mut self) -> Option<SteelVal> {
1130
        self.drop_buffer.pop_front()
1131
    }
1132

1133
    fn push_back(&mut self, value: SteelVal) {
1134
        match &value {
1135
            SteelVal::BoolV(_)
1136
            | SteelVal::NumV(_)
1137
            | SteelVal::IntV(_)
1138
            | SteelVal::CharV(_)
1139
            | SteelVal::Void
1140
            | SteelVal::StringV(_)
1141
            | SteelVal::FuncV(_)
1142
            | SteelVal::SymbolV(_)
1143
            | SteelVal::FutureFunc(_)
1144
            | SteelVal::FutureV(_)
1145
            | SteelVal::BoxedFunction(_)
1146
            | SteelVal::MutFunc(_)
1147
            | SteelVal::BuiltIn(_)
1148
            | SteelVal::ByteVector(_)
1149
            | SteelVal::BigNum(_) => return,
1150
            _ => {
1151
                self.drop_buffer.push_back(value);
1152
            }
1153
        }
1154
    }
1155

1156
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
1157
    fn visit_bool(&mut self, _boolean: bool) {}
1158
    fn visit_float(&mut self, _float: f64) {}
1159
    fn visit_int(&mut self, _int: isize) {}
1160
    fn visit_rational(&mut self, _: Rational32) {}
1161
    fn visit_bigrational(&mut self, _: Gc<BigRational>) {}
1162
    fn visit_char(&mut self, _c: char) {}
1163
    fn visit_void(&mut self) {}
1164
    fn visit_string(&mut self, _string: SteelString) {}
1165
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) {}
1166
    fn visit_symbol(&mut self, _symbol: SteelString) {}
1167
    fn visit_port(&mut self, _port: SteelPort) {}
1168
    fn visit_future(&mut self, _future: Gc<FutureResult>) {}
1169
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) {}
1170
    fn visit_complex(&mut self, _: Gc<SteelComplex>) {}
1171
    fn visit_bignum(&mut self, _bignum: Gc<BigInt>) {}
1172
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) {}
1173
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) {}
1174
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) {}
1175

1176
    fn visit_closure(&mut self, closure: Gc<ByteCodeLambda>) {
1177
        if let Ok(mut inner) = closure.try_unwrap() {
1178
            for value in std::mem::take(&mut inner.captures) {
1179
                self.push_back(value);
1180
            }
1181
        }
1182
    }
1183

1184
    fn visit_immutable_vector(&mut self, mut vector: SteelVector) {
1185
        if let Some(inner) = vector.0.get_mut() {
1186
            for value in std::mem::take(inner) {
1187
                self.push_back(value);
1188
            }
1189
        }
1190
    }
1191

1192
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) {
1193
        if let Ok(inner) = custom_type.try_unwrap() {
1194
            let mut inner = inner.consume();
1195

1196
            // TODO: @matt - Don't leave this while merging
1197
            // inner.drop_mut(self);
1198

1199
            // let this decide if we're doing anything with this custom type
1200
            // inner.drop_mut(self);
1201
        }
1202
    }
1203

1204
    fn visit_hash_map(&mut self, mut hashmap: SteelHashMap) {
1205
        if let Some(inner) = hashmap.0.get_mut() {
1206
            for (key, value) in std::mem::take(inner) {
1207
                self.push_back(key);
1208
                self.push_back(value);
1209
            }
1210
        }
1211
    }
1212

1213
    fn visit_hash_set(&mut self, mut hashset: SteelHashSet) {
1214
        if let Some(inner) = hashset.0.get_mut() {
1215
            for key in std::mem::take(inner) {
1216
                self.push_back(key);
1217
            }
1218
        }
1219
    }
1220

1221
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) {
1222
        if let Ok(mut inner) = steel_struct.try_unwrap() {
1223
            for value in inner.fields.drain(..) {
1224
                self.push_back(value);
1225
            }
1226
        }
1227
    }
1228

1229
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) {
1230
        if let Ok(inner) = transducer.try_unwrap() {
1231
            for transducer in inner.ops {
1232
                match transducer {
1233
                    crate::values::transducers::Transducers::Map(m) => self.push_back(m),
1234
                    crate::values::transducers::Transducers::Filter(v) => self.push_back(v),
1235
                    crate::values::transducers::Transducers::Take(t) => self.push_back(t),
1236
                    crate::values::transducers::Transducers::Drop(d) => self.push_back(d),
1237
                    crate::values::transducers::Transducers::FlatMap(fm) => self.push_back(fm),
1238
                    crate::values::transducers::Transducers::Flatten => {}
1239
                    crate::values::transducers::Transducers::Window(w) => self.push_back(w),
1240
                    crate::values::transducers::Transducers::TakeWhile(tw) => self.push_back(tw),
1241
                    crate::values::transducers::Transducers::DropWhile(dw) => self.push_back(dw),
1242
                    crate::values::transducers::Transducers::Extend(e) => self.push_back(e),
1243
                    crate::values::transducers::Transducers::Cycle => {}
1244
                    crate::values::transducers::Transducers::Enumerating => {}
1245
                    crate::values::transducers::Transducers::Zipping(z) => self.push_back(z),
1246
                    crate::values::transducers::Transducers::Interleaving(i) => self.push_back(i),
1247
                }
1248
            }
1249
        }
1250
    }
1251

1252
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) {
1253
        if let Ok(inner) = reducer.try_unwrap() {
1254
            match inner {
1255
                Reducer::ForEach(f) => self.push_back(f),
1256
                Reducer::Generic(rf) => {
1257
                    self.push_back(rf.initial_value);
1258
                    self.push_back(rf.function);
1259
                }
1260
                _ => {}
1261
            }
1262
        }
1263
    }
1264

1265
    fn visit_stream(&mut self, stream: Gc<LazyStream>) {
1266
        if let Ok(mut inner) = stream.try_unwrap() {
1267
            self.push_back(replace_with_void(&mut inner.initial_value));
1268
            self.push_back(replace_with_void(&mut inner.stream_thunk));
1269
        }
1270
    }
1271

1272
    // Walk the whole thing! This includes the stack and all the stack frames
1273
    fn visit_continuation(&mut self, continuation: Continuation) {
1274
        if let Ok(inner) = crate::gc::Shared::try_unwrap(continuation.inner).map(|x| x.consume()) {
1275
            match inner {
1276
                ContinuationMark::Closed(mut inner) => {
1277
                    for value in std::mem::take(&mut inner.stack) {
1278
                        self.push_back(value);
1279
                    }
1280

1281
                    if let Some(inner) = inner.current_frame.function.get_mut() {
1282
                        for value in std::mem::take(&mut inner.captures) {
1283
                            self.push_back(value);
1284
                        }
1285
                    }
1286

1287
                    for mut frame in std::mem::take(&mut inner.stack_frames) {
1288
                        if let Some(inner) = frame.function.get_mut() {
1289
                            for value in std::mem::take(&mut inner.captures) {
1290
                                self.push_back(value);
1291
                            }
1292
                        }
1293
                    }
1294
                }
1295

1296
                ContinuationMark::Open(mut inner) => {
1297
                    for value in inner.current_stack_values {
1298
                        self.push_back(value);
1299
                    }
1300

1301
                    if let Some(inner) = inner.current_frame.function.get_mut() {
1302
                        for value in std::mem::take(&mut inner.captures) {
1303
                            self.push_back(value);
1304
                        }
1305
                    }
1306
                }
1307
            }
1308
        }
1309
    }
1310

1311
    fn visit_list(&mut self, list: List<SteelVal>) {
1312
        // println!("VISITING LIST: {}", list.strong_count());
1313
        // println!("list: {:?}", list);
1314

1315
        if list.strong_count() == 1 {
1316
            for value in list.draining_iterator() {
1317
                // println!(
1318
                // "PUSHING BACK VALUE - queue size: {}",
1319
                // self.drop_buffer.len()
1320
                // );
1321

1322
                // println!("enqueueing: {}", value);
1323

1324
                self.push_back(value);
1325
            }
1326
        }
1327
    }
1328

1329
    // TODO: When this gets replaced with heap storage, then we can do this more
1330
    // effectively!
1331
    fn visit_mutable_vector(&mut self, _vector: HeapRef<Vec<SteelVal>>) {}
1332

1333
    // TODO: Once the root is added back to this, bring it back
1334
    fn visit_boxed_iterator(&mut self, iterator: GcMut<OpaqueIterator>) {
1335
        if let Ok(inner) = iterator.try_unwrap() {
1336
            self.push_back(inner.consume().root)
1337
        }
1338
    }
1339

1340
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) {
1341
        if let Ok(inner) = syntax_object.try_unwrap() {
1342
            if let Some(raw) = inner.raw {
1343
                self.push_back(raw);
1344
            }
1345

1346
            self.push_back(inner.syntax);
1347
        }
1348
    }
1349

1350
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) {
1351
        if let Ok(inner) = boxed_value.try_unwrap() {
1352
            self.push_back(inner.consume());
1353
        }
1354
    }
1355

1356
    fn visit_reference_value(&mut self, reference: Gc<OpaqueReference<'static>>) {
1357
        if let Ok(mut inner) = Gc::try_unwrap(reference) {
1358
            // TODO: @matt - Don't leave this while merging
1359
            // inner.drop_mut(self);
1360
        }
1361
    }
1362

1363
    fn visit_heap_allocated(&mut self, _heap_ref: HeapRef<SteelVal>) -> Self::Output {}
1364

1365
    // TODO: After a certain point, we should just pause
1366
    // and continue the iteration on another thread. That will
1367
    // help with long drops for recursive data structures.
1368
    fn visit(&mut self) -> Self::Output {
1369
        let mut ret = self.default_output();
1370

1371
        while let Some(value) = self.pop_front() {
1372
            ret = match value {
1373
                Closure(c) => self.visit_closure(c),
1374
                BoolV(b) => self.visit_bool(b),
1375
                NumV(n) => self.visit_float(n),
1376
                IntV(i) => self.visit_int(i),
1377
                Rational(x) => self.visit_rational(x),
1378
                BigRational(x) => self.visit_bigrational(x),
1379
                BigNum(b) => self.visit_bignum(b),
1380
                Complex(x) => self.visit_complex(x),
1381
                CharV(c) => self.visit_char(c),
1382
                VectorV(v) => self.visit_immutable_vector(v),
1383
                Void => self.visit_void(),
1384
                StringV(s) => self.visit_string(s),
1385
                FuncV(f) => self.visit_function_pointer(f),
1386
                SymbolV(s) => self.visit_symbol(s),
1387
                SteelVal::Custom(c) => self.visit_custom_type(c),
1388
                HashMapV(h) => self.visit_hash_map(h),
1389
                HashSetV(s) => self.visit_hash_set(s),
1390
                CustomStruct(c) => self.visit_steel_struct(c),
1391
                PortV(p) => self.visit_port(p),
1392
                IterV(t) => self.visit_transducer(t),
1393
                ReducerV(r) => self.visit_reducer(r),
1394
                FutureFunc(f) => self.visit_future_function(f),
1395
                FutureV(f) => self.visit_future(f),
1396
                StreamV(s) => self.visit_stream(s),
1397
                BoxedFunction(b) => self.visit_boxed_function(b),
1398
                ContinuationFunction(c) => self.visit_continuation(c),
1399
                ListV(l) => self.visit_list(l),
1400
                MutFunc(m) => self.visit_mutable_function(m),
1401
                BuiltIn(b) => self.visit_builtin_function(b),
1402
                MutableVector(b) => self.visit_mutable_vector(b),
1403
                BoxedIterator(b) => self.visit_boxed_iterator(b),
1404
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
1405
                Boxed(b) => self.visit_boxed_value(b),
1406
                Reference(r) => self.visit_reference_value(r),
1407
                HeapAllocated(b) => self.visit_heap_allocated(b),
1408
                Pair(p) => self.visit_pair(p),
1409
                ByteVector(b) => self.visit_bytevector(b),
1410
            };
1411
        }
1412

1413
        ret
1414
    }
1415

1416
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
1417
        if let Ok(inner) = Gc::try_unwrap(pair) {
1418
            self.push_back(inner.car);
1419
            self.push_back(inner.cdr);
1420
        }
1421
    }
1422
}
1423

1424
pub trait BreadthFirstSearchSteelValVisitor {
1425
    type Output;
1426

1427
    fn default_output(&mut self) -> Self::Output;
1428

1429
    fn pop_front(&mut self) -> Option<SteelVal>;
1430

1431
    fn push_back(&mut self, value: SteelVal);
1432

1433
    fn visit(&mut self) -> Self::Output {
13,005✔
1434
        let mut ret = self.default_output();
13,005✔
1435

1436
        while let Some(value) = self.pop_front() {
40,581✔
1437
            ret = match value {
13,788✔
1438
                Closure(c) => self.visit_closure(c),
17✔
1439
                BoolV(b) => self.visit_bool(b),
113✔
1440
                NumV(n) => self.visit_float(n),
26✔
1441
                IntV(i) => self.visit_int(i),
863✔
1442
                Rational(x) => self.visit_rational(x),
4✔
1443
                BigRational(x) => self.visit_bigrational(x),
2✔
1444
                BigNum(b) => self.visit_bignum(b),
×
1445
                Complex(x) => self.visit_complex(x),
×
1446
                CharV(c) => self.visit_char(c),
2,801✔
1447
                VectorV(v) => self.visit_immutable_vector(v),
×
1448
                Void => self.visit_void(),
25✔
1449
                StringV(s) => self.visit_string(s),
9,238✔
1450
                FuncV(f) => self.visit_function_pointer(f),
4✔
1451
                SymbolV(s) => self.visit_symbol(s),
265✔
1452
                SteelVal::Custom(c) => self.visit_custom_type(c),
×
1453
                HashMapV(h) => self.visit_hash_map(h),
1✔
1454
                HashSetV(s) => self.visit_hash_set(s),
×
1455
                CustomStruct(c) => self.visit_steel_struct(c),
70✔
1456
                PortV(p) => self.visit_port(p),
×
1457
                IterV(t) => self.visit_transducer(t),
×
1458
                ReducerV(r) => self.visit_reducer(r),
×
1459
                FutureFunc(f) => self.visit_future_function(f),
×
1460
                FutureV(f) => self.visit_future(f),
×
1461
                StreamV(s) => self.visit_stream(s),
12✔
1462
                BoxedFunction(b) => self.visit_boxed_function(b),
×
1463
                ContinuationFunction(c) => self.visit_continuation(c),
×
1464
                ListV(l) => self.visit_list(l),
224✔
1465
                MutFunc(m) => self.visit_mutable_function(m),
×
1466
                BuiltIn(b) => self.visit_builtin_function(b),
×
1467
                MutableVector(b) => self.visit_mutable_vector(b),
2✔
1468
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1469
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
6✔
1470
                Boxed(b) => self.visit_boxed_value(b),
×
1471
                Reference(r) => self.visit_reference_value(r),
×
1472
                HeapAllocated(b) => self.visit_heap_allocated(b),
104✔
1473
                Pair(p) => self.visit_pair(p),
9✔
1474
                ByteVector(b) => self.visit_bytevector(b),
2✔
1475
            };
1476
        }
1477

1478
        ret
13,005✔
1479
    }
1480

1481
    fn visit_closure(&mut self, closure: Gc<ByteCodeLambda>) -> Self::Output;
1482
    fn visit_bool(&mut self, _: bool) -> Self::Output;
1483
    fn visit_float(&mut self, _: f64) -> Self::Output;
1484
    fn visit_int(&mut self, _: isize) -> Self::Output;
1485
    fn visit_rational(&mut self, _: Rational32) -> Self::Output;
1486
    fn visit_bigrational(&mut self, _: Gc<BigRational>) -> Self::Output;
1487
    fn visit_bignum(&mut self, _: Gc<BigInt>) -> Self::Output;
1488
    fn visit_complex(&mut self, _: Gc<SteelComplex>) -> Self::Output;
1489
    fn visit_char(&mut self, _: char) -> Self::Output;
1490
    fn visit_immutable_vector(&mut self, vector: SteelVector) -> Self::Output;
1491
    fn visit_void(&mut self) -> Self::Output;
1492
    fn visit_string(&mut self, string: SteelString) -> Self::Output;
1493
    fn visit_function_pointer(&mut self, ptr: FunctionSignature) -> Self::Output;
1494
    fn visit_symbol(&mut self, symbol: SteelString) -> Self::Output;
1495
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) -> Self::Output;
1496
    fn visit_hash_map(&mut self, hashmap: SteelHashMap) -> Self::Output;
1497
    fn visit_hash_set(&mut self, hashset: SteelHashSet) -> Self::Output;
1498
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) -> Self::Output;
1499
    fn visit_port(&mut self, port: SteelPort) -> Self::Output;
1500
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) -> Self::Output;
1501
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) -> Self::Output;
1502
    fn visit_future_function(&mut self, function: BoxedAsyncFunctionSignature) -> Self::Output;
1503
    fn visit_future(&mut self, future: Gc<FutureResult>) -> Self::Output;
1504
    fn visit_stream(&mut self, stream: Gc<LazyStream>) -> Self::Output;
1505
    fn visit_boxed_function(&mut self, function: Gc<BoxedDynFunction>) -> Self::Output;
1506
    fn visit_continuation(&mut self, continuation: Continuation) -> Self::Output;
1507
    fn visit_list(&mut self, list: List<SteelVal>) -> Self::Output;
1508
    fn visit_mutable_function(&mut self, function: MutFunctionSignature) -> Self::Output;
1509
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output;
1510
    fn visit_builtin_function(&mut self, function: BuiltInSignature) -> Self::Output;
1511
    fn visit_boxed_iterator(&mut self, iterator: GcMut<OpaqueIterator>) -> Self::Output;
1512
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) -> Self::Output;
1513
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) -> Self::Output;
1514
    fn visit_reference_value(&mut self, reference: Gc<OpaqueReference<'static>>) -> Self::Output;
1515
    fn visit_heap_allocated(&mut self, heap_ref: HeapRef<SteelVal>) -> Self::Output;
1516
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output;
1517
    fn visit_bytevector(&mut self, bytevector: SteelByteVector) -> Self::Output;
1518
}
1519

1520
pub trait BreadthFirstSearchSteelValReferenceVisitor<'a> {
1521
    type Output;
1522

1523
    fn default_output(&mut self) -> Self::Output;
1524

1525
    fn pop_front(&mut self) -> Option<&'a SteelVal>;
1526

1527
    fn push_back(&mut self, value: &'a SteelVal);
1528

1529
    fn visit(&mut self) -> Self::Output {
×
1530
        let mut ret = self.default_output();
×
1531

1532
        while let Some(value) = self.pop_front() {
×
1533
            ret = match value {
×
1534
                Closure(c) => self.visit_closure(c),
×
1535
                BoolV(b) => self.visit_bool(*b),
×
1536
                NumV(n) => self.visit_float(*n),
×
1537
                IntV(i) => self.visit_int(*i),
×
1538
                Rational(x) => self.visit_rational(*x),
×
1539
                BigRational(x) => self.visit_bigrational(x),
×
1540
                Complex(_) => unimplemented!(),
×
1541
                CharV(c) => self.visit_char(*c),
×
1542
                VectorV(v) => self.visit_immutable_vector(v),
×
1543
                Void => self.visit_void(),
×
1544
                StringV(s) => self.visit_string(s),
×
1545
                FuncV(f) => self.visit_function_pointer(*f),
×
1546
                SymbolV(s) => self.visit_symbol(s),
×
1547
                SteelVal::Custom(c) => self.visit_custom_type(c),
×
1548
                HashMapV(h) => self.visit_hash_map(h),
×
1549
                HashSetV(s) => self.visit_hash_set(s),
×
1550
                CustomStruct(c) => self.visit_steel_struct(c),
×
1551
                PortV(p) => self.visit_port(p),
×
1552
                IterV(t) => self.visit_transducer(t),
×
1553
                ReducerV(r) => self.visit_reducer(r),
×
1554
                FutureFunc(f) => self.visit_future_function(f),
×
1555
                FutureV(f) => self.visit_future(f),
×
1556
                StreamV(s) => self.visit_stream(s),
×
1557
                BoxedFunction(b) => self.visit_boxed_function(b),
×
1558
                ContinuationFunction(c) => self.visit_continuation(c),
×
1559
                ListV(l) => self.visit_list(l),
×
1560
                MutFunc(m) => self.visit_mutable_function(m),
×
1561
                BuiltIn(b) => self.visit_builtin_function(b),
×
1562
                MutableVector(b) => self.visit_mutable_vector(b),
×
1563
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1564
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
×
1565
                Boxed(b) => self.visit_boxed_value(b),
×
1566
                Reference(r) => self.visit_reference_value(r),
×
1567
                BigNum(b) => self.visit_bignum(b),
×
1568
                HeapAllocated(b) => self.visit_heap_allocated(b),
×
1569
                Pair(p) => self.visit_pair(p),
×
1570
                ByteVector(b) => self.visit_bytevector(b),
×
1571
            };
1572
        }
1573

1574
        ret
×
1575
    }
1576

1577
    fn visit_bytevector(&mut self, _: &'a SteelByteVector) -> Self::Output;
1578
    fn visit_closure(&mut self, _: &'a Gc<ByteCodeLambda>) -> Self::Output;
1579
    fn visit_bool(&mut self, _: bool) -> Self::Output;
1580
    fn visit_float(&mut self, _: f64) -> Self::Output;
1581
    fn visit_int(&mut self, _: isize) -> Self::Output;
1582
    fn visit_rational(&mut self, _: Rational32) -> Self::Output;
1583
    fn visit_bigrational(&mut self, _: &'a Gc<BigRational>) -> Self::Output;
1584
    fn visit_bignum(&mut self, _: &'a Gc<BigInt>) -> Self::Output;
1585
    fn visit_char(&mut self, _: char) -> Self::Output;
1586
    fn visit_immutable_vector(&mut self, vector: &'a SteelVector) -> Self::Output;
1587
    fn visit_void(&mut self) -> Self::Output;
1588
    fn visit_string(&mut self, string: &'a SteelString) -> Self::Output;
1589
    fn visit_function_pointer(&mut self, ptr: FunctionSignature) -> Self::Output;
1590
    fn visit_symbol(&mut self, symbol: &'a SteelString) -> Self::Output;
1591
    fn visit_custom_type(&mut self, custom_type: &'a GcMut<Box<dyn CustomType>>) -> Self::Output;
1592
    fn visit_hash_map(&mut self, hashmap: &'a SteelHashMap) -> Self::Output;
1593
    fn visit_hash_set(&mut self, hashset: &'a SteelHashSet) -> Self::Output;
1594
    fn visit_steel_struct(&mut self, steel_struct: &'a Gc<UserDefinedStruct>) -> Self::Output;
1595
    fn visit_port(&mut self, port: &'a SteelPort) -> Self::Output;
1596
    fn visit_transducer(&mut self, transducer: &'a Gc<Transducer>) -> Self::Output;
1597
    fn visit_reducer(&mut self, reducer: &'a Gc<Reducer>) -> Self::Output;
1598
    fn visit_future_function(&mut self, function: &'a BoxedAsyncFunctionSignature) -> Self::Output;
1599
    fn visit_future(&mut self, future: &'a Gc<FutureResult>) -> Self::Output;
1600
    fn visit_stream(&mut self, stream: &'a Gc<LazyStream>) -> Self::Output;
1601
    fn visit_boxed_function(&mut self, function: &'a Gc<BoxedDynFunction>) -> Self::Output;
1602
    fn visit_continuation(&mut self, continuation: &'a Continuation) -> Self::Output;
1603
    fn visit_list(&mut self, list: &'a List<SteelVal>) -> Self::Output;
1604
    fn visit_mutable_function(&mut self, function: &'a MutFunctionSignature) -> Self::Output;
1605
    fn visit_mutable_vector(&mut self, vector: &'a HeapRef<Vec<SteelVal>>) -> Self::Output;
1606
    fn visit_builtin_function(&mut self, function: &'a BuiltInSignature) -> Self::Output;
1607
    fn visit_boxed_iterator(&mut self, iterator: &'a GcMut<OpaqueIterator>) -> Self::Output;
1608
    fn visit_syntax_object(&mut self, syntax_object: &'a Gc<Syntax>) -> Self::Output;
1609
    fn visit_boxed_value(&mut self, boxed_value: &'a GcMut<SteelVal>) -> Self::Output;
1610
    fn visit_reference_value(
1611
        &mut self,
1612
        reference: &'a Gc<OpaqueReference<'static>>,
1613
    ) -> Self::Output;
1614
    fn visit_heap_allocated(&mut self, heap_ref: &'a HeapRef<SteelVal>) -> Self::Output;
1615
    fn visit_pair(&mut self, pair: &'a Gc<Pair>) -> Self::Output;
1616
}
1617

1618
thread_local! {
1619
    static LEFT_QUEUE: RefCell<Vec<SteelVal>> = RefCell::new(Vec::with_capacity(128));
1620
    static RIGHT_QUEUE: RefCell<Vec<SteelVal>> = RefCell::new(Vec::with_capacity(128));
1621
    static VISITED_SET: RefCell<fxhash::FxHashSet<usize>> = RefCell::new(fxhash::FxHashSet::default());
1622
    static EQ_DEPTH: Cell<usize> = Cell::new(0);
1623
}
1624

1625
fn increment_eq_depth() {
×
1626
    #[cfg(feature = "sandbox")]
1627
    EQ_DEPTH.with(|x| x.set(x.get() + 1));
×
1628
}
1629

1630
fn decrement_eq_depth() {
×
1631
    #[cfg(feature = "sandbox")]
1632
    EQ_DEPTH.with(|x| x.set(x.get() - 1));
×
1633
}
1634

1635
fn reset_eq_depth() {
54,554✔
1636
    #[cfg(feature = "sandbox")]
1637
    EQ_DEPTH.with(|x| x.set(0));
54,554✔
1638
}
1639

1640
fn eq_depth() -> usize {
39✔
1641
    #[cfg(feature = "sandbox")]
1642
    return EQ_DEPTH.with(|x| x.get());
39✔
1643

1644
    #[cfg(not(feature = "sandbox"))]
39✔
1645
    0
39✔
1646
}
1647

1648
struct RecursiveEqualityHandler<'a> {
1649
    left: EqualityVisitor<'a>,
1650
    right: EqualityVisitor<'a>,
1651
    visited: &'a mut fxhash::FxHashSet<usize>,
1652
    // found_mutable_object: bool,
1653
}
1654

1655
impl<'a> RecursiveEqualityHandler<'a> {
1656
    pub fn compare_equality(&mut self, left: SteelVal, right: SteelVal) -> bool {
54,554✔
1657
        self.left.push_back(left);
54,554✔
1658
        self.right.push_back(right);
54,554✔
1659

1660
        self.visit()
54,554✔
1661
    }
1662

1663
    fn should_visit(&mut self, value: usize) -> bool {
82,766✔
1664
        // if !self.found_mutable_object {
1665
        // return true;
1666
        // }
1667

1668
        if self.visited.insert(value) {
82,766✔
1669
            return true;
82,765✔
1670
        }
1671

1672
        return false;
1✔
1673
    }
1674

1675
    fn visit(&mut self) -> bool {
54,554✔
1676
        loop {
×
1677
            let (left, right) = match (self.left.pop_front(), self.right.pop_front()) {
189,648✔
1678
                (Some(l), Some(r)) => (l, r),
74,376✔
1679
                (None, None) => return true,
40,896✔
1680
                _ => return false,
×
1681
            };
1682

1683
            // println!("{} - {}", left, right);
1684

1685
            // println!(
1686
            //     "Queue size: {:?}",
1687
            //     self.left.queue.len(),
1688
            //     // self.right.queue.len()
1689
            // );
1690

1691
            match (left, right) {
74,376✔
1692
                (ListV(l), ListV(r)) => {
41,380✔
1693
                    // If we've reached the same object, we're good
1694
                    if l.ptr_eq(&r) || l.storage_ptr_eq(&r) {
82,674✔
1695
                        continue;
86✔
1696
                    }
1697

1698
                    if self.should_visit(l.elements_as_ptr_usize())
41,294✔
1699
                        && self.should_visit(r.elements_as_ptr_usize())
41,294✔
1700
                    {
1701
                        if l.len() != r.len() {
41,293✔
1702
                            return false;
152✔
1703
                        }
1704

1705
                        for (lvalue, rvalue) in l.iter().zip(r.iter()) {
11,981✔
1706
                            // TODO: @Matt - need to do optimistic checks here so we don't
1707
                            // visit things we don't need to - basically a "check left" function
1708
                            match (lvalue, rvalue) {
11,981✔
1709
                                (SteelVal::ListV(llist), SteelVal::ListV(rlist))
429✔
1710
                                    if (llist.is_empty() && rlist.is_empty())
453✔
1711
                                        || llist.ptr_eq(&rlist)
405✔
1712
                                        || llist.storage_ptr_eq(&rlist) =>
429✔
1713
                                {
1714
                                    continue;
272✔
1715
                                }
1716
                                // (SteelVal::ListV(llist), SteelVal::ListV(rlist)) if llist.len() == 1 && rlist.len() == 1 {
1717

1718
                                // }
1719
                                (a, b) => {
11,709✔
1720
                                    // println!("Pushing back: {}", a);
1721

1722
                                    self.left.push_back(a.clone());
11,709✔
1723
                                    self.right.push_back(b.clone());
11,709✔
1724
                                }
1725
                            }
1726
                        }
1727
                    }
1728

1729
                    continue;
41,142✔
1730
                }
1731
                // If we run into issues with any stack overflow
1732
                // check here first.
1733
                (Pair(l), Pair(r)) => {
20✔
1734
                    if Gc::ptr_eq(&l, &r) {
20✔
1735
                        continue;
1✔
1736
                    }
1737

1738
                    self.left.push_back(l.car());
19✔
1739
                    self.right.push_back(r.car());
19✔
1740

1741
                    self.left.push_back(l.cdr());
19✔
1742
                    self.right.push_back(r.cdr());
19✔
1743
                }
1744
                (BoolV(l), BoolV(r)) => {
1✔
1745
                    if l != r {
1✔
1746
                        return false;
×
1747
                    }
1748

1749
                    continue;
1✔
1750
                }
1751
                (NumV(l), NumV(r)) => {
×
1752
                    if l != r {
×
1753
                        return false;
×
1754
                    }
1755

1756
                    continue;
×
1757
                }
1758
                (IntV(l), IntV(r)) => {
13,464✔
1759
                    if l != r {
13,464✔
UNCOV
1760
                        return false;
×
1761
                    }
1762

1763
                    continue;
13,464✔
1764
                }
1765
                (CharV(l), CharV(r)) => {
267✔
1766
                    if l != r {
267✔
1767
                        return false;
246✔
1768
                    }
1769

1770
                    continue;
21✔
1771
                }
1772
                (VectorV(l), VectorV(r)) => {
43✔
1773
                    if l.len() != r.len() {
43✔
1774
                        return false;
2✔
1775
                    }
1776

1777
                    // If these point to the same object, break early
1778
                    if Gc::ptr_eq(&l.0, &r.0) {
41✔
1779
                        continue;
×
1780
                    }
1781

1782
                    // Should we visit these?
1783
                    if self.should_visit(l.0.as_ptr() as usize)
41✔
1784
                        && self.should_visit(r.0.as_ptr() as usize)
41✔
1785
                    {
1786
                        self.left.visit_immutable_vector(l);
41✔
1787
                        self.right.visit_immutable_vector(r);
41✔
1788
                    } else {
1789
                        return false;
×
1790
                    }
1791

1792
                    continue;
41✔
1793
                }
1794

1795
                (VectorV(l), MutableVector(r)) => {
48✔
1796
                    if l.len() != r.get().len() {
48✔
1797
                        return false;
×
1798
                    }
1799

1800
                    self.left.visit_immutable_vector(l);
48✔
1801
                    self.right.visit_mutable_vector(r);
48✔
1802

1803
                    continue;
48✔
1804
                }
1805
                (MutableVector(l), VectorV(r)) => {
×
1806
                    if l.get().len() != r.len() {
×
1807
                        return false;
×
1808
                    }
1809

1810
                    self.left.visit_mutable_vector(l);
×
1811
                    self.right.visit_immutable_vector(r);
×
1812

1813
                    continue;
×
1814
                }
1815
                (Void, Void) => {
×
1816
                    continue;
×
1817
                }
1818
                (StringV(l), StringV(r)) => {
202✔
1819
                    if l != r {
202✔
1820
                        return false;
×
1821
                    }
1822
                    continue;
202✔
1823
                }
1824
                (FuncV(l), FuncV(r)) => {
×
1825
                    if l as usize != r as usize {
×
1826
                        return false;
×
1827
                    }
1828
                    continue;
×
1829
                }
1830
                (MutFunc(l), MutFunc(r)) => {
×
1831
                    if l as usize != r as usize {
×
1832
                        return false;
×
1833
                    }
1834
                    continue;
×
1835
                }
1836
                (SymbolV(l), SymbolV(r)) => {
3,625✔
1837
                    if l != r {
3,625✔
1838
                        return false;
2✔
1839
                    }
1840
                    continue;
3,623✔
1841
                }
1842
                (SteelVal::Custom(l), SteelVal::Custom(r)) => {
×
1843
                    if l.read().inner_type_id() != r.read().inner_type_id() {
×
1844
                        return false;
×
1845
                    }
1846

1847
                    if l.read().check_equality_hint(r.read().as_ref()) {
×
1848
                        // Go down to the next level
1849
                        self.left.visit_custom_type(l);
×
1850
                        self.right.visit_custom_type(r);
×
1851
                        continue;
×
1852
                    } else {
1853
                        return false;
×
1854
                    }
1855
                }
1856

1857
                (SteelVal::Custom(l), other) => {
×
1858
                    if l.read().check_equality_hint_general(&other) {
×
1859
                        self.left.visit_custom_type(l);
×
1860

1861
                        match other {
×
1862
                            Closure(x) => self.right.visit_closure(x),
×
1863
                            VectorV(v) => self.right.visit_immutable_vector(v),
×
1864
                            SteelVal::Custom(r) => self.right.visit_custom_type(r),
×
1865
                            HashMapV(r) => self.right.visit_hash_map(r),
×
1866
                            HashSetV(r) => self.right.visit_hash_set(r),
×
1867
                            CustomStruct(r) => self.right.visit_steel_struct(r),
×
1868
                            PortV(r) => self.right.visit_port(r),
×
1869
                            IterV(r) => self.right.visit_transducer(r),
×
1870
                            ReducerV(r) => self.right.visit_reducer(r),
×
1871
                            ContinuationFunction(r) => self.right.visit_continuation(r),
×
1872
                            ListV(r) => self.right.visit_list(r),
×
1873
                            SteelVal::Pair(r) => self.right.visit_pair(r),
×
1874
                            MutableVector(r) => self.right.visit_mutable_vector(r),
×
1875
                            BoxedIterator(r) => self.right.visit_boxed_iterator(r),
×
1876
                            SteelVal::SyntaxObject(r) => self.right.visit_syntax_object(r),
×
1877
                            Boxed(r) => self.right.visit_boxed_value(r),
×
1878
                            HeapAllocated(r) => self.right.visit_heap_allocated(r),
×
1879
                            _ => {}
×
1880
                        }
1881

1882
                        continue;
×
1883
                    } else {
1884
                        return false;
×
1885
                    }
1886
                }
1887

1888
                (other, SteelVal::Custom(r)) => {
1✔
1889
                    if r.read().check_equality_hint_general(&other) {
1✔
1890
                        match other {
×
1891
                            Closure(x) => self.left.visit_closure(x),
×
1892
                            VectorV(v) => self.left.visit_immutable_vector(v),
×
1893
                            SteelVal::Custom(r) => self.left.visit_custom_type(r),
×
1894
                            HashMapV(r) => self.left.visit_hash_map(r),
×
1895
                            HashSetV(r) => self.left.visit_hash_set(r),
×
1896
                            CustomStruct(r) => self.left.visit_steel_struct(r),
×
1897
                            PortV(r) => self.left.visit_port(r),
×
1898
                            IterV(r) => self.left.visit_transducer(r),
×
1899
                            ReducerV(r) => self.left.visit_reducer(r),
×
1900
                            ContinuationFunction(r) => self.left.visit_continuation(r),
×
1901
                            ListV(r) => self.left.visit_list(r),
×
1902
                            SteelVal::Pair(r) => self.left.visit_pair(r),
×
1903
                            MutableVector(r) => self.left.visit_mutable_vector(r),
×
1904
                            BoxedIterator(r) => self.left.visit_boxed_iterator(r),
×
1905
                            SteelVal::SyntaxObject(r) => self.left.visit_syntax_object(r),
×
1906
                            Boxed(r) => self.left.visit_boxed_value(r),
×
1907
                            HeapAllocated(r) => self.left.visit_heap_allocated(r),
×
1908
                            _ => {}
×
1909
                        }
1910

1911
                        self.right.visit_custom_type(r);
×
1912

1913
                        continue;
×
1914
                    } else {
1915
                        return false;
1✔
1916
                    }
1917
                }
1918

1919
                (SteelVal::HashMapV(l), SteelVal::HashMapV(r)) => {
35✔
1920
                    if Gc::ptr_eq(&l.0, &r.0) {
35✔
1921
                        continue;
×
1922
                    }
1923

1924
                    if self.should_visit(l.0.as_ptr() as usize)
35✔
1925
                        && self.should_visit(r.0.as_ptr() as usize)
35✔
1926
                    {
1927
                        if l.len() != r.len() {
35✔
1928
                            return false;
×
1929
                        }
1930

1931
                        // TODO: Implicitly here we are assuming that this key was even hashable
1932
                        // to begin with, since it ended up in the right spot, and didn't blow
1933
                        // the stack on a recursive structure.
1934
                        //
1935
                        // This still does not handle the pathological edge case of something like
1936
                        // (hash (hash (hash ...) value) value)
1937
                        //
1938
                        // In this case, we'll get a stack overflow, when trying to compare equality
1939
                        // with these maps if they're sufficiently deep.
1940
                        //
1941
                        // The issue is that if the two maps are equivalent, we need to check the
1942
                        // existence of each key in the left map with each key in the right map.
1943
                        // Doing so invokes an equality check, where we'll then invoke this logic
1944
                        // again. We could solve this by disallowing hashmaps as keys - then
1945
                        // we would not the same issue where putting a hashmap into the map
1946
                        // causes the equality checks to go off the rails.
1947

1948
                        if eq_depth() > 512 {
35✔
1949
                            log::error!("Aborting eq checks before the stack overflows");
×
1950

1951
                            return false;
×
1952
                        }
1953

1954
                        for (key, value) in l.0.iter() {
75✔
1955
                            if let Some(right_value) = r.0.get(key) {
150✔
1956
                                self.left.push_back(value.clone());
×
1957
                                self.right.push_back(right_value.clone());
×
1958
                            } else {
1959
                                // We know that these are not equal, because the
1960
                                // key in the left map does not exist in the right
1961
                                return false;
×
1962
                            }
1963
                        }
1964
                    }
1965

1966
                    continue;
35✔
1967
                }
1968
                (HashSetV(l), HashSetV(r)) => {
4✔
1969
                    if Gc::ptr_eq(&l.0, &r.0) {
4✔
1970
                        continue;
×
1971
                    }
1972

1973
                    if self.should_visit(l.0.as_ptr() as usize)
4✔
1974
                        && self.should_visit(r.0.as_ptr() as usize)
4✔
1975
                    {
1976
                        if l.len() != r.len() {
4✔
1977
                            return false;
×
1978
                        }
1979
                        if eq_depth() > 512 {
4✔
1980
                            log::error!("Aborting eq checks before the stack overflows");
×
1981

1982
                            return false;
×
1983
                        }
1984

1985
                        for key in l.0.iter() {
11✔
1986
                            if !l.0.contains(key) {
11✔
1987
                                return false;
×
1988
                            }
1989
                        }
1990
                    }
1991

1992
                    continue;
4✔
1993
                }
1994
                (CustomStruct(l), CustomStruct(r)) => {
9✔
1995
                    // If these are the same object, just continue
1996
                    if Gc::ptr_eq(&l, &r) {
9✔
1997
                        continue;
×
1998
                    }
1999

2000
                    if self.should_visit(l.as_ptr() as usize)
9✔
2001
                        && self.should_visit(r.as_ptr() as usize)
9✔
2002
                    {
2003
                        // Check the top level equality indicators to make sure
2004
                        // that these two types are the same
2005
                        if !(l.type_descriptor == r.type_descriptor && l.name() == r.name()) {
18✔
2006
                            return false;
×
2007
                        }
2008

2009
                        self.left.visit_steel_struct(l);
9✔
2010
                        self.right.visit_steel_struct(r);
9✔
2011
                    }
2012

2013
                    continue;
9✔
2014
                }
2015
                // (PortV(_), PortV(_)) => {
2016
                // return
2017
                // }
2018
                (IterV(l), IterV(r)) => {
×
2019
                    self.left.visit_transducer(l);
×
2020
                    self.right.visit_transducer(r);
×
2021

2022
                    continue;
×
2023
                }
2024
                (ReducerV(l), ReducerV(r)) => {
×
2025
                    self.left.visit_reducer(l);
×
2026
                    self.right.visit_reducer(r);
×
2027

2028
                    continue;
×
2029
                }
2030
                // FutureV(f) => self.visit_future(f),
2031
                (ContinuationFunction(l), ContinuationFunction(r)) => {
×
2032
                    if !Continuation::ptr_eq(&l, &r) {
×
2033
                        return false;
×
2034
                    }
2035

2036
                    continue;
×
2037
                }
2038
                // MutFunc(m) => self.visit_mutable_function(m),
2039
                (BuiltIn(l), BuiltIn(r)) => {
×
2040
                    if l as usize != r as usize {
×
2041
                        return false;
×
2042
                    }
2043
                    continue;
×
2044
                }
2045
                // MutableVector(b) => self.visit_mutable_vector(b),
2046
                // BoxedIterator(b) => self.visit_boxed_iterator(b),
2047
                // SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
2048
                // Boxed(b) => self.visit_boxed_value(b),
2049
                // Reference(r) => self.visit_reference_value(r),
2050
                (BigNum(l), BigNum(r)) => {
2✔
2051
                    if l != r {
2✔
2052
                        return false;
×
2053
                    }
2054
                    continue;
2✔
2055
                }
2056
                (SyntaxObject(l), SyntaxObject(r)) => {
×
2057
                    if Gc::ptr_eq(&l, &r) {
×
2058
                        continue;
×
2059
                    }
2060

2061
                    self.left.visit_syntax_object(l);
×
2062
                    self.right.visit_syntax_object(r);
×
2063

2064
                    continue;
×
2065
                }
2066
                (HeapAllocated(l), HeapAllocated(r)) => {
×
2067
                    if HeapRef::ptr_eq(&l, &r) {
×
2068
                        continue;
×
2069
                    }
2070

2071
                    self.left.visit_heap_allocated(l);
×
2072
                    self.right.visit_heap_allocated(r);
×
2073

2074
                    continue;
×
2075
                }
2076

2077
                (MutableVector(l), MutableVector(r)) => {
2,004✔
2078
                    if HeapRef::ptr_eq(&l, &r) {
2,004✔
2079
                        continue;
×
2080
                    }
2081

2082
                    self.left.visit_mutable_vector(l);
2,004✔
2083
                    self.right.visit_mutable_vector(r);
2,004✔
2084

2085
                    continue;
2,004✔
2086
                }
2087
                (Closure(l), Closure(r)) => {
187✔
2088
                    if l != r {
187✔
2089
                        return false;
171✔
2090
                    }
2091

2092
                    self.left.visit_closure(l);
16✔
2093
                    self.right.visit_closure(r);
16✔
2094

2095
                    continue;
16✔
2096
                }
2097
                (_, _) => {
×
2098
                    return false;
13,084✔
2099
                }
2100
            }
2101

2102
            // unreachable!();
2103
        }
2104
    }
2105
}
2106

2107
// TODO: This _needs_ to use references. Or otherwise we'll thrash stuff on drop
2108
pub struct EqualityVisitor<'a> {
2109
    // Mark each node that we've visited, if we encounter any mutable objects
2110
    // on the way, then we'll start using the visited set. But we'll optimistically
2111
    // assume that there are no mutable objects, and we won't start using this
2112
    // until we absolutely have to.
2113
    // found_mutable_object: bool,
2114
    queue: &'a mut Vec<SteelVal>,
2115
}
2116

2117
impl<'a> BreadthFirstSearchSteelValVisitor for EqualityVisitor<'a> {
2118
    type Output = ();
2119

2120
    fn default_output(&mut self) -> Self::Output {}
×
2121

2122
    fn pop_front(&mut self) -> Option<SteelVal> {
230,544✔
2123
        self.queue.pop()
230,544✔
2124
    }
2125

2126
    fn push_back(&mut self, value: SteelVal) {
149,430✔
2127
        self.queue.push(value)
149,430✔
2128
    }
2129

2130
    fn visit_closure(&mut self, _closure: Gc<ByteCodeLambda>) -> Self::Output {}
32✔
2131

2132
    // Leaf nodes, we don't need to do anything here
2133
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
×
2134
    fn visit_bool(&mut self, _boolean: bool) -> Self::Output {}
×
2135
    fn visit_float(&mut self, _float: f64) -> Self::Output {}
×
2136
    fn visit_int(&mut self, _int: isize) -> Self::Output {}
×
2137
    fn visit_rational(&mut self, _: Rational32) -> Self::Output {}
×
2138
    fn visit_bigrational(&mut self, _: Gc<BigRational>) -> Self::Output {}
×
2139
    fn visit_bignum(&mut self, _: Gc<BigInt>) -> Self::Output {}
×
2140
    fn visit_complex(&mut self, _: Gc<SteelComplex>) -> Self::Output {}
×
2141
    fn visit_char(&mut self, _c: char) -> Self::Output {}
×
2142
    fn visit_void(&mut self) -> Self::Output {}
×
2143
    fn visit_string(&mut self, _string: SteelString) -> Self::Output {}
×
2144
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) -> Self::Output {}
×
2145
    fn visit_symbol(&mut self, _symbol: SteelString) -> Self::Output {}
×
2146
    fn visit_port(&mut self, _port: SteelPort) -> Self::Output {}
×
2147
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) -> Self::Output {}
×
2148
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) -> Self::Output {}
×
2149
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) -> Self::Output {}
×
2150

2151
    //
2152
    fn visit_immutable_vector(&mut self, vector: SteelVector) -> Self::Output {
130✔
2153
        // If we've found the mutable object, mark that this has been visited. Only
2154
        // if self.should_visit(vector.0.as_ptr() as usize) {
2155
        for value in vector.iter() {
956✔
2156
            self.push_back(value.clone());
413✔
2157
        }
2158
        // }
2159
    }
2160

2161
    // SHOULD SET MUTABLE HERE
2162
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) -> Self::Output {
×
2163
        custom_type.read().visit_children_for_equality(self);
×
2164
    }
2165

2166
    fn visit_hash_map(&mut self, _hashmap: SteelHashMap) -> Self::Output {
×
2167
        // TODO: See comment above
2168
    }
2169

2170
    fn visit_hash_set(&mut self, _hashset: SteelHashSet) -> Self::Output {
×
2171
        // TODO: See comment above
2172
    }
2173

2174
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) -> Self::Output {
18✔
2175
        // if self.should_visit(steel_struct.as_ptr() as usize) {
2176
        for value in steel_struct.fields.iter() {
62✔
2177
            self.push_back(value.clone());
22✔
2178
        }
2179
        // }
2180
    }
2181

2182
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) -> Self::Output {
×
2183
        for transducer in transducer.ops.iter() {
×
2184
            match transducer.clone() {
×
2185
                crate::values::transducers::Transducers::Map(m) => self.push_back(m),
×
2186
                crate::values::transducers::Transducers::Filter(v) => self.push_back(v),
×
2187
                crate::values::transducers::Transducers::Take(t) => self.push_back(t),
×
2188
                crate::values::transducers::Transducers::Drop(d) => self.push_back(d),
×
2189
                crate::values::transducers::Transducers::FlatMap(fm) => self.push_back(fm),
×
2190
                crate::values::transducers::Transducers::Flatten => {}
×
2191
                crate::values::transducers::Transducers::Window(w) => self.push_back(w),
×
2192
                crate::values::transducers::Transducers::TakeWhile(tw) => self.push_back(tw),
×
2193
                crate::values::transducers::Transducers::DropWhile(dw) => self.push_back(dw),
×
2194
                crate::values::transducers::Transducers::Extend(e) => self.push_back(e),
×
2195
                crate::values::transducers::Transducers::Cycle => {}
×
2196
                crate::values::transducers::Transducers::Enumerating => {}
×
2197
                crate::values::transducers::Transducers::Zipping(z) => self.push_back(z),
×
2198
                crate::values::transducers::Transducers::Interleaving(i) => self.push_back(i),
×
2199
            }
2200
        }
2201
    }
2202

2203
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) -> Self::Output {
×
2204
        match reducer.as_ref().clone() {
×
2205
            Reducer::ForEach(f) => self.push_back(f),
×
2206
            Reducer::Generic(rf) => {
×
2207
                self.push_back(rf.initial_value);
×
2208
                self.push_back(rf.function);
×
2209
            }
2210
            _ => {}
×
2211
        }
2212
    }
2213

2214
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) -> Self::Output {}
×
2215
    fn visit_future(&mut self, _future: Gc<FutureResult>) -> Self::Output {}
×
2216

2217
    fn visit_stream(&mut self, _stream: Gc<LazyStream>) -> Self::Output {}
×
2218

2219
    fn visit_continuation(&mut self, _continuation: Continuation) -> Self::Output {}
×
2220

2221
    fn visit_list(&mut self, list: List<SteelVal>) -> Self::Output {
×
2222
        for value in list.iter() {
×
2223
            self.push_back(value.clone());
×
2224
        }
2225
    }
2226

2227
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output {
4,056✔
2228
        for value in vector.get().iter() {
36,542✔
2229
            self.push_back(value.clone());
16,243✔
2230
        }
2231
    }
2232

2233
    fn visit_boxed_iterator(&mut self, _iterator: GcMut<OpaqueIterator>) -> Self::Output {}
×
2234

2235
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) -> Self::Output {
×
2236
        if let Some(raw) = syntax_object.raw.clone() {
×
2237
            self.push_back(raw);
×
2238
        }
2239

2240
        self.push_back(syntax_object.syntax.clone());
×
2241
    }
2242

2243
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) -> Self::Output {
×
2244
        self.push_back(boxed_value.read().clone());
×
2245
    }
2246

2247
    fn visit_reference_value(&mut self, _reference: Gc<OpaqueReference<'static>>) -> Self::Output {}
×
2248

2249
    // Should set mutable here
2250
    fn visit_heap_allocated(&mut self, heap_ref: HeapRef<SteelVal>) -> Self::Output {
×
2251
        // self.found_mutable_object = true;
2252

2253
        // if self.should_visit(heap_ref.as_ptr_usize()) {
2254
        self.push_back(heap_ref.get());
×
2255
        // }
2256
    }
2257

2258
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
×
2259
        self.push_back(pair.car());
×
2260
        self.push_back(pair.cdr());
×
2261
    }
2262
}
2263

2264
impl PartialEq for SteelVal {
2265
    fn eq(&self, other: &Self) -> bool {
534,322✔
2266
        match (self, other) {
534,322✔
2267
            (Void, Void) => true,
29✔
2268
            (BoolV(l), BoolV(r)) => l == r,
17,895✔
2269
            (IntV(l), IntV(r)) => l == r,
262,099✔
2270
            (NumV(l), NumV(r)) => l == r,
259✔
2271
            (Rational(l), Rational(r)) => l == r,
88✔
2272
            (BigRational(l), BigRational(r)) => l == r,
5✔
2273
            (BigNum(l), BigNum(r)) => l == r,
54✔
2274
            (Complex(l), Complex(r)) => l == r,
57✔
2275
            (StringV(l), StringV(r)) => l == r,
13,604✔
2276
            (SymbolV(l), SymbolV(r)) => l == r,
147,291✔
2277
            (CharV(l), CharV(r)) => l == r,
38,368✔
2278
            (FuncV(l), FuncV(r)) => *l as usize == *r as usize,
×
2279
            (ByteVector(l), ByteVector(r)) => l == r,
19✔
2280
            // (VectorV(l), VectorV(r)) => l == r,
2281
            // (ListV(l), ListV(r)) => l == r,
2282
            // (HashSetV(l), HashSetV(r)) => l == r,
2283
            // (HashMapV(l), HashMapV(r)) => l == r,
2284
            // (Closure(l), Closure(r)) => l == r,
2285
            // (IterV(l), IterV(r)) => l == r,
2286
            // (ListV(l), ListV(r)) => l == r,
2287
            // (CustomStruct(l), CustomStruct(r)) => l == r,
2288
            // (Custom(l), Custom(r)) => Gc::ptr_eq(l, r),
2289
            // (HeapAllocated(l), HeapAllocated(r)) => l.get() == r.get(),
2290
            (left, right) => {
54,554✔
2291
                LEFT_QUEUE.with(|left_queue| {
109,108✔
2292
                    RIGHT_QUEUE.with(|right_queue| {
109,108✔
2293
                        VISITED_SET.with(|visited_set| {
109,108✔
2294
                            match (
54,554✔
2295
                                left_queue.try_borrow_mut(),
54,554✔
2296
                                right_queue.try_borrow_mut(),
54,554✔
2297
                                visited_set.try_borrow_mut(),
54,554✔
2298
                            ) {
2299
                                (Ok(mut left_queue), Ok(mut right_queue), Ok(mut visited_set)) => {
54,554✔
2300
                                    let mut equality_handler = RecursiveEqualityHandler {
54,554✔
2301
                                        left: EqualityVisitor {
54,554✔
2302
                                            queue: &mut left_queue,
54,554✔
2303
                                        },
2304
                                        right: EqualityVisitor {
54,554✔
2305
                                            queue: &mut right_queue,
54,554✔
2306
                                        },
2307
                                        visited: &mut visited_set,
54,554✔
2308
                                    };
2309

2310
                                    let res = equality_handler
54,554✔
2311
                                        .compare_equality(left.clone(), right.clone());
54,554✔
2312

2313
                                    reset_eq_depth();
54,554✔
2314

2315
                                    // Clean up!
2316
                                    equality_handler.left.queue.clear();
54,554✔
2317
                                    equality_handler.right.queue.clear();
54,554✔
2318
                                    equality_handler.visited.clear();
54,554✔
2319

2320
                                    res
54,554✔
2321
                                }
2322
                                _ => {
2323
                                    let mut left_queue = Vec::new();
×
2324
                                    let mut right_queue = Vec::new();
×
2325

2326
                                    let mut visited_set = fxhash::FxHashSet::default();
×
2327

2328
                                    increment_eq_depth();
×
2329

2330
                                    let mut equality_handler = RecursiveEqualityHandler {
×
2331
                                        left: EqualityVisitor {
×
2332
                                            queue: &mut left_queue,
×
2333
                                        },
2334
                                        right: EqualityVisitor {
×
2335
                                            queue: &mut right_queue,
×
2336
                                        },
2337
                                        visited: &mut visited_set,
×
2338
                                    };
2339

2340
                                    let res = equality_handler
×
2341
                                        .compare_equality(left.clone(), right.clone());
×
2342

2343
                                    decrement_eq_depth();
×
2344

2345
                                    res
×
2346
                                }
2347
                            }
2348
                        })
2349
                    })
2350
                })
2351
            }
2352
        }
2353
    }
2354
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc